talking heads - writing an api does not need to be a "psycho killer"

73
Talking Heads REST api’s don’t need to be your “psycho-killer”

Upload: theo-van-hoesel

Post on 23-Jun-2015

203 views

Category:

Internet


0 download

DESCRIPTION

In a world where things get more and more connected to services there is a dire need to understand the principals off HTTP protocols. It is simply not enough to 'just return' some data and expect the the client knows what to do. Proper clients, wether it is a 'thing' or 'software' can intelligently communicate with servers. And servers should take the responsibility to give clear answers.... or let the client know that it is not fully clear what the client requested. This talk is a brief overview on how client and server communicate with each other concerning caching, content-negociation and the methods provided. Its all about what is going on inside the HEADs when client and server are TALKING http. Then there is Web::Machine, based on Basho's work. It brings structure in the whole decision tree of what is happening with all the Request Header Fileds. A nice work from, but arguably, it has some weak spots. And of course a great new module for Dancer2 will be introduced: Dancer2::Plugin::HTTP::Auth::Extensible, the first of a series that will make life easy when developing REST api's with Dancer

TRANSCRIPT

Page 1: Talking Heads - writing an API does not need to be a "psycho killer"

Talking HeadsREST api’s don’t need to be your “psycho-killer”

Page 2: Talking Heads - writing an API does not need to be a "psycho killer"

Dancer 2A Lightweight WEB Framework

… that does nothing wrong

Page 3: Talking Heads - writing an API does not need to be a "psycho killer"

Dancer 2A Lightweight WEB Framework

… that does nothing you don’t want

Page 4: Talking Heads - writing an API does not need to be a "psycho killer"

Dancer 2A Lightweight WEB Framework … does it do what it should do?

Page 5: Talking Heads - writing an API does not need to be a "psycho killer"

RESTful API’s Dancer 2

HTTP protocols and RFC’s

Page 6: Talking Heads - writing an API does not need to be a "psycho killer"

Overview• HTTP 1.1 Protocol

• Let’s Dance

• Caching

• Conditional Requests

• Content Negotiation

• Authorization

Page 7: Talking Heads - writing an API does not need to be a "psycho killer"

HTTP/1.1 Protocol

• Requests

• Responses

• Request Methods

• Header Fields

• Status codes

Page 8: Talking Heads - writing an API does not need to be a "psycho killer"

HTTP 1.1 Protocol Request

GET /some_resource HTTP/1.1 Host: server.tst User-Agent: insane_client/0.1 Accept: text/html; q=0.8, image/gif; q=0.3 Accept-Charset: iso8509-1 Content-Length: 123 Here can come some content, for what ever purpose

Page 9: Talking Heads - writing an API does not need to be a "psycho killer"

HTTP 1.1 Protocol Request

GET /some_resource HTTP/1.1 Host: server.tst User-Agent: insane_client/0.1 Accept: text/html; q=0.8, image/gif; q=0.3 Accept-Charset: iso8509-1 Content-Length: 123 Here can come some content, for what ever purpose

Page 10: Talking Heads - writing an API does not need to be a "psycho killer"

HTTP 1.1 Protocol Request

GET /some_resource HTTP/1.1 Host: server.tst User-Agent: insane_client/0.1 Accept: text/html; q=0.8, image/gif; q=0.3 Accept-Charset: iso8509-1 Content-Length: 123 Here can come some content, for what ever purpose

Page 11: Talking Heads - writing an API does not need to be a "psycho killer"

HTTP 1.1 Protocol Request

GET /some_resource HTTP/1.1 Host: server.tst User-Agent: insane_client/0.1 Accept: text/html; q=0.8, image/gif; q=0.3 Accept-Charset: iso8509-1 Content-Length: 123 Here can come some content, for what ever purpose

Page 12: Talking Heads - writing an API does not need to be a "psycho killer"

HTTP 1.1 Protocol Request

GET /some_resource HTTP/1.1 Host: server.tst User-Agent: insane_client/0.1 Accept: text/html; q=0.8, image/gif; q=0.3 Accept-Charset: iso8509-1 Content-Length: 123 Here can come some content, for what ever purpose

Page 13: Talking Heads - writing an API does not need to be a "psycho killer"

HTTP 1.1 Protocol Response

HTTP/1.1 200 OK Server: dancer/2.0 Date: Tue, 14 Oct 2014 12:34:56 GMT Content-Type: text/plain Charset: iso8509-1 Content-Length: 456 You asked for something, here it is!

