This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Building Restful services (Notes)Definitions: | |
An idempotent operation produces the same result, whether it is invoked 1 or multiple times. | |
PUT, GET, DELETE and HEAD are idempotent operations | |
****************************** | |
* GET as Read * | |
****************************** | |
****************************** | |
* DELETE as Delete * | |
****************************** | |
****************************** | |
* HEAD as Headers, no body * | |
****************************** | |
****************************** | |
* PUT as Create * | |
****************************** | |
- Identifier is known upfront by client. | |
- The client must provide all properties/data of that resource | |
PUT /users/{robbypelssers} | |
{ | |
"firstname": "Robby", | |
"lastname": "Pelssers", | |
"age": 36, | |
"phone": "phonenumberOld" | |
} | |
****************************** | |
* POST as Create * | |
****************************** | |
- On a parent resource (typicaly a collection) | |
POST /users | |
{ | |
"firstname": "Robby", | |
"lastname": "Pelssers", | |
"age": 36, | |
"phone": "phonenumberOld" | |
} | |
Response: 201 created | |
Location: http://www.mycompany.com/users/{robbypelssers} | |
****************************** | |
* POST as Update * | |
****************************** | |
- partial or full update | |
- saves bandwidth | |
POST /users/{robbypelssers} | |
{ | |
"phone": "phonenumberNew" | |
} | |
Response: 200 OK | |
****************************** | |
* Media Types * | |
****************************** | |
- Format specification + parsing rules | |
- Request: Accept header | |
- Response: Content-Type header | |
application/json | |
****************************** | |
* Designing Rest Services * | |
****************************** | |
* Pick an easy to remember base URL, e.g. http(s)://api.mycompany.nl | |
* Versioning: | |
http(s)://api.mycompany.nl/v1 | |
or | |
Media-Type | |
application/json;application,v=1 | |
* Resource Formats: | |
Date / Time / Timestamps: use ISO 8601 | |
* Content Negotiation (2 ways of negotiation) | |
[1] Header | |
- Acccept Header | |
- Header values comma delimited in order of preference | |
GET /user/robbypelssers | |
Accept: application/json, text-plain | |
[2] Resource extension (conventionally overrides Accept header) | |
/users/robbypelssers.json | |
/users/robbypelssers.xml | |
/users/robbypelssers.csv | |
* Resource referencing (aka linking) | |
- Hypermedia is paramount | |
- Linking is fundamental to scalability | |
- Tricky in JSON | |
- XML has it (XLink), JSON doesn't | |
Instance reference: | |
GET users/robbypelssers | |
200 OK | |
{ | |
"href": "http://api.mycompany.nl/users/robbypelssers", | |
"firstname": "Robby", | |
"lastname": "Pelssers", | |
"age": 36, | |
"phone": "phonenumberNew", | |
"children": [ | |
{"href": "http://api.mycompany.nl/users/lindseypelssers"}, | |
{"href": "http://api.mycompany.nl/users/valeriepelssers"} | |
] | |
} | |
* Reference expansion (aka Entity expansion, Link expansion) | |
- reduce number of requests by allowing clients to specify expansion of specific properties | |
GET users/robbypelssers?expand=children | |
{ | |
"href": "http://api.mycompany.nl/users/robbypelssers", | |
"firstname": "Robby", | |
"lastname": "Pelssers", | |
"age": 36, | |
"phone": "phonenumberNew", | |
"children": [ | |
{ | |
"href": "http://api.mycompany.nl/users/lindseypelssers", | |
"firstname": "Lindsey", | |
"lastname": "Pelssers", | |
"age": 10 | |
}, | |
{ | |
"href": "http://api.mycompany.nl/users/valeriepelssers", | |
"firstname": "Valerie", | |
"lastname": "Pelssers", | |
"age": 6 | |
} | |
] | |
} | |
GET users/robbypelssers?expand=* --> expand everything 1 level deep | |
* Partial representation | |
GET users/robbypelssers?fields=firstname, lastname | |
* Pagination | |
Collection resource supports query params offset and limit | |
GET /users?offset=50&limit=25 | |
GET /users | |
Response: 200 OK | |
{ | |
"href": "http://api.mycompany.nl/users" | |
"limit": 25 | |
"offset": 0, | |
"first": {"href": "http://api.mycompany.nl/users?offset=0"}, | |
"previous": null, | |
"next": {"href": "http://api.mycompany.nl/users?offset=25"}, | |
"last": {"href": "http://api.mycompany.nl/users?offset=145"}, | |
"items": [ | |
{...}, | |
{...} | |
] | |
} | |
* Errors | |
- As descriptive as possible | |
- As much information as possible | |
- Developers are your customers | |
POST /users | |
Response: 409 conflict | |
{ | |
"status": 409, | |
"code": 40912, | |
"exception": "UserAlreadyExistsException", | |
"message: "A user 'robbypelssers' already exists", | |
"href": "http://api.mycompany.nl/docs/api/errors/40912" | |
} | |
* IDs | |
- should be opaque | |
- Should be globally unique | |
- Avoid sequential numbers (contention) | |
- Good candidates (UUID, Url64) | |
* HTTP Method overrides | |
POST /user/robbypelssers?_method=DELETE | |
* Caching and concurrency control | |
- Server (initial response) | |
Etag: "678988546345a76b" | |
- Client (later request) | |
If-None-Match: "678988546345a76b" | |
- Server (later response) | |
304: Not Modified | |
* Security | |
- Avoid sessions when possible | |
- Authenticate every request if necessary | |
- Stateless | |
- Authorize based upon resource content, not URL | |
- Use existing protocols: OAuth, Basic over SSL only | |
- Use custom scheme | |
- only if you provide client code / SDK | |
- only if you really know what you're doing | |
* Maintenance | |
- Use HTTP Redirects | |
- Create abstraction layer/ endpoints when migrating | |
- Use well defined custom Media Types |
No comments:
Post a Comment