HTTP and REpresentational State Transfer (REST)
HTTP Requests and Responses
An HTTP request consists of a verb (HTTP method), scheme (http/https), server, port and resource (noun), and query parameters.
e.g. GET https://127.0.0.1:8000/api/widgets/123?sort=-name
is actually transmitted like this
after establishing a TCP connection to port 8000:
GET /widgets/?sort=-name HTTP/1.1 Host: 127.0.0.1:8000 Authorization: Basic YWRtaW46YWRtaW4xMjM= User-Agent: curl/7.54.0 Accept: application/vnd.api+json
Common methods are:
Request Body
Usually where longer form parameters or content is POSTed. The Content-Type
header specifies what kind of content is being POSTed, PATCHed or PUT.
For our purposes this will be application/json
or a variant such as application/vnd.api+json
.
Response Body
The response to the request, which can be empty (204 No Content) and looks like this, for example for a 200 response:
HTTP/1.1 200 OK Date: Fri, 14 Dec 2018 16:26:02 GMT Server: WSGIServer/0.2 CPython/3.6.6 Content-Type: application/vnd.api+json Vary: Accept Allow: GET, POST, HEAD, OPTIONS X-Frame-Options: SAMEORIGIN Content-Length: 74 {"data":{"type":"widgets","id": "123","attributes":{"name":"can opener"}}}
Query Parameters
These are usually short parameters that somehow modify a request
Example: GET /api/v1/widgets?sort=-name,+qty
Headers
The HTTP Accept
header lists what (prioritized) content types the
requestor will accept. We’ll keep it simple and only
accept one response type: Accept: application/json
The HTTP Content-Type header specifies the format of the response:
Content-type: application/json
Authentication and Authorization
The HTTP Authorization
header is commonly used for access control. There
are 3 main styles:
-
Authorization: Bearer <token>
header – used with OAuth 2 and REST APIs. Stateless. -
Authorization: Basic <b64-encoded-user:password>
header – Common for server-to-server, including REST. Stateless. -
Session – Used by conventional browser-based apps with a user at one end. Maintains state across multiple HTTP request/response iterations via session cookies (
Set-Cookie
response andCookie
request headers).
Our “real” apps will use Bearer tokens. For testing in Django, it can be more convenient to use Basic auth as Bearer tokens have to be refreshed from time-to-time. We’ll see how to configure these soon.
Characteristics of RESTful APIs
Representational State Transfer (REST) is a core component of an HTTP-based object model style.
RESTful APIs, unlike SOAP APIs, use only the native HTTP methods as they were meant to be used.
-
Object-Relational Model (ORM) API using HTTP
-
Client-server - separation of concerns, allows independent evolution of components.
-
Stateless - no session state maintained between requests
-
Layered - Client/Server don’t care if additional layers (e.g. caches, load balancers) are in between.
-
Uniform interface:
-
Resource collections and items within collections: ID in URL
-
Use of HTTP request methods and responses as designed (contrasted with SOAP/WSDL)
-
-
Idempotent (hopefully) - same request can be duplicated with equivalent result
-
Cacheable - frequently referenced data can be cached to avoid unnecessary network traffic. Can happen at multiple levels (client, cache in front of server, cached in server, etc.)
Proper REST HTTP URLs are characterized by resources and resource collections which are plural nouns. The only verbs are the HTTP methods: GET, POST, DELETE, PATCH, PUT, etc.
For example:
GET /v1/registrations
returns a collection of individual registrations each having an <id>
.
GET /v1/registrations/<id>
returns an individual registration.
POST /v1/registrations
(with
a request body containing a JSON document with other information like the identity of the registrant)
results in a new registration object identified as:
/v1/registrations/<id>
.
HATEOAS: Hypermedia As The Engine Of Application State.
Given a starting URL, a client app should be able to discover everything it needs without any separate external documentation of the interface.
Avoid REST anti-patterns
It is unfortunately common to see non-RESTful patterns sneaking into what claim to be RESTful APIs. The most
common of these anti-patterns is turning a REST API endpoint into a SOAP-like endpoint by invoking
a remote method call. For example, POST /v1/courses/01ca197f-c00c-4f24-a743-091b62f1d500/enroll
.
You can immediately tell this is an anti-pattern because “enroll” is used here as a verb
and RESTful resources should only be nouns.
A good REST pattern
A RESTful approach to the above might be something along the lines of:
POST /v1/registrations/
with a body containing:
{ "data": { "type": "registrations", "attributes": { "uni": "abc1234", "enrollment": "requested" }, "relationships": { "courses": { "data": { "type": "courses", "id": "01ca197f-c00c-4f24-a743-091b62f1d500" } } } } }
A side-effect of this data being added to the Registration Model would be to invoke the
enrollment process. This could be synchronous, returning a final 201 Created
status
or asynchronous, returning a 202 Accepted
perhaps with a Location
header that indicates the URL
to check back at. See more in the REST Cookbook.
Keeping the HTTP RESTful allows us to take full advantage of all that HTTP has to offer including caching, the ability to operate through stateless proxies and so on.
Here’s an example of a 201 Created
response in which the enrollment process was completed synchronously:
HTTP/1.1 201 Created Date: Fri, 14 Dec 2018 17:02:55 GMT Server: WSGIServer/0.2 CPython/3.6.6 Content-Type: application/vnd.api+json Location: http://127.0.0.1:8000/v1/registrations/001b55e0-9a60-4386-98c7-4c856bb840b4/ Vary: Accept Allow: GET, POST, HEAD, OPTIONS X-Frame-Options: SAMEORIGIN Content-Length: 556
{ "data": { "type": "registrations", "id": "001b55e0-9a60-4386-98c7-4c856bb840b4", "attributes": { "uni": "abc1234", "enrollment": "confirmed" }, "relationships": { "courses": { "data": { "type": "courses", "id": "01ca197f-c00c-4f24-a743-091b62f1d500" } } }, "links": { "self": "http://127.0.0.1:8000/v1/registrations/001b55e0-9a60-4386-98c7-4c856bb840b4/" } } }
An asynchronous 202 response might have shown "enrollment": "pending"
. One would check back at the URL
shown in the Location
header (perhaps after waiting based on a Retry-After
header).
The newly-created resource URL is further represented in the self
link in the JSON-formatted response body.
This particular JSON example is styled using a format called {json:api}, which
we’ll delve into next.