Post on 12-Apr-2017
transcript
CLAUDIO D’ALICANDRO
Backend developer @Chupamobile.com
@ClaudioSThought on twitter
Pugger since 2013
TECH TALKS
PHP Fast API by @toretto460
ZendFramework by @lorenzoferrara
Laravel by @malatestafra
MongoDB by @kekko
… take a look at http://roma.grusp.org/
Booking engine requirements
● A user should be able to find a hotel so that he can
check the availability.
● A user should be able to show a list of room with
details so that he can choose one of them.
● A user should be able to find a hotel for the given
check-in/check-out date so that he can make a
reservation by choosing a free room.
Booking engine APIs
● Check the hotel availability
● Show the room detail
● Book a room
● Check the room availability
● Modify a booking
● Cancel a booking
RPC - Style
POST /booking-engine
Host: my-hotel.com
{
"action": "findHotelsByCity",
"args": {
"city": "Todi",
"order_by": "distance"
}
}
RPC - StyleHTTP/1.1 200 OK
{
"hotels": [
{
"id": "dahu5942hfki58-fjaau7645-lo987",
"name": "Hotel Europa",
"coordinates": { "lat": ..., "long": ...}
},
{
"id": "dr594dahty71013-jfuh628fh47ft37",
"name": "Hotel Asia",
"coordinates": { "lat": ..., "long": ...}
}
]
}
RPC - Style
POST /booking-engine
Host: my-hotel.com
{
"action": "getAvailability",
"args": {
"interval": {
"checkin": "2015-09-26",
"checkout": "2015-09-27"
},
"hotel_id": "dahu5942hfki58-fjaau7645-lo987"
}
}
SOAP - Request
POST /FindHotelByCity.asmx HTTP/1.1
Host: my-hotel.com
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://my-hotel.com/FindHotelByCity"
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://my-
hotel.com/">
<SOAP-ENV:Body>
<ns1:HotelsToFind>
<ns1:City>Todi</ns1:City>
</ns1:HotelsToFind>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
SOAP - ResponseHTTP/1.1 200 OK
Cache-Control: private, max-age=0
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.
org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<HotelList>
<Hotel id="w3dhfu8272dlo-ldo8364j">
<Name>Hotel Europa</Name>
<Coordinates lat=".." lon=".."><Coordinates>
</Hotel>
<Hotel id="w3dhfu8272dlo-ldo8364j">
<Name>Hotel Europa</Name>
<Coordinates lat=".." lon=".."><Coordinates>
</Hotel>
</HotelList>
</soap:Body>
</soap:Envelope>
is SOAP the key ?
● Documentation NOT SO READABLE
● Tunneling over HTTP POST BAD
● Non standard Errors BAD
● Impossible to CACHE REALLY BAD
REST Constraints
● Client-Server model
● Stateless
● Cacheable
● Layered System
● Uniform Interface
○ Identification of resources
○ Manipulation of resources through these
representations
○ Hypermedia as the engine of application state
Booking engine APIs
● Check the hotel availability
● Show the room detail
● Book a room
● Check the room availability
● Modify a booking
● Cancel a booking
Check the Hotel availability - RPC
POST /booking-engine-api
{
"action": "findRoom",
"params": {
"hotel": 12456,
"interval": {
"checkin": "2015-10-01",
"checkout": "2015-10-09"
},
"pax": 3
}
}
HTTP/1.1 200 OK
Date: Sun, 27 Sep 2015 10:00:45 GMT
Cache-Control: max-age=0, no-cache, no-store
Pragma: no-cache
{
"rooms": [
{
"id": 567,
"beds": ["single", "double"],
"amenities": [...]
},
...
]
}
Check the Hotel availability - REST
GET /api/hotels/12456/rooms?checkin=2015-
10-01&checkout=2015-10-09&pax=3
HTTP/1.1 200 OK
Date: Sun, 27 Sep 2015 10:00:45 GMT
{
"rooms": [
{
"id": 567,
"beds": ["single", "double"],
"amenities": [...]
},
...
]
}
HTTP CACHE
HTTP/1.1 200 OK
Date: Sun, 27 Sep 2015 10:00:45 GMT
Cache-Control: public, max-age=600
ETag: db87ju95dgtyg-12348765209
Expiration modelValidation model
I CAN’T CACHE IT
POST /booking-engine-api
{
"action": "findRoom",
"params": {
"interval": {
"checkin": "2015-10-01",
"checkout": "2015-10-09"
},
"pax": 3
}
"hotel": 12456
}
Check the Hotel availability
GET /api/hotels/12456/rooms?checkin=2015-10-01&checkout=2015-10-09&pax=3
HTTP/1.1 200 OK
Date: Sun, 27 Sep 2015 10:00:45 GMT
{
"error": "Hotel Not Found"
}
STATUS CODES 100 HTTP CONTINUE 101 HTTP SWITCHING PROTOCOLS 102 HTTP PROCESSING 201 HTTP CREATED 202 HTTP ACCEPTED 203 HTTP NON AUTHORITATIVE INFORMATION 204 HTTP NO CONTENT 205 HTTP RESET CONTENT 206 HTTP PARTIAL CONTENT 207 HTTP MULTI STATUS 208 HTTP ALREADY REPORTED 226 HTTP IM USED 300 HTTP MULTIPLE CHOICES 301 HTTP MOVED PERMANENTLY 302 HTTP FOUND 303 HTTP SEE OTHER 304 HTTP NOT MODIFIED 305 HTTP USE PROXY 306 HTTP RESERVED 307 HTTP TEMPORARY REDIRECT 308 HTTP PERMANENTLY REDIRECT 400 HTTP BAD REQUEST 401 HTTP UNAUTHORIZED 402 HTTP PAYMENT REQUIRED 403 HTTP FORBIDDEN 404 HTTP NOT FOUND 405 HTTP METHOD NOT ALLOWED 406 HTTP NOT ACCEPTABLE
407 HTTP PROXY AUTHENTICATION REQUIRED 408 HTTP REQUEST TIMEOUT 409 HTTP CONFLICT 410 HTTP GONE 411 HTTP LENGTH REQUIRED 412 HTTP PRECONDITION FAILED 413 HTTP REQUEST ENTITY TOO LARGE 414 HTTP REQUEST URI TOO LONG 415 HTTP UNSUPPORTED MEDIA TYPE 416 HTTP REQUESTED RANGE NOT SATISFIABLE 417 HTTP EXPECTATION FAILED 418 HTTP I AM A TEAPOT 422 HTTP UNPROCESSABLE ENTITY 423 HTTP LOCKED 424 HTTP FAILED DEPENDENCY 425 HTTP RESERVED FOR WEBDAV ADVANCED … 426 HTTP UPGRADE REQUIRED 428 HTTP PRECONDITION REQUIRED 429 HTTP TOO MANY REQUESTS 431 HTTP REQUEST HEADER FIELDS TOO LARGE 500 HTTP INTERNAL SERVER ERROR 501 HTTP NOT IMPLEMENTED 502 HTTP BAD GATEWAY 503 HTTP SERVICE UNAVAILABLE 504 HTTP GATEWAY TIMEOUT 505 HTTP VERSION NOT SUPPORTED 506 HTTP VARIANT ALSO NEGOTIATES EXPERIMENTAL 507 HTTP INSUFFICIENT STORAGE ...
200 HTTP OK SOAP is here
USE THE RIGHT STATUS CODE
GET /api/hotels/12456/rooms?checkin=2015-10-01&checkout=2015-10-09&pax=3
{...}
HTTP/1.1 200 OK
{"error": "Hotel Not Found"}
HTTP/1.1 404 Not Found
ex. MIDDLEWAREvar app = require('express')();
var logger = new (winston.Logger)({
transports: [
new (winston.transports.Console)({ level: 'info' })
]
});
app.use(function(req, res, next) {
logger.info("Received request: %s", JSON.stringify({
headers: req.headers,
method: req.method,
url: req.url
})
);
next();
});
var server = app.listen(3000);
ex. MIDDLEWARE
// File web/app.php
require_once __DIR__.'/../app/bootstrap.php.cache';
require_once __DIR__.'/../app/AppKernel.php';
require_once __DIR__.'/../app/AppCache.php';
use Symfony\Component\HttpFoundation\Request;
$kernel = new AppKernel('prod', false);
$kernel->loadClassCache();
// wrap the default AppKernel with the AppCache one
$kernel = new AppCache($kernel);
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);
Richardson Maturity Model
Level 0 - Plain Old XML
Level 1 - Resources
Level 2 - HTTP Verbs
Level 3 - Hypermedia Controls
HYPERMEDIA EXAMPLE{
"links": [
{
"rel": "new",
"href": "http://mycompany.hotels/api/hotels/12456/room/new"
},
],
"rooms": [
{
"id": 567,
"beds": ["single", "double"],
"links": [
{
"rel": "self",
"href": "http://mycompany.hotels/api/hotels/12456/room/567"
},
{
"rel": "amenities",
"href": "http://mycompany.hotels/api/hotels/12456/room/567/amenities"
}
]
}, ...
]
}
THE RESPONSE FOR THE CUSTOMER
# The Customer (from Android client)
GET /api/hotels/12456/room/new HTTP/1.1
Host: mycompany.hotels
HTTP/1.1 403 Forbidden
THE RESPONSE FOR THE ADMIN# The Admin (From the SPA in the backoffice)
GET /api/hotels/12456/room/new HTTP/1.1
Host: mycompany.hotels
HTTP/1.1 200 OK
{
"links": {
"ref": "action",
"method": "POST"
"href": "http://mycompany.hotels/api/hotels/12456/room"
}
room: {
"beds": {
"multiple": true,
"options": {
"single": {
"label": "Single"
},
"double": {
"label": "Double"
}
},
}
}
}
HATEOAS ISN’T A SILVER BULLET
The documentation is important, but instead of explaining what to look for and where, should explain how to look and how to interpret
the resources.
WITHSTAND BREAKING CHANGES
“The foolish and the dead alone never change their opinions”
- James Russell Lowell -
Versioning an interface is just a
"polite" way to kill deployed clients.
— Roy Fielding.
WRONG WAY #3Versioning by content type
GET /api/your/resource/idHost: yoursite.comAccept: application/vnd.mycorp.bookings.v2+jsonVary: Accept