Saturday, December 10, 2016

Using URLs, HTTP Methods, and HTTP Status Codes

RESTful web services use request URLs to identify the requested resource and HTTP methods to ascertain the action wanted. They also use HTTP status codes to reveal the result of a request. Different status codes apply to different types of requests. However, there are a few status codes that are universal and apply to all types of requests:

400 Bad Request indicates that the request made by the client was unsupported or unrecognized. This is usually because the proper request syntax was not followed. For example, if a required field of a resource were left blank on a POST or PUT, that would result in a 400 Bad Request response. The response body should include information about why the request was bad in the representation format agreed upon or in the default representation in absence of agreement.

401 Unauthorized signals that authentication and authorization are required before accessing the resource or performing the requested state transition. The content of the response varies based on the authentication protocol in use (such as OAuth).

403 Forbidden indicates that the client, though authenticated, does not have permission to access the resource or perform the requested state transition. Depending on the authentication protocol in use, it’s possible that re-authenticating with more requested privileges could resolve this condition.

404 Not Found, quite simply, tells the client that the requested resource does not exist. It should never be used to indicate that a resource exists but the state transition is simply not supported or allowed. That is what 405 Method Not Allowed and 403 Forbidden are for.

405 Method Not Allowed means that the state transition (HTTP method) requested is not supported. This should never be used to indicate that the client does not have permission to perform the state transition; 403 Forbidden is reserved for that.

406 Not Acceptable indicates that the representation format requested in the Accept header is not supported by the server. For example, the client may have requested application/xml, but the server can generate only application/json. In these cases, the server may simply return the default supported representation instead of 406 Not Acceptable, and that is what most vendors do.

415 Unsupported Media Type is very similar to 406 Not Acceptable. It indicates that the Content-Type header in the request (the representation of the request entity) is a type that is not supported by the server. The server may also include an Accept response header indicating which media types the server supports. It’s possible that both the Accept request header value and the Content-Type request header value can be unsupported media types.
In such a case, the 415 Unsupported Media Type response takes precedence because 406 Not Acceptable is an optional response.

500 Internal Server Error signals that an error occurred while processing the request.
The response content should include as much information about the error as is safe and possible in the representation format agreed upon or in the default representation in absence of agreement.


HTTP Methods


OPTIONS is one of the few standardized discovery mechanisms available. When a request is made to a resource URL with the OPTIONS method, the server must return a 200 OK response containing an Allow header whose contents is a comma-separated list of the HTTP methods supported for the resource. If the client is authenticated, the Allow header may instead contain the HTTP methods that the client is authorized to invoke for the resource.
Optionally, the server may also return a response body describing how to use each HTTP method for that resource. The response body should be in the representation form agreed upon or in the default representation in the absence of any agreement. If the PATCH method is one of the supported methods, the response should also contain the Allow-Patch header, which specifies a comma-separated list of the representations (media types) permitted for PATCH requests, which may differ for other types of requests.

There are some exceptions to these requirements:
- If the resource does not exist, the server should return 404 Not Found instead of 200 OK with the Allow header.
- If no methods are permitted on a resource without authentication, the server should return 401 Unauthorized.
- If the client is authenticated but does not have permission to invoke any actions on the resource, the server should return 403 Forbidden.

OPTIONS requests are considered nullipotent (safe); that is, they should never, under any circumstances, result in modification of any resources.

N.B. Some web services enable Cross-Origin Resource Sharing (CORS). This special, relatively new protocol enables authorized browser applications to bypass the AJAX same-origin policy using a set of specialized headers. If you want your web services to be directly accessible from JavaScript applications, you need to enable CORS support.
CORS-enabled web services must support OPTIONS requests for all resources and, when responding to OPTIONS requests, must include the Access-Control-Allow-Methods response header, whose contents are identical to the Allow header.

Whenever GET is supported and allowed on a resource, HEAD must also be supported and allowed.
The only difference between GET and HEAD is that a HEAD response must have no response body.
The HEAD response must have headers identical to what would be in a GET response. GET requests are used to obtain a resource or resources. A URL such as /services/Rest/account indicates that the client wants to obtain all accounts or a filtered list of accounts. Typically, filter, order, and pagination instructions are included in query string parameters.
A request URL such as /services/Rest/account/1075 indicates that the client wants to obtain a single account with the unique identifier 1075. Some vendors also support relational URLs such as /services/Rest/account/1075/order and /services/Rest/account/1075/order/1522, signaling requests for a (possibly filtered) list of orders for account 1075 and for order 1522 belonging to account 1075, respectively. This recursion can, theoretically, continue as far as the limits on URL length will permit. /services/Rest/account/1075/order/1522/item/12 could return a particular line item from an order for an account.
The server should respond 200 OK for successful GET and HEAD requests and include the resource or resources requested in the agreed-upon (or default) representation in the response body for GET requests.
GET and HEAD requests are also nullipotent and should not have any side effects for resources on the server.