Page 14: Talking Heads - writing an API does not need to be a "psycho killer"

HTTP 1.1 Protocol Response

HTTP/1.1 200 OK Server: dancer/2.0 Date: Tue, 14 Oct 2014 12:34:56 GMT Content-Type: text/plain Charset: iso8509-1 Content-Length: 456 You asked for something, here it is!

Page 15: Talking Heads - writing an API does not need to be a "psycho killer"

HTTP 1.1 Protocol Response

HTTP/1.1 200 OK Server: dancer/2.0 Date: Tue, 14 Oct 2014 12:34:56 GMT Content-Type: text/plain Charset: iso8509-1 Content-Length: 456 You asked for something, here it is!

Page 16: Talking Heads - writing an API does not need to be a "psycho killer"

HTTP 1.1 Protocol Response

HTTP/1.1 200 OK Server: dancer/2.0 Date: Tue, 14 Oct 2014 12:34:56 GMT Content-Type: text/plain Charset: iso8509-1 Content-Length: 456 You asked for something, here it is!

Page 17: Talking Heads - writing an API does not need to be a "psycho killer"

HTTP 1.1 Protocol Response

HTTP/1.1 200 OK Server: dancer/2.0 Date: Tue, 14 Oct 2014 12:34:56 GMT Content-Type: text/plain Charset: iso8509-1 Content-Length: 456 You asked for something, here it is!

Page 18: Talking Heads - writing an API does not need to be a "psycho killer"

HTTP 1.1 Protocol Request Methodes

• GET

• POST

• PUT

• DELETE

• HEAD

• OPTIONS

• PATCH

Page 19: Talking Heads - writing an API does not need to be a "psycho killer"

HTTP 1.1 Protocol Header Fields

• Host

• User-Agent

• If-Modified-Since

• Content-Type

• Content-Length

• Location

• Server

• Last-Modification

• Content-Type

• Content-Length

Page 20: Talking Heads - writing an API does not need to be a "psycho killer"

HTTP 1.1 Protocol Status Codes

• 1—— Informational

• 2—— Succes

• 3—— Redirection

• 4—— Client Error

• 5—— Server

Page 21: Talking Heads - writing an API does not need to be a "psycho killer"

Status: 404Not Found

Page 22: Talking Heads - writing an API does not need to be a "psycho killer"

Status: 404Not Found

Page 23: Talking Heads - writing an API does not need to be a "psycho killer"

Status: 500Internal Server Error

Page 24: Talking Heads - writing an API does not need to be a "psycho killer"

HTTP 1.1 Protocol Status 1—— Informational

• 100 Continue

Page 25: Talking Heads - writing an API does not need to be a "psycho killer"

HTTP 1.1 Protocol Status 2—— Succes

• 200 OK

• 201 Created

• 202 Accepted

• 204 No Content

Page 26: Talking Heads - writing an API does not need to be a "psycho killer"

HTTP 1.1 Protocol Status 3—— Redirection

• 300 Multiple Choices

• 301 Moved Permanently

• 304 Not Modified

Page 27: Talking Heads - writing an API does not need to be a "psycho killer"

HTTP 1.1 Protocol Status 4—— Client Error

• 400 Bad Request

• 401 Unauthorized

• 403 Forbidden

• 404 Not Found

• 405 Method Not Allowed

• 407 Not Acceptable

• 410 Gone

• 412 Precondition Failed

Page 28: Talking Heads - writing an API does not need to be a "psycho killer"

HTTP 1.1 Protocol Status 5—— Server Error

• 500 Internal Server Error

• 501 Not Implemented

• 503 Service Unavailable

Page 29: Talking Heads - writing an API does not need to be a "psycho killer"

It takes Two

Dancer 2 & LWP

Page 30: Talking Heads - writing an API does not need to be a "psycho killer"

Client Server Let’s Dance

use Dancer2; get ’/user/:id’ => { my $user_info = resultset(’users’)->find(id=>params(’id’)); template ’user.tt’, $user_info; }; dance; 1;

Page 31: Talking Heads - writing an API does not need to be a "psycho killer"

Client Server Let’s Dance

use Dancer2; get ’/user/:id’ => { my $user_info = resultset(’users’)->find(id=>params(’id’)); template ’user.tt’, $user_info; }; dance; 1;

Page 32: Talking Heads - writing an API does not need to be a "psycho killer"

Client Server Let’s Dance

