This section contains a quick overview of conventions and rules that are outlined and discussed in more detail in the remaining sections of this document. Use this checklist whenever developing or reworking REST resources, resource objects, or clients.
Things you should do. Make sure:
Accept
and Content-Type
)AbstractRestResource
, AbstractRestCollectionResource
, and AbstractResourceObject
to ensure consistencyThings you should not do:
@Json...
and @Xml...
annotations to define (de)serialization of your ROs.This document is only a style guide. It does not discuss the underlying principles more deeply .
Before beginning with the design and/or implementation of a REST API, resource, or resource object, you should read the following documents:Everything that is addressable via a URI in a REST API has to be a resource, i.e., something representing a thing, or a concept, which can be retrieved and/or modified. REST APIs do not expose services(unlike SOAP WebServices).
All resources represent either a single resource, or a collection of resources. Method calls/identifiers as well as processing instructions (sorting, locale parameters, ...) are illegal in REST URIs except for query or matrix parameters.
/categories/<categoryID>
, not /categories/category/<categoryID>
, as /category
does not represent a resource itself.Use a sub-resource if the sub-resource:
Use an attribute if the attribute is :
All accessible objects (resources, collection resources, i.e., the subjects) are accessed only via URIs using standard HTTP methods (GET, POST, PUT, DELETE, OPTIONS, i.e., the verbs).
The following table gives a short overview of the most important actions. For more detail, see RFC 2616, Sec.9 .
Method | Purpose | CRUD equivalent |
---|---|---|
| Retrieves whatever information (in the form of an entity) is identified by the Request-URI. If the Request-URI refers to a data-producing process, it is the produced data which shall be returned as the entity in the response. | Read |
| Requests that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line. | Create |
| Requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity should be considered as a modified version of the one residing on the origin server. | Update |
| Requests that the origin server delete the resource identified by the Request-URI. | Delete |
| Requests information about the communication options available on the request/response chain identified by the Request-URI. |
|
Note
The methods POST
and PUT
are often confused since POST
has been used for years to achieve the results of PUT
on the web.
( RFC 2616, Sec.9.6 )The fundamental difference between the POST and PUT requests is reflected in the different meaning of the Request-URI. The URI of a POST request identifies the resource that will handle the enclosed entity. That resource might be a data-accepting process, a gateway to some other protocol, or a separate entity that accepts annotations. In contrast, the URI in a PUT request identifies the entity enclosed with the request – the user agent knows what URI is intended and the server MUST NOT attempt to apply the request to some other resource.
GET
and HEAD
are safe methods: They do not change anything on the server side. They can be called multiple times for the same URI and do not have any effects than returning (maybe) different values.
GET
, HEAD
, PUT
, DELETE
, OPTIONS
, and TRACE
are idempotent methods: They may have server side effects, but can still be called multiple times for the same resource without negative effects. For example, the same change to the same resource can be done multiple times; the state after that will be the same as after the first change (e.g., x=1
is the same as x=1;x=1;...;x=1
). An HTTP PUT
in Java corresponds to something like map.put(key, value)
with the URI (or parts of it) being the key and the transferred data being the value.
In contrast, POST
is a non-idempotent operation: In Java, it corresponds to something like list.add(value)
with the transferred data being the value. It can have undesired effects when doing that multiple times.
If you feel you cannot adequately express the needed functionality with these methods, reconsider your resource structure. E.g., if you want to implement something like "add a new address for this user" and the user can have multiple addresses, you must not have a call like POST /users/myUser with an address, and you must not have a call like POST /users/myUser/addAddress?.... Instead, you should expose the addresses as a sub-resource, and manipulate that sub-resource with a call like POST /users/myUser/addresses.
In contrast to plain Jersey-based REST APIs, the Intershop REST framework enforces hierarchical access to the resources. This means, that the resource addresses addressed via /users/-/addresses can only be accessed if the parent resource users can be accessed.
This allows any protection scheme to extend protection to all its sub-resources.
The user/client can send an authorization token, also in cases where no authentication is necessary (e.g. registration process).
UserListResource
for the resource handling user listings, UserResource
for the resource handling the user data.AbstractRestResource
or AbstractRestCollectionResource
.RestExceptions
, e.g., throw new RestException(RestException.ERROR_STATUS_NOT_FOUND);
instead of setting response codes and headers manually).The response format is chosen by the server in response to the client's HTTP Accept
header (cf. RFC 2616, Sec.14 ).
Basically, this means that a client sending Accept: text/xml
gets the response in XML format and a client sending Accept: application/json
gets the response in JSON. If sending multiple values, the server selects its preferred of the supported formats.
If the server cannot provide the needed format, it returns the HTTP status code 406
automatically.
A format is supported if the resource handling of the request has the same annotated method as the handling of the used HTTP method and produces the requested content type:
@GET @Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML }) public ProductRO getProduct()
A resource class can provide any number of methods handling as many combinations of HTTP methods and @Consumes
and @Produces
annotations.
To access specific representations of a resource, there are multiple ways:
When an error occurs, an appropriate HTTP status code (see W3C RFC2616 ) has to be returned. This can be achieved by manually calling addResponseData
at the current resource or by simply throwing a RestException
with the appropriate status code, message, and custom headers.
Do not use 200 OK
with custom content for errors.
If additional data has to be transferred in a machine-readable way, custom HTTP response headers should be used, e.g.:
Status: 400 Bad Request (The attributes a,b,c are missing) Headers: error-key: error.product.notification.attributes.missing error-type: error-missing-attributes error-missing-attributes: a,b,c
This allows the client to lookup the error message for the error-key and display it. Also, it enables the client to process the missing attributes individually by parsing the comma-separated list error-missing-attributes, the key of which was transferred in the fixed header error-type.
RestException
to generate valid, consistent error responses (e.g., throw new RestException().notFound()
)RestResponseBuilder
to generate valid, consistent responses (e.g., return getResponseBuilder().created(uri).build()
.Examples for the use of these classes can be found in the Concept - REST Framework.
For all operations, use the appropriate HTTP status code to inform the client about the result of its call. The most important ones are the following:
HTTP status code | Meaning | Description | Notes |
---|---|---|---|
200 | OK | The request has succeeded. | Standard response code; generated automatically if no exception occurred. |
201 | Created | The request has been fulfilled and resulted in a new resource being created. The newly created resource can be referenced by the URI(s) returned in the entity of the response, with the most specific URI for the resource given by a location header field. The response should include an entity containing a list of resource characteristics and location(s) from which the user or user agent can choose the one most appropriate. | Header Location is mandatory. Return
|
204 | No content | The request has succeeded, but has no response data. | Return |
303 | See other | The response to the request can be found under a different URI and should be retrieved using a | Return |
400 | Bad Request | The request could not be understood by the server due to malformed syntax. The client should not repeat the request without modifications. | Use if required content is missing or invalid. Do not use when just a wrong query parameter was sent. For most cases, thrownew RestException().missingAttributes() or new RestException().invalidAttributes() . |
401 | Unauthorized | The request requires user authentication. The response must include a WWW-Authenticate header field ( section 14.47 ) containing a challenge applicable to the requested resource. The client may repeat the request with a suitable Authorization header field ( section 14.8 ). If the request already included authorization credentials, then the 401 response indicates that authorization has been refused for those credentials. | Handled by |
403 | Forbidden | The server understood the request, but is refusing to fulfill it. Authorization will not help and the request should not be repeated. | Used if the user is known (i.e., authenticated), but not allowed access (i.e., not authorized) |
404 | Not found | The server has not found anything matching the Request-URI. No indication is given of whether the condition is temporary or permanent. This status code is commonly used when the server does not wish to reveal exactly why the request has been refused, or when no other response is applicable. | Use only if no resource could be found at this URI. Do not use it if the resource was found but couldn't return any date (e.g., a search without results). Thrownew RestException().notFound() . |
405 | Method not allowed | The method specified in the Request-Line is not allowed for the resource identified by the Request-URI. The response must include an Allow header containing a list of valid methods for the requested resource. | The framework generates this status and the corresponding "allow" header. |
406 | Not acceptable | The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request. | Use only if the requested content type cannot be provided. Automatically generated. |
409 | Conflict | The request could not be completed due to a conflict with the current state of the resource. This code is only allowed in situations where it is expected that the user may be able to resolve the conflict and resubmit the request. The response body should include enough information for the user to recognize the source of the conflict. Ideally, the response entity includes enough information for the user or user agent to fix the problem; however, this may not be possible and is not required. Conflicts are most likely to occur in response to a PUT request. | Throw |
500 | Internal Server Error | The server encountered an unexpected condition which prevented it from fulfilling the request. | Thrown automatically when uncaught exception occurs. Do not use for foreseeable errors! |
In addition, operations may have specific status codes.
There are some standard status code and header combinations following directly from the above table:
Method | Action | Code | Header | Body |
---|---|---|---|---|
GET uri | Retrieve representation | 200 (OK) | - | Representation of item |
POST uri | Add new item as sub-resource of uri | 201 (Created) | Location | LinkRO |
If the creation updated more than just this item, send "See other" | 303 (See Other) | Location | LinkRO | |
PUT uri | Update the item at uri | 200 (OK) | Updated representation of item | |
If the creation updated more than just this item, send "See other" | 303 (See Other) | Location | LinkRO | |
DELETE | Delete the item at uri | 204 (No Content) | - | - |
If the creation updated more than just this item, send "See other" | 303 (See Other) | Location | LinkRO |
The status codes for authentication and permission issues are usually generated automatically, as are the status codes 405 and 406.
Recource objects (RO) are the standard means of data exchange. They are just data containers without logic.
Their serialization is controlled using Jackson annotations like@JsonProperty
and Jackson-Jaxb annotations like @XmlElement
.An RO exposes all intended properties. It needs to have getters and setters to allow for JSON and XML (de)serialization.
However, an RO having a setter for a property does not mean that the property can be set from the outside! It only means that the property will be deserialized. Server-side logic (in the resource class handling the RO input) then decides whether or not to use that information. Therefore, do not introduce custom ROs just to prevent input!
There should only be as many types of ROs as necessary. Do not add ROs just to represent different views on the same resource. The client should be able to understand a set of ROs relevant for its purpose, and not encounter new ROs all the time.
All resource objects should be derived from AbstractResourceObject
or ExtensibleResourceObject
. This makes sure that serialization and deserialization are handled consistently.
These base classes are Jackson-annotated to expose only public fields and getters/setters. All other fields and methods which should be exposed have to be annotated with @JsonProperty
or @XmlElement
/ @XmlAttribute
. Values which resolve to null are not serialized. This behavior is inherited by all sub-classes.
As a result, a typical RO can be a plain old Java object (POJO) extending AbstractResourceObject
.
Serialization is configured to use the type attribute of an RO when (de)serializing. The type is automatically included in the JSON representation as "type":"MyObject"
and in XML as <MyObject name="someName" type="MyObject">
.
Note: In XML, the tag name will be set to the type only for anonymous objects (e.g., elements of a list). If an RO is set as an attribute value on some other RO, the tag name will be the name of the attribute.
The type should be set to the name of the RO without the suffix "RO", i.e., the type of theProductRO
should be Product.The name of the resource object should describe the object, so that it is understandable to both the server-side developer and the client-developer. It should end in the suffix "RO". I.e., an RO representing a Product should be named "ProductRO".
If your RO's name includes semantics other than the described object, it should be examined critically. E.g., when encountering ROs named NewCustomerRO
, re-evaluate the need for that representation. Does the NewCustomerRO
describe an object different from CustomerRO
? In what respect? If it just exists to add more attributes and setters/getters, it should be removed (see section 1831115766).
LinkRO
As REST APIs typically need to return links to resources (e.g., in an item listing), the framework provides a simple class for this purpose.
The LinkRO
has the following attributes:
AbstractResourceObject
)To enhance the LinkRO
, it can also contain additional attributes. Be careful, do not to return too many attributes here. Return only those attributes that are necessary for the client when choosing one of the links to follow. For a product link, this can be the price, the availability, or a thumbnail, but usually not the high-definition product image.
ResourceCollectionRO
The ResourceCollectionRO
is a typed ordered list of ROs. In addition, it has properties for paging and sorting information (although such functionality is not provided by the class itself).
The ResourceCollectionRO
should only be used as a direct response to a request (i.e., as a top-level data type). E.g., a GET /someResource
could return a ResourceCollectionRO
, but a someRO
should not have an attribute of type ResourceCollectionRO
. It introduces an unnecessary hierarchy-level (with the attribute elements) and is harder to (de)serialize.
Background: The ResourceCollectionRO
only exists to provide access to enhanced list information/options, such as pageable, total, offset, etc. These options can only be accessed when the resource itself is reachable via a URI. Because of that, it makes no sense to use this data-type for enclosed lists which are only reachable via another items attributes and cannot be parametrized.
@GET
, @POST
, @PUT
, @DELETE
, or @OPTIONS
need to be well-documented.