POST requests are used to create new resources on the server. A POST request should always be directed at a collection URI (/services/Rest/account), but it can also be directed at a subordinate collection URI (/services/Rest/account/1075/order). A POST request to an individual element URI (/services/Rest/account/1075) should result in a 405 Method Not Allowed response. In a successful POST request, the server creates the requested resource and returns 201 Created. The response should include a Location header with the URL for the newly created resource.
For example, a POST request to /services/Rest/account to create a new account might return http://www.example.com/services/Rest/account/9156 in the Location header. The response body should be the created resource because it would be returned with a GET request to the URL in the Location header.
A POST request is non-nullipotent (non-safe : the request does result in modification of one or more resources) and non-idempotent (making multiple identical POST requests results in multiple created resources).

A PUT request results in the replacement of a resource on the server. For this reason, PUT requests are used to update existing resources. PUT requests, unlike POST requests, are never made to collection URIs. Instead, they are made to individual element URIs and subordinate element URIs (/services/Rest/account/1075, /services/Rest/account/1075/order/5122). A PUT request to a collection URI or subordinate collection URI should result in a 405 Method Not Allowed response. The response to a successful PUT request should be 204 No Content and its body should be empty.
PUT requests are, obviously, non-nullipotent. They should, however, be idempotent. Two or more successive, identical PUT requests should have no side effects other than those in the first PUT request.
One area that this can be a challenge is if the resource being updated includes a “last modified” timestamp or a version number. To abide by the restriction of idempotence, the service should update the resource timestamp and/or version number for PUT requests only if the underlying entity truly changed. Such a requirement can actually be quite challenging to implement, so many vendors implement PUT in a partially idempotent manner, documenting that the timestamp and version number will still be updated in identical PUT requests.

PATCH requests are very similar to PUT requests in both purpose and semantics. PATCH is a relatively new HTTP method, added only in the last several years. It is not part of the original HTTP/1.1 specification. PATCH requests, like PUT requests, are also intended to update resources at individual element URIs. However, a PATCH request indicates only a partial update of a resource, not a complete replacement of the resource.
For example, if a PATCH request to /service/Rest/account/1075/order/5122 contains only the shippingAddress property, then only the shippingAddress on the order will be updated. Other properties will remain unchanged (except, possibly, timestamps and version numbers). This is an extremely powerful request method, but it is also quite challenging to implement. To support PATCH, your application must accept a very flexible set of properties in the request entity, and then update only those resource properties that are present in the request. You cannot merely use a check for null because the PATCH may be intentionally setting a property value to null.
The response to a successful PATCH request should be either 200 OK or 204 No Content. It’s your choice whether to return the full, updated entity or no content in the response body. If PATCH is supported, the requested media type is supported, and the PATCH request content is understood and parsed successfully, but the server still cannot apply the patch (for example, because the patch would make the entity invalid), the server should respond with 422 Unprocessable Entity. If the client uses the If-Match request header or If-Unmodified-Since request header to define a precondition for the patch and that precondition fails, the server should return 412 Precondition Failed. If multiple requests attempt to patch a resource simultaneously and that is not permitted, the server should return 409 Conflict.
PATCH requests, like PUT requests, are non-nullipotent and should be idempotent.

A DELETE request, naturally, is used to delete a resource. A DELETE request may be made against an individual element URI in which case that single resource is deleted or it may be made against a (possibly filtered) collection URI in which case all the matching resources are deleted. Allowing the deletion of multiple resources is often not desirable, so this capability is usually not supported. Upon successful deletion of a resource, the server should respond 200 OK with the deleted resource in the response body or 204 No Content with no content in the response body. If for some reason the server accepts the delete command but cannot execute it immediately (perhaps the resource is in use), it may return 202 Accepted. In this case, the response body should contain a resource URL that the client can use to follow up on the request and check on its status at a later time.
DELETE requests are obviously non-nullipotent, but their idempotence is an interesting topic. Deleting a resource may result in setting a flag on that resource but retaining its data (a soft delete), or it may result in actually permanently and irrevocably eliminating the resource’s data (a hard delete). When employing soft deletes, multiple identical DELETE requests always return the same response and have no additional side effects, making DELETE idempotent. However, when using hard deletes a second identical DELETE request always results in 404 Not Found because the resource no longer exists. Technically this is considered non-idempotent because the response differs, but it doesn’t actually have different side effects and is still the best practice when accepting DELETE requests for hard deletes.

No comments:

Post a Comment