use Dancer2; get ’/user/:id’ => { my $user_info = resultset(’users’)->find(id=>params(’id’)); template ’user.tt’, $user_info; }; dance; 1;

Page 33: Talking Heads - writing an API does not need to be a "psycho killer"

Client Server Let’s Dance

use Dancer2; get ’/user/:id’ => { my $user_info = resultset(’users’)->find(id=>params(’id’)); template ’user.tt’, $user_info; }; dance; 1;

Page 34: Talking Heads - writing an API does not need to be a "psycho killer"

Client Server Let’s Dance

use Dancer2; get ’/user/:id’ => { my $user_info = resultset(’users’)->find(id=>params(’id’)); template ’user.tt’, $user_info; }; dance; 1;

Page 35: Talking Heads - writing an API does not need to be a "psycho killer"

Client Server Let’s Dance

use Dancer2; get ’/user/:id’ => { my $user_info = resultset(’users’)->find(id=>params(’id’)); template ’user.tt’, $user_info; }; dance; 1;

Page 36: Talking Heads - writing an API does not need to be a "psycho killer"

Client Server LWP::UserAgent

use LWP; use LWP::UserAgent; my $agent = LWP::UserAgent->new; my $response = $agent->get(’/user/999-999’);

Page 37: Talking Heads - writing an API does not need to be a "psycho killer"

Caching Temporary Storage

• Reduce cost

• Improve Responsivness

Page 38: Talking Heads - writing an API does not need to be a "psycho killer"

Caching Temporary Storage

• Client Side

• Public Proxy

• Server Side

Page 39: Talking Heads - writing an API does not need to be a "psycho killer"

Caching If-Modified-Since

use LWP; use LWP::UserAgent; my $agent = LWP::UserAgent->new; my $request = HTTP::Request->new( GET => ’/user/999-999-999’); $request->header( If-Modified-Since => Tue, 14 Oct 2014 12:34:56 GMT ); my $response = $agent->request($request);

Page 40: Talking Heads - writing an API does not need to be a "psycho killer"

Caching Response

• Response Status: 304 Not Modified

• Last-Modified: Tue, 07 Oct 2014 12:34:56 GMT

• Age: 3600

• Expire: Tue, 21 Oct 2014 12:34:56 GMT

• Cache-Control: public | private | max-age | no-cache

Page 41: Talking Heads - writing an API does not need to be a "psycho killer"

Conditional Requests

• Stateless

• No Resource Locking

• Only execute request if not modified since last

• Only execute request if the resource is still the same

Page 42: Talking Heads - writing an API does not need to be a "psycho killer"

Conditional Requests If-Unmodified-Since

use LWP; use LWP::UserAgent; my $agent = LWP::UserAgent->new; my $request = HTTP::Request->new( DELETE => ’/user/999-999-999’); $request->header( If-Unmodified-Since => ’Tue, 14 Oct 2014 12:34:56 GMT’); my $response = $agent->request($request);

Page 43: Talking Heads - writing an API does not need to be a "psycho killer"

Conditional Requests If-Match

use LWP; use LWP::UserAgent; my $agent = LWP::UserAgent->new; my $request = HTTP::Request->new( DELETE => ’/user/999-999-999’); $request->header( If-Match => ’a23dfe4532dab7b21a83d3e0f4c2a6f1’ ); my $response = $agent->request($request);

Page 44: Talking Heads - writing an API does not need to be a "psycho killer"

Conditional Requests Response

• Response Status: 412 Precondition Failed

• Response Status: 428 Precondition Required

• Last-Modified: Tue, 07 Oct 2014 12:34:56 GMT

• ETag: a23dfe4532dab7b21a83d3e0f4c2a6f1

Page 45: Talking Heads - writing an API does not need to be a "psycho killer"

Content Negotiation

• the same resource

• another representation

• the same URL

• client can let the server know what type is preferred

• server will try to deliver in requested content

• caches need to know that this is another variant

• the same URL

Page 46: Talking Heads - writing an API does not need to be a "psycho killer"

Content Negotiation Resources & Representation• Uniform Resouse Identifier

• Does not say anything about representation:

• Charset

• Encoding

• Language

• Format

Page 47: Talking Heads - writing an API does not need to be a "psycho killer"

Content Negotiation Accept & friends

• Accept: text/html, text/plain, image/png

• Accept-Language: nl, en, fr

• Accept-Charset: iso_

• Accept-Encoding: gzip

Page 48: Talking Heads - writing an API does not need to be a "psycho killer"

