Download - Talking Heads - writing an API does not need to be a "psycho killer"

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]