Skip to content

Latest commit

 

History

History
168 lines (121 loc) · 9.47 KB

2. REST.md

File metadata and controls

168 lines (121 loc) · 9.47 KB

REST Services

REST (REpresentational State Transfer) services were defined in a dissertation written by Roy Fielding in 2000.

The main characteristics of a REST service are:

  • Client/Server
  • Stateless - the server should not maintain client state. State is sent with each request.
  • Cacheable - resources decide for themselves if they are cacheable or not (and how long)
  • Hypertext driven (in an ideal world)

REST Resources

A central concept in REST is the Resource. Each resource represents some entity which is usually a noun (not a verb!), and has a separate URI. For instance, the following are all different resources:

  • /api/courses
  • /api/courses/26892
  • /api/courses/26892/students
  • etc.

It is common to define a resource (such as /api/courses) which represents a list (in this case, a list of courses). The URL /api/courses/26892 represents a given instance of a course, and is a separate resource. Then, that particular course can have further subresources, such as a list of students in a given course (/api/courses/26892/students).

REST and HTTP

Technically, REST doesn't depend on HTTP. However, most REST services are implemented using HTTP, and the reason for that is that HTTP fits very well, since all the main characteristics of REST are also prevalent in HTTP.

It is therefore almost universal for REST services to use concepts from HTTP:

  • Using HTTP verbs (GET, POST, PUT, PATCH, DELETE) to indicate what kind of operation is being performed. The alternative is to put the verb into the URL, but this should be avoided.
  • Less frequent is to use the OPTIONS and HEAD methods.
  • Using HTTP status codes (200 OK, 301 Redirect, 404 Not Found etc.) to indicate if the request was successful or not.
  • Using the "Accept" HTTP Header to indicate what format should be used for data (XML, JSON etc.).
  • Using cache-commands defined in HTTP to control caching.
  • Using the "Location" HTTP header when a new entity is created, i.e. the response from the server should include the new Location.

Example of usages:

  • GET (returns single or many "records")
  • POST (create "record")
  • PUT (complete update on "record")
  • DELETE (remove connection, set deleted flag or delete "record")
  • PATCH (updates one or more properties in a record)

Guidelines and tutorials on HTTP methods for RESTful services.

Using HTTP Verbs

In this model, when the client issues a GET request for a given resource, could indicate that the client wants to read information about that resource. Example: a GET request for /api/courses should return a list of courses, while a POST request for that resource will create a new course. It might make sense to support the HTTP DELETE verb as well, which would then delete all courses. Supporting other verbs would hardly make any sense in that case.

Other resources, such as /api/courses/26892 could support the following HTTP verbs:

  • GET (returns a single course)
  • PUT (updates the course)
  • DELETE (removes the course)
  • PATCH (updates one or more properties in a course)

In this case, a POST request would not really make any sense (what should be created?). It is also worth noting that a DELETE request doesn't actually have to delete anything, i.e. it is perfectly acceptable to implement that method such that the underlying resource is only marked as deleted, although the data is still there. It might even make sense to implement other methods using the DELETE verb. Assume we've got a task application, and we need to implement a "close task" operation. This could be done by using a DELETE verb, since by closing a task it should no longer be accessible.

HTTP Status codes

In general, APIs will use only a handful of HTTP Status codes. This could include:

  • 200 - the request was successful.
  • 201 - returned when a POST request to create an entity was successful.
  • 204 - no content. Used if a request doesn't return anything.
  • 301 - redirect - the resource was moved. Probably not used much.
  • 400 - bad request. A general error on behalf of the client, i.e. the request was incorrect. Usually, the response body contains some more information about what exactly was wrong.
  • 401 - unauthorized. The client must specify a "Authentication" HTTP header.
  • 403 - forbidden. The server knows the identity of the client, but the client isn't allowed to perform this action.
  • 404 - not found. Can be used for any resource which isn't found. Example: a request for /api/courses/123456 could return 404 if no course with id 123456 is present.
  • 412 - precondition failed. Useful for instance when implementing POST/PUT/PATCH, and a particular property is required, but wasn't provided by the client.
  • 500 - a general server error. There is nothing the client can do about it.

Note that an API is not required to use all of these status codes, and could use any of the official HTTP status codes. In practise, the status codes above will probably be enough.

Response format

When a REST method is called, it will (usually) return some data. The REST definition doesn't say anything about what that format should be. Many developers believe that JSON is the only thing that a REST service can return, but that is incorrect. A REST service could return any content whatsoever, while JSON is certainly very popular in REST services.

One possible method is to inspect the "Accept" request header, and if the data format being requested can be served, then that is done. Otherwise, some default format (perhaps JSON) will be returned. The "Accept" header should contain a MIME type, such as "application/xml", "text/plain", "application/json" etc. Many Web API frameworks will handle this automatically for us.

Versioning

All APIs will need maintenance, and this will probably lead to changes in the API. Some of these changes may be breaking, i.e. they will require different behaviour for clients. This is very likely, and should therefore be anticipated from the start.

One way to solve this is to put a version number into the URL:

  • /api/v1/courses
  • /api/v1/courses/26892
  • etc.

This way, if a given resource changes, it could get a new version number, while the old version of the resource could be maintained (not indefinitely though, as a general rule of thumb we should phase out old versions, and only maintain older versions for maybe 2-3 versions).

This is by no means the only way to implement versioning. Other possibilities include:

  • adding a special HTTP Header to the request, which would indicate what version is being requested.
  • modifying the "Accept" HTTP header to include the version.

None of those options is without faults, as is discussed in this article. Also, a recent criticism regarding the Uber API found here is not very friendly towards putting the version in the URL. There is no universally accepted "best way" to implement this though.

REST Maturity Model

The REST Maturity Model was defined in 2008 by Leonard Richardson.

It has 4 levels:

  • Level 0: A single URL, often only accessible using HTTP POST.
  • Level 1: Resources - i.e. the application defines different URIs for each resource.
  • Level 2: HTTP verbs - the application uses GET/POST/PUT/PATCH/DELETE instead of putting the verb in the URL (/api/v1/courses/create)
  • Level 3: Hypermedia controls - the application only exposes a single URI, and the client doesn't really need to know anything about the available resources, other than the root resource.

Most REST services written today are at level 2 (or perhaps 2.5).

HATEOAS

At level 3, a REST service will only define a single URL (a Root URL), which all subresources are accessible through. See here: http://www.bizcoder.com/hypermedia-as-the-engine-of-application-state-the-client-server-dance

At this level, it is even possible to have URLs which aren't hackable. For web services which are at level 1 or 2, URLs usually are hackable, i.e. it is usually fairly obvious what each URL does.

Example of HATEOAS response:

{
    "name": "Alice",
    "links": [ {
        "rel": "self",
        "href": "http://localhost:8080/customer/1"
    } ]
}
  • rel which stands for relationship, is a self-referencing hyperlink.
  • href is a URL that links to Alice's profile

Tools

Rest Clients

Further reading