Content Negotiation Accepting Preferences

• The client can have some nice preferences

• it might like some representation above the other

• it might not like anything else

• The server can only deliver some representations

• it might deliver something it prefers

• it might give a list of options

Page 49: Talking Heads - writing an API does not need to be a "psycho killer"

Content Negotiation Mutable Serializer

use Dancer2; use Dancer2::Plugin::Mutable::Serializer;get ’/user/:id’ => { my $user_info = resultset(’users’)->find(id=>params(’id’)); return $user_info; }; 1;

Page 50: Talking Heads - writing an API does not need to be a "psycho killer"

Content Negotiation Mutable Serializer

use LWP; use LWP::UserAgent; my $agent = LWP::UserAgent->new; my $request = HTTP::Request->new( GET => ’/user/999-999-999’); $request->header( Accept => ’application/json’ ); my $response = $agent->request($request);

Page 51: Talking Heads - writing an API does not need to be a "psycho killer"

Content Negotiation Mutable Serializer

use LWP; use LWP::UserAgent; my $agent = LWP::UserAgent->new; my $request = HTTP::Request->new( GET => ’/user/999-999-999’); $request->header( Accept => ’application/json’ ); $request->header( Content-Type => ’application/xml’ ); my $response = $agent->request($request);

Page 52: Talking Heads - writing an API does not need to be a "psycho killer"

Content Negotiation Let’s do things differently

use Dancer2; get ’/user/:id’ => { my $user_info = resultset(’users’)->find(id=>params(’id’)); if (grep $_ eq ’text/html’, header(’Accept’)) { template ’user.tt’, $user_info; } if (grep $_ eq ’application/json’, header(’Accept’’)) { return to_json $user_info; } if (grep $_ eq ’application/xml’, header(’Accept’’)) { return to_xml $user_info; } }; 1;

Page 53: Talking Heads - writing an API does not need to be a "psycho killer"

Content Negotiation Let’s do things differently

use LWP; use LWP::UserAgent; my $agent = LWP::UserAgent->new; my $request = HTTP::Request->new( GET => ’/user/999-999-999’); $request->header( Accept => ’application/json’ ); my $response = $agent->request($request);

Page 54: Talking Heads - writing an API does not need to be a "psycho killer"

Content Negotiation Let’s do things differently

use Dancer2; get ’/user/:id’ => { my $user_info = resultset(’users’)->find(id=>params(’id’)); if (grep $_ eq ’text/html’, header(’Accept’)) { template ’user.tt’, $user_info; } if (grep $_ eq ’application/json’, header(’Accept’’)) { return to_json $user_info; } if (grep $_ eq ’application/xml’, header(’Accept’’)) { return to_xml $user_info; } }; 1;

Page 55: Talking Heads - writing an API does not need to be a "psycho killer"

Content Negotiation Let’s do things differently

use LWP; use LWP::UserAgent; my $agent = LWP::UserAgent->new; my $request = HTTP::Request->new( GET => ’/user/999-999-999’); $request->header( Accept => ’application/xml; q=0.1, text/html’ ); my $response = $agent->request($request);

Page 56: Talking Heads - writing an API does not need to be a "psycho killer"

Content Negotiation Let’s do things differently

use Dancer2; get ’/user/:id’ => { my $user_info = resultset(’users’)->find(id=>params(’id’)); if (grep $_ eq ’text/html’, header(’Accept’)) { template ’user.tt’, $user_info; } if (grep $_ eq ’application/json’, header(’Accept’’)) { return to_json $user_info; } if (grep $_ eq ’application/xml’, header(’Accept’’)) { return to_xml $user_info; } }; 1;

Page 57: Talking Heads - writing an API does not need to be a "psycho killer"

Content Negotiation Let’s get Messy

Accept: text/plain; q=0.3, text/html; q=0.5, */*; q=0.0

text/plainq=0.3

text/htmlq=0.5

*/*q=0.0

«I’m fine with plain-txt, but like html more… but if it’s anything else, I DON’T WANT THAT»

Page 58: Talking Heads - writing an API does not need to be a "psycho killer"

Content Negotiation Let’s get Messy

use Dancer2; use Dancer2::Plugin::HTTP::ContentNegotiation;get ’/user/:id’ => { my $user_info = resultset(’users’)->find(id=>params(’id’)); http_choose_accept ( ’text/html’ => sub {template ’user.tt’, $user_info}, ’application/json’ => sub {to_json $user_info}, ’application/xml’ => sub {to_xml $user_info}, ); };

Page 59: Talking Heads - writing an API does not need to be a "psycho killer"

Content Negotiation Let’s get Messy

use Dancer2; use Dancer2::Plugin::HTTP::ContentNegotiation;get ’/user/:id’ => { my $user_info = resultset(’users’)->find(id=>params(’id’)); http_choose_accept ( [’image/png’, ’image/jpg’, ’image/gif’] => sub {magick(http_accept->minor)}, ); };

Page 60: Talking Heads - writing an API does not need to be a "psycho killer"

Content Negotiation Resources & Representation• Status 300: Multiple Choices

• format at the end of the URL

• language at the beginning of the URL

• Status 406: Not Acceptable

• Vary: Accept, Aceept-Language . . .

Page 61: Talking Heads - writing an API does not need to be a "psycho killer"

Auth Resource Access

• Stateless

• Submit credentials with every request

• Authentication

• Username & Password

• Authentication Scheme

• Authorisation

• Rolebased access

Page 62: Talking Heads - writing an API does not need to be a "psycho killer"

Auth Usual WEB handling

1. Attempt to acces some page

2. Not Authorised ?

3. Go to /login

4. Send credentials

5. Authenticated Now ?

6. Setup session cookie

7. Go back to original requested page

Page 63: Talking Heads - writing an API does not need to be a "psycho killer"

Auth REST api

1. Attempt to acces some page

2. Not Authenticated ?

3. Status: 401 “Not Authorized”

4. Resend same request including credentials

5. Authorised ?

• Continue processing

• Status: 403 “Forbidden”

Page 64: Talking Heads - writing an API does not need to be a "psycho killer"

Auth HTTP Auth::Extensible

use Dancer2; use Dancer2::Plugin::HTTP::Auth::Extensible; get '/realm' => http_require_authentication sub { "You are logged in using realm: " . http_realm }; get '/vodka' => http_require_role HardDrinker => sub { "Only hard drinkers get vodka"; };

Page 65: Talking Heads - writing an API does not need to be a "psycho killer"

Auth HTTP Auth::Extensible

use Dancer2; use Dancer2::Plugin::HTTP::Auth::Extensible; get '/realm' => http_require_authentication sub { "You are logged in using realm: " . http_realm }; get '/vodka' => http_require_role HardDrinker => sub { "Only hard drinkers get vodka"; };

Page 66: Talking Heads - writing an API does not need to be a "psycho killer"

Auth HTTP Auth::Extensible

use Dancer2; use Dancer2::Plugin::HTTP::Auth::Extensible; get '/realm' => http_require_authentication sub { "You are logged in using realm: " . http_realm }; get '/vodka' => http_require_role HardDrinker => sub { "Only hard drinkers get vodka"; };

Page 67: Talking Heads - writing an API does not need to be a "psycho killer"

Auth HTTP Auth::Extensible

plugins: 'HTTP::Auth::Extensible': realms: example: provider: Config users: - user: ‘beerdrinker' pass: ‘password' name: 'Beer drinker’ roles: - BeerDrinker

Page 68: Talking Heads - writing an API does not need to be a "psycho killer"

Auth Resource Access

• Status 401: Unauthorized

• You should return a WWW-Authenticate also

• HTTP Request Header Field: Authorize

• Status 403: Forbidden

Page 69: Talking Heads - writing an API does not need to be a "psycho killer"

Dancer2::Plugin::HTTP Family

• Dancer2::Plugin::HTTP::ContentNegotiation

• Dancer2::Plugin::HTTP::Auth::Extensible

• Dancer2::Plugin::HTTP::Conditional

• Dancer2::Plugin::HTTP::Cache

Page 70: Talking Heads - writing an API does not need to be a "psycho killer"

Dancer2::Plugin::HTTP HTTP::Header::ActionPack

• Authentication / Authorisation

• MIME-types

• DateTime conversion

Page 71: Talking Heads - writing an API does not need to be a "psycho killer"

Dancer2::Plugin::HTTP HTTP::Header::ActionPack

• Authentication / Authorisation

• MIME-types

• DateTime conversion

Page 72: Talking Heads - writing an API does not need to be a "psycho killer"

Net::WebMachine HTTP::Header::ActionPack

• Basho schema

• Ruby implementation

• Stevan Little / Dave Rolsky

Page 73: Talking Heads - writing an API does not need to be a "psycho killer"

Talking Headshttp://github/THEMA-MEDIA/

[email protected]