Making Your Perl REST

136
Making Your Perl REST Sterling Hanenkamp, PPW 2008

Transcript of Making Your Perl REST

Page 1: Making Your Perl REST

Making Your Perl REST Sterling Hanenkamp, PPW 2008

Page 2: Making Your Perl REST

What is REST?

Page 3: Making Your Perl REST

What is REST?

• Ever heard of...

Page 4: Making Your Perl REST

What is REST?

• Ever heard of...

• SOAP?

Page 5: Making Your Perl REST

What is REST?

• Ever heard of...

• SOAP?

• RPC?

Page 6: Making Your Perl REST

What is REST?

• Ever heard of...

• SOAP?

• RPC?

• XML-RPC?

Page 7: Making Your Perl REST

What is REST?

• Ever heard of...

• SOAP?

• RPC?

• XML-RPC?

• WebDAV?

Page 8: Making Your Perl REST

What is REST?

• Ever heard of...

• SOAP?

• RPC?

• XML-RPC?

• WebDAV?

• It’s kind of like that.

Page 9: Making Your Perl REST

What is REST?

• Ever heard of...

• SOAP?

• RPC?

• XML-RPC?

• WebDAV?

• It’s kind of like that.

• Ever heard of...

Page 10: Making Your Perl REST

What is REST?

• Ever heard of...

• SOAP?

• RPC?

• XML-RPC?

• WebDAV?

• It’s kind of like that.

• Ever heard of...

• HTTP?

Page 11: Making Your Perl REST

What is REST?

• Ever heard of...

• SOAP?

• RPC?

• XML-RPC?

• WebDAV?

• It’s kind of like that.

• Ever heard of...

• HTTP?

• XML?

Page 12: Making Your Perl REST

What is REST?

• Ever heard of...

• SOAP?

• RPC?

• XML-RPC?

• WebDAV?

• It’s kind of like that.

• Ever heard of...

• HTTP?

• XML?

• YAML?

Page 13: Making Your Perl REST

What is REST?

• Ever heard of...

• SOAP?

• RPC?

• XML-RPC?

• WebDAV?

• It’s kind of like that.

• Ever heard of...

• HTTP?

• XML?

• YAML?

• JSON?

Page 14: Making Your Perl REST

What is REST?

• Ever heard of...

• SOAP?

• RPC?

• XML-RPC?

• WebDAV?

• It’s kind of like that.

• Ever heard of...

• HTTP?

• XML?

• YAML?

• JSON?

• It’s kind of like that too

Page 15: Making Your Perl REST

What are we NOT talking about?

Page 16: Making Your Perl REST

• REST is a general purpose term

What are we NOT talking about?

Page 17: Making Your Perl REST

• REST is a general purpose term

• I’m not talking about a general notion

What are we NOT talking about?

Page 18: Making Your Perl REST

• REST is a general purpose term

• I’m not talking about a general notion

• We’re talking about REST APIs

What are we NOT talking about?

Page 19: Making Your Perl REST

• REST is a general purpose term

• I’m not talking about a general notion

• We’re talking about REST APIs

• Web Services

What are we NOT talking about?

Page 20: Making Your Perl REST

• REST is a general purpose term

• I’m not talking about a general notion

• We’re talking about REST APIs

• Web Services

• Mashups!!!! Web 2.0!!! Yippee!!!

What are we NOT talking about?

Page 21: Making Your Perl REST

• REST is a general purpose term

• I’m not talking about a general notion

• We’re talking about REST APIs

• Web Services

• Mashups!!!! Web 2.0!!! Yippee!!!

What are we NOT talking about?

Page 22: Making Your Perl REST

• REST is a general purpose term

• I’m not talking about a general notion

• We’re talking about REST APIs

• Web Services

• Mashups!!!! Web 2.0!!! Yippee!!!

• I’ve used my lifetime supply of exclamation points...

What are we NOT talking about?

Page 23: Making Your Perl REST

The REST Triangle

Page 24: Making Your Perl REST

The REST Triangle

nouns http://www.onlamp.com

ver

bs

GET, P

OST, P

UT, DEL

ETE

content typesYAML, XML, JSON, etc.

Page 25: Making Your Perl REST

The REST Triangle

• Nouns (=URL)nouns http://www.onlamp.com

ver

bs

GET, P

OST, P

UT, DEL

ETE

content typesYAML, XML, JSON, etc.

Page 26: Making Your Perl REST

The REST Triangle

• Nouns (=URL)

• Verbs (=HTTP METHOD)

nouns http://www.onlamp.com

ver

bs

GET, P

OST, P

UT, DEL

ETE

content typesYAML, XML, JSON, etc.

Page 27: Making Your Perl REST

The REST Triangle

• Nouns (=URL)

• Verbs (=HTTP METHOD)

• Content (=MIME Type)

nouns http://www.onlamp.com

ver

bs

GET, P

OST, P

UT, DEL

ETE

content typesYAML, XML, JSON, etc.

Page 28: Making Your Perl REST

REST Web Services

Page 29: Making Your Perl REST

REST Web Services

CRUD

Page 30: Making Your Perl REST

REST Web Services

CRUD

Create

Page 31: Making Your Perl REST

REST Web Services

CRUD

Create

Read

Page 32: Making Your Perl REST

REST Web Services

CRUD

Create

Read

Update

Page 33: Making Your Perl REST

REST Web Services

CRUD

Create

Read

Update

Delete

Page 34: Making Your Perl REST

REST Web Services

• Treat your data like a static web page CRUD

Create

Read

Update

Delete

Page 35: Making Your Perl REST

REST Web Services

• Treat your data like a static web page

• Add data by POSTing a web page

CRUD

Create

Read

Update

Delete

Page 36: Making Your Perl REST

REST Web Services

• Treat your data like a static web page

• Add data by POSTing a web page

• Fetch data by GETting a web pageCRUD

Create

Read

Update

Delete

Page 37: Making Your Perl REST

REST Web Services

• Treat your data like a static web page

• Add data by POSTing a web page

• Fetch data by GETting a web page

• Update data by PUTing a web page

CRUD

Create

Read

Update

Delete

Page 38: Making Your Perl REST

REST Web Services

• Treat your data like a static web page

• Add data by POSTing a web page

• Fetch data by GETting a web page

• Update data by PUTing a web page

• Delete data by DELETing a web page

CRUD

Create

Read

Update

Delete

Page 39: Making Your Perl REST

REST Web Services

• Treat your data like a static web page

• Add data by POSTing a web page

• Fetch data by GETting a web page

• Update data by PUTing a web page

• Delete data by DELETing a web page

• E... it’s missing and it bugs me...

CRUD

Create

Read

Update

Delete

Page 40: Making Your Perl REST

Who Uses It

Page 41: Making Your Perl REST

Who Uses It

• Google

• Yahoo!

• Amazon

• Intuit

• Best Practical

• Facebook

• Socialtext

• Digg

• Twitter

• eBay

• Technorati

• Too many others to mention...

Page 42: Making Your Perl REST

What about other stuff?

Page 43: Making Your Perl REST

• RPC is a square peg

What about other stuff?

Page 44: Making Your Perl REST

• RPC is a square peg

• REST is round hole

What about other stuff?

Page 45: Making Your Perl REST

• RPC is a square peg

• REST is round hole

What about other stuff?

Page 46: Making Your Perl REST

• RPC is a square peg

• REST is round hole

• But it works!

What about other stuff?

Page 47: Making Your Perl REST

• RPC is a square peg

• REST is round hole

• But it works!

• Getting Info? GET

What about other stuff?

Page 48: Making Your Perl REST

• RPC is a square peg

• REST is round hole

• But it works!

• Getting Info? GET

• Modifying Something? POST

What about other stuff?

Page 49: Making Your Perl REST

• RPC is a square peg

• REST is round hole

• But it works!

• Getting Info? GET

• Modifying Something? POST

• Both? Not sure? POST

What about other stuff?

Page 50: Making Your Perl REST

ENOUGH BASICS. LET’S DO SOMETHING.

Page 51: Making Your Perl REST

# Manage your books from the command-line. Woo-hoo!% ./book list

Page 52: Making Your Perl REST

# Manage your books from the command-line. Woo-hoo!% ./book list0-8024-8160-4: .../library.cgi/=/model/book/id/0-8024-8160-40-85151-760-9: .../library.cgi/=/model/book/id/0-85151-760-90-936083-11-5: .../library.cgi/=/model/book/id/0-936083-11-5

Page 53: Making Your Perl REST

% ./book read 0-8024-8160-4

Page 54: Making Your Perl REST

---author: David Clotfeltercity: Chicagoid: 0-8024-8160-4isbn: 0-8024-8160-4publisher: Moody Publisherstitle: > Sinners in the Hands of a Good God: Reconciling Divine Judgment and Mercy'year: 2004

% ./book read 0-8024-8160-4

Page 55: Making Your Perl REST
Page 56: Making Your Perl REST

% ./book create reformation.yml

Page 57: Making Your Perl REST

% ./book create reformation.yml0-87552-183-5: .../library.cgi/=/model/book/id/0-87552-183-5

Page 58: Making Your Perl REST

% ./book create reformation.yml0-87552-183-5: .../library.cgi/=/model/book/id/0-87552-183-5

% ./book update 0-87552-183-5 reformation2.yml

Page 59: Making Your Perl REST

% ./book create reformation.yml0-87552-183-5: .../library.cgi/=/model/book/id/0-87552-183-5

% ./book update 0-87552-183-5 reformation2.ymlUpdated 0-87552-183-5

Page 60: Making Your Perl REST

% ./book create reformation.yml0-87552-183-5: .../library.cgi/=/model/book/id/0-87552-183-5

% ./book update 0-87552-183-5 reformation2.ymlUpdated 0-87552-183-5

% ./book delete 0-87552-183-5

Page 61: Making Your Perl REST

Deleted 0-87552-183-5

% ./book create reformation.yml0-87552-183-5: .../library.cgi/=/model/book/id/0-87552-183-5

% ./book update 0-87552-183-5 reformation2.ymlUpdated 0-87552-183-5

% ./book delete 0-87552-183-5

Page 62: Making Your Perl REST

Okay... But what did that do?

Putting it together

Page 63: Making Your Perl REST

Explaining the Nouns (a.k.a. URLs)

Page 64: Making Your Perl REST

Explaining the Nouns (a.k.a. URLs)

Scheme Borrowed from Jifty

Page 65: Making Your Perl REST

Explaining the Nouns (a.k.a. URLs)

What Example Why?

Prefix /= Marker for REST

Kind of Thing /=/model More Kinds

Name of Thing /=/model/book More Models

Field of Thing /=/model/book/id Multiple Identifiers

Value of Field /=/mode/book/id/1 Multiple Things

Scheme Borrowed from Jifty

Page 66: Making Your Perl REST

Explaining the Nouns (a.k.a. URLs)

What Example Why?

Prefix /= Marker for REST

Kind of Thing /=/model More Kinds

Name of Thing /=/model/book More Models

Field of Thing /=/model/book/id Multiple Identifiers

Value of Field /=/mode/book/id/1 Multiple Things

Scheme Borrowed from Jifty

Page 67: Making Your Perl REST

Explaining the Nouns (a.k.a. URLs)

What Example Why?

Prefix /= Marker for REST

Kind of Thing /=/model More Kinds

Name of Thing /=/model/book More Models

Field of Thing /=/model/book/id Multiple Identifiers

Value of Field /=/mode/book/id/1 Multiple Things

Scheme Borrowed from Jifty

Page 68: Making Your Perl REST

Explaining the Nouns (a.k.a. URLs)

What Example Why?

Prefix /= Marker for REST

Kind of Thing /=/model More Kinds

Name of Thing /=/model/book More Models

Field of Thing /=/model/book/id Multiple Identifiers

Value of Field /=/mode/book/id/1 Multiple Things

Scheme Borrowed from Jifty

Page 69: Making Your Perl REST

Explaining the Nouns (a.k.a. URLs)

What Example Why?

Prefix /= Marker for REST

Kind of Thing /=/model More Kinds

Name of Thing /=/model/book More Models

Field of Thing /=/model/book/id Multiple Identifiers

Value of Field /=/mode/book/id/1 Multiple Things

Scheme Borrowed from Jifty

Page 70: Making Your Perl REST

Explaining the Nouns (a.k.a. URLs)

What Example Why?

Prefix /= Marker for REST

Kind of Thing /=/model More Kinds

Name of Thing /=/model/book More Models

Field of Thing /=/model/book/id Multiple Identifiers

Value of Field /=/mode/book/id/1 Multiple Things

Scheme Borrowed from Jifty

Page 71: Making Your Perl REST

# book list - lists all the books the server returnssubcommand 'list' => sub {

# GET /=/model/book my $response = $ua->request(GET HOST.'/=/model/book/id');

# On success, find the link and print them out if ($response->is_success) { my @links = $response->content =~ /\bhref="([^"]+)"/gm; for my $url (@links) { my ($id) = $url =~ /([\d-]+)$/; print "$id: $url\n"; } }

# On failure, barf else { barf $response; }};

Read (List)

Page 72: Making Your Perl REST

# book list - lists all the books the server returnssubcommand 'list' => sub {

# GET /=/model/book my $response = $ua->request(GET HOST.'/=/model/book/id');

# On success, find the link and print them out if ($response->is_success) { my @links = $response->content =~ /\bhref="([^"]+)"/gm; for my $url (@links) { my ($id) = $url =~ /([\d-]+)$/; print "$id: $url\n"; } }

# On failure, barf else { barf $response; }};

Read (List)

Page 73: Making Your Perl REST

# book list - lists all the books the server returnssubcommand 'list' => sub {

# GET /=/model/book my $response = $ua->request(GET HOST.'/=/model/book/id');

# On success, find the link and print them out if ($response->is_success) { my @links = $response->content =~ /\bhref="([^"]+)"/gm; for my $url (@links) { my ($id) = $url =~ /([\d-]+)$/; print "$id: $url\n"; } }

# On failure, barf else { barf $response; }};

Read (List)

Page 74: Making Your Perl REST

# book list - lists all the books the server returnssubcommand 'list' => sub {

# GET /=/model/book my $response = $ua->request(GET HOST.'/=/model/book/id');

# On success, find the link and print them out if ($response->is_success) { my @links = $response->content =~ /\bhref="([^"]+)"/gm; for my $url (@links) { my ($id) = $url =~ /([\d-]+)$/; print "$id: $url\n"; } }

# On failure, barf else { barf $response; }};

Read (List)

Page 75: Making Your Perl REST

# book list - lists all the books the server returnssubcommand 'list' => sub {

# GET /=/model/book my $response = $ua->request(GET HOST.'/=/model/book/id');

# On success, find the link and print them out if ($response->is_success) { my @links = $response->content =~ /\bhref="([^"]+)"/gm; for my $url (@links) { my ($id) = $url =~ /([\d-]+)$/; print "$id: $url\n"; } }

# On failure, barf else { barf $response; }};

Read (List)

Page 76: Making Your Perl REST

# Get a whole list of available documentsGET qr{^/=/model/book/id$} => sub { print $q->header('text/html');

# Find all the files available my @items; for my $filename (glob get_local_path('*')) { my ($id) = $filename =~ m{([\d-]+)$}; next unless defined $id;

push @items, $q->li( $q->a({ href => absolute_url('/=/model/book/id/'.$id) }, $id), ); }

# List the items print $q->ul( @items);};

Read (List)

Page 77: Making Your Perl REST

# Get a whole list of available documentsGET qr{^/=/model/book/id$} => sub { print $q->header('text/html');

# Find all the files available my @items; for my $filename (glob get_local_path('*')) { my ($id) = $filename =~ m{([\d-]+)$}; next unless defined $id;

push @items, $q->li( $q->a({ href => absolute_url('/=/model/book/id/'.$id) }, $id), ); }

# List the items print $q->ul( @items);};

Read (List)

Page 78: Making Your Perl REST

# Get a whole list of available documentsGET qr{^/=/model/book/id$} => sub { print $q->header('text/html');

# Find all the files available my @items; for my $filename (glob get_local_path('*')) { my ($id) = $filename =~ m{([\d-]+)$}; next unless defined $id;

push @items, $q->li( $q->a({ href => absolute_url('/=/model/book/id/'.$id) }, $id), ); }

# List the items print $q->ul( @items);};

Read (List)

Page 79: Making Your Perl REST

# Get a whole list of available documentsGET qr{^/=/model/book/id$} => sub { print $q->header('text/html');

# Find all the files available my @items; for my $filename (glob get_local_path('*')) { my ($id) = $filename =~ m{([\d-]+)$}; next unless defined $id;

push @items, $q->li( $q->a({ href => absolute_url('/=/model/book/id/'.$id) }, $id), ); }

# List the items print $q->ul( @items);};

Read (List)

Page 80: Making Your Perl REST

# Get a whole list of available documentsGET qr{^/=/model/book/id$} => sub { print $q->header('text/html');

# Find all the files available my @items; for my $filename (glob get_local_path('*')) { my ($id) = $filename =~ m{([\d-]+)$}; next unless defined $id;

push @items, $q->li( $q->a({ href => absolute_url('/=/model/book/id/'.$id) }, $id), ); }

# List the items print $q->ul( @items);};

Read (List)

Page 81: Making Your Perl REST

# book read <id> - reads the book file for <id>subcommand 'read' => sub { my $id = shift @ARGV;

# GET /=/model/book/id/[id] my $response = $ua->request(GET HOST.'/=/model/book/id/'

.$id);

# On success, print the file if ($response->is_success) { print $response->content; }

# On failure, barf else { barf $response; }};

Read (Single)

Page 82: Making Your Perl REST

# book read <id> - reads the book file for <id>subcommand 'read' => sub { my $id = shift @ARGV;

# GET /=/model/book/id/[id] my $response = $ua->request(GET HOST.'/=/model/book/id/'

.$id);

# On success, print the file if ($response->is_success) { print $response->content; }

# On failure, barf else { barf $response; }};

Read (Single)

Page 83: Making Your Perl REST

# book read <id> - reads the book file for <id>subcommand 'read' => sub { my $id = shift @ARGV;

# GET /=/model/book/id/[id] my $response = $ua->request(GET HOST.'/=/model/book/id/'

.$id);

# On success, print the file if ($response->is_success) { print $response->content; }

# On failure, barf else { barf $response; }};

Read (Single)

Page 84: Making Your Perl REST

# book read <id> - reads the book file for <id>subcommand 'read' => sub { my $id = shift @ARGV;

# GET /=/model/book/id/[id] my $response = $ua->request(GET HOST.'/=/model/book/id/'

.$id);

# On success, print the file if ($response->is_success) { print $response->content; }

# On failure, barf else { barf $response; }};

Read (Single)

Page 85: Making Your Perl REST

# book read <id> - reads the book file for <id>subcommand 'read' => sub { my $id = shift @ARGV;

# GET /=/model/book/id/[id] my $response = $ua->request(GET HOST.'/=/model/book/id/'

.$id);

# On success, print the file if ($response->is_success) { print $response->content; }

# On failure, barf else { barf $response; }};

Read (Single)

Page 86: Making Your Perl REST

# Look up and read a resourceGET qr{^/=/model/book/id/([\d-]+)$} => sub { my $id= $1;

# Look up the resource file my $filename = get_local_path($id); if (-f $filename) {

# Open and slurp up the file and output the resource open my $bookfh, $filename or barf 500, "I Am Broke", "Cannot open $filename: $!";

print $q->header('text/yaml'); print do { local $/; <$bookfh> }; }

# No such resource exists else{ barf 404, "Where is What?", "Book for $id does not exist."; }};

Read (Single)

Page 87: Making Your Perl REST

# Look up and read a resourceGET qr{^/=/model/book/id/([\d-]+)$} => sub { my $id= $1;

# Look up the resource file my $filename = get_local_path($id); if (-f $filename) {

# Open and slurp up the file and output the resource open my $bookfh, $filename or barf 500, "I Am Broke", "Cannot open $filename: $!";

print $q->header('text/yaml'); print do { local $/; <$bookfh> }; }

# No such resource exists else{ barf 404, "Where is What?", "Book for $id does not exist."; }};

Read (Single)

Page 88: Making Your Perl REST

# Look up and read a resourceGET qr{^/=/model/book/id/([\d-]+)$} => sub { my $id= $1;

# Look up the resource file my $filename = get_local_path($id); if (-f $filename) {

# Open and slurp up the file and output the resource open my $bookfh, $filename or barf 500, "I Am Broke", "Cannot open $filename: $!";

print $q->header('text/yaml'); print do { local $/; <$bookfh> }; }

# No such resource exists else{ barf 404, "Where is What?", "Book for $id does not exist."; }};

Read (Single)

Page 89: Making Your Perl REST

# Look up and read a resourceGET qr{^/=/model/book/id/([\d-]+)$} => sub { my $id= $1;

# Look up the resource file my $filename = get_local_path($id); if (-f $filename) {

# Open and slurp up the file and output the resource open my $bookfh, $filename or barf 500, "I Am Broke", "Cannot open $filename: $!";

print $q->header('text/yaml'); print do { local $/; <$bookfh> }; }

# No such resource exists else{ barf 404, "Where is What?", "Book for $id does not exist."; }};

Read (Single)

Page 90: Making Your Perl REST

# Look up and read a resourceGET qr{^/=/model/book/id/([\d-]+)$} => sub { my $id= $1;

# Look up the resource file my $filename = get_local_path($id); if (-f $filename) {

# Open and slurp up the file and output the resource open my $bookfh, $filename or barf 500, "I Am Broke", "Cannot open $filename: $!";

print $q->header('text/yaml'); print do { local $/; <$bookfh> }; }

# No such resource exists else{ barf 404, "Where is What?", "Book for $id does not exist."; }};

Read (Single)

Page 91: Making Your Perl REST

# Look up and read a resourceGET qr{^/=/model/book/id/([\d-]+)$} => sub { my $id= $1;

# Look up the resource file my $filename = get_local_path($id); if (-f $filename) {

# Open and slurp up the file and output the resource open my $bookfh, $filename or barf 500, "I Am Broke", "Cannot open $filename: $!";

print $q->header('text/yaml'); print do { local $/; <$bookfh> }; }

# No such resource exists else{ barf 404, "Where is What?", "Book for $id does not exist."; }};

Read (Single)

Page 92: Making Your Perl REST

# book create <filename> - submits the book file in <filename> to the serversubcommand 'create' => sub { my $file = shift @ARGV;

# Slurp up the contents of the given filename my $book_data = slurp $file;

# POST /=/model/book my $response = $ua->request(POST HOST.'/=/model/book', 'Content-Type' => 'text/yaml', Content => $book_data, );

# On success, return the new ID assigned to the resource if ($response->is_success) { my $url = $response->header('Location'); my ($id) = $url =~ /([\d-]+)$/; print "$id: $url\n"; }

# On failure, barf else { barf $response; }};

Create

Page 93: Making Your Perl REST

# book create <filename> - submits the book file in <filename> to the serversubcommand 'create' => sub { my $file = shift @ARGV;

# Slurp up the contents of the given filename my $book_data = slurp $file;

# POST /=/model/book my $response = $ua->request(POST HOST.'/=/model/book', 'Content-Type' => 'text/yaml', Content => $book_data, );

# On success, return the new ID assigned to the resource if ($response->is_success) { my $url = $response->header('Location'); my ($id) = $url =~ /([\d-]+)$/; print "$id: $url\n"; }

# On failure, barf else { barf $response; }};

Create

Page 94: Making Your Perl REST

# book create <filename> - submits the book file in <filename> to the serversubcommand 'create' => sub { my $file = shift @ARGV;

# Slurp up the contents of the given filename my $book_data = slurp $file;

# POST /=/model/book my $response = $ua->request(POST HOST.'/=/model/book', 'Content-Type' => 'text/yaml', Content => $book_data, );

# On success, return the new ID assigned to the resource if ($response->is_success) { my $url = $response->header('Location'); my ($id) = $url =~ /([\d-]+)$/; print "$id: $url\n"; }

# On failure, barf else { barf $response; }};

Create

Page 95: Making Your Perl REST

# book create <filename> - submits the book file in <filename> to the serversubcommand 'create' => sub { my $file = shift @ARGV;

# Slurp up the contents of the given filename my $book_data = slurp $file;

# POST /=/model/book my $response = $ua->request(POST HOST.'/=/model/book', 'Content-Type' => 'text/yaml', Content => $book_data, );

# On success, return the new ID assigned to the resource if ($response->is_success) { my $url = $response->header('Location'); my ($id) = $url =~ /([\d-]+)$/; print "$id: $url\n"; }

# On failure, barf else { barf $response; }};

Create

Page 96: Making Your Perl REST

# book create <filename> - submits the book file in <filename> to the serversubcommand 'create' => sub { my $file = shift @ARGV;

# Slurp up the contents of the given filename my $book_data = slurp $file;

# POST /=/model/book my $response = $ua->request(POST HOST.'/=/model/book', 'Content-Type' => 'text/yaml', Content => $book_data, );

# On success, return the new ID assigned to the resource if ($response->is_success) { my $url = $response->header('Location'); my ($id) = $url =~ /([\d-]+)$/; print "$id: $url\n"; }

# On failure, barf else { barf $response; }};

Create

Page 97: Making Your Perl REST

# book create <filename> - submits the book file in <filename> to the serversubcommand 'create' => sub { my $file = shift @ARGV;

# Slurp up the contents of the given filename my $book_data = slurp $file;

# POST /=/model/book my $response = $ua->request(POST HOST.'/=/model/book', 'Content-Type' => 'text/yaml', Content => $book_data, );

# On success, return the new ID assigned to the resource if ($response->is_success) { my $url = $response->header('Location'); my ($id) = $url =~ /([\d-]+)$/; print "$id: $url\n"; }

# On failure, barf else { barf $response; }};

Create

Page 98: Making Your Perl REST

# Handle the creation of new booksPOST qr{^/=/model/book$} => sub {

# Check to make sure the input book is sane my $book= check_book( $q->param('POSTDATA') );

# If we have an ISBN (some books don't!), then die if we already have # it because we don't permit POST cannot for updates! if ($book->{isbn} and -f get_local_path($book->{isbn})) { barf 500, 'Not Gonna Do It', 'A POST may not be used to update an existing book.'; }

# Our data is sane!

# ...

Create (part 1)

Page 99: Making Your Perl REST

# Handle the creation of new booksPOST qr{^/=/model/book$} => sub {

# Check to make sure the input book is sane my $book= check_book( $q->param('POSTDATA') );

# If we have an ISBN (some books don't!), then die if we already have # it because we don't permit POST cannot for updates! if ($book->{isbn} and -f get_local_path($book->{isbn})) { barf 500, 'Not Gonna Do It', 'A POST may not be used to update an existing book.'; }

# Our data is sane!

# ...

Create (part 1)

Page 100: Making Your Perl REST

# Handle the creation of new booksPOST qr{^/=/model/book$} => sub {

# Check to make sure the input book is sane my $book= check_book( $q->param('POSTDATA') );

# If we have an ISBN (some books don't!), then die if we already have # it because we don't permit POST cannot for updates! if ($book->{isbn} and -f get_local_path($book->{isbn})) { barf 500, 'Not Gonna Do It', 'A POST may not be used to update an existing book.'; }

# Our data is sane!

# ...

Create (part 1)

Page 101: Making Your Perl REST

# Handle the creation of new booksPOST qr{^/=/model/book$} => sub {

# Check to make sure the input book is sane my $book= check_book( $q->param('POSTDATA') );

# If we have an ISBN (some books don't!), then die if we already have # it because we don't permit POST cannot for updates! if ($book->{isbn} and -f get_local_path($book->{isbn})) { barf 500, 'Not Gonna Do It', 'A POST may not be used to update an existing book.'; }

# Our data is sane!

# ...

Create (part 1)

Page 102: Making Your Perl REST

# Handle the creation of new booksPOST qr{^/=/model/book$} => sub {

# Check to make sure the input book is sane my $book= check_book( $q->param('POSTDATA') );

# If we have an ISBN (some books don't!), then die if we already have # it because we don't permit POST cannot for updates! if ($book->{isbn} and -f get_local_path($book->{isbn})) { barf 500, 'Not Gonna Do It', 'A POST may not be used to update an existing book.'; }

# Our data is sane!

# ...

Create (part 1)

Page 103: Making Your Perl REST

# Handle the creation of new booksPOST qr{^/=/model/book$} => sub {

# Check to make sure the input book is sane my $book= check_book( $q->param('POSTDATA') );

# If we have an ISBN (some books don't!), then die if we already have # it because we don't permit POST cannot for updates! if ($book->{isbn} and -f get_local_path($book->{isbn})) { barf 500, 'Not Gonna Do It', 'A POST may not be used to update an existing book.'; }

# Our data is sane!

# ...

Create (part 1)

Page 104: Making Your Perl REST

# ...

# Figure out an ID, this is either the ISBN or a generated ID my $id = $book->{isbn} ? $book->{isbn} : next_id;

# Store the ID for reference within the record $book->{id} = $id;

# Save the resource eval { YAML::DumpFile(get_local_path($id), $book) }; barf 500, 'I Am Broke', $@ if $@;

# Note the success to the end-user my $resource_url = absolute_url('/=/model/book/id/'.$id); print $q->header( -status => 201, -type => 'text/html', -location => $resource_url, ); print $q->h1("Created $book->{title}"); print $q->ul( $q->li( $q->a({ href => $resource_url }, $resource_url) ) );};

Create (part 2)

Page 105: Making Your Perl REST

# ...

# Figure out an ID, this is either the ISBN or a generated ID my $id = $book->{isbn} ? $book->{isbn} : next_id;

# Store the ID for reference within the record $book->{id} = $id;

# Save the resource eval { YAML::DumpFile(get_local_path($id), $book) }; barf 500, 'I Am Broke', $@ if $@;

# Note the success to the end-user my $resource_url = absolute_url('/=/model/book/id/'.$id); print $q->header( -status => 201, -type => 'text/html', -location => $resource_url, ); print $q->h1("Created $book->{title}"); print $q->ul( $q->li( $q->a({ href => $resource_url }, $resource_url) ) );};

Create (part 2)

Page 106: Making Your Perl REST

# ...

# Figure out an ID, this is either the ISBN or a generated ID my $id = $book->{isbn} ? $book->{isbn} : next_id;

# Store the ID for reference within the record $book->{id} = $id;

# Save the resource eval { YAML::DumpFile(get_local_path($id), $book) }; barf 500, 'I Am Broke', $@ if $@;

# Note the success to the end-user my $resource_url = absolute_url('/=/model/book/id/'.$id); print $q->header( -status => 201, -type => 'text/html', -location => $resource_url, ); print $q->h1("Created $book->{title}"); print $q->ul( $q->li( $q->a({ href => $resource_url }, $resource_url) ) );};

Create (part 2)

Page 107: Making Your Perl REST

# ...

# Figure out an ID, this is either the ISBN or a generated ID my $id = $book->{isbn} ? $book->{isbn} : next_id;

# Store the ID for reference within the record $book->{id} = $id;

# Save the resource eval { YAML::DumpFile(get_local_path($id), $book) }; barf 500, 'I Am Broke', $@ if $@;

# Note the success to the end-user my $resource_url = absolute_url('/=/model/book/id/'.$id); print $q->header( -status => 201, -type => 'text/html', -location => $resource_url, ); print $q->h1("Created $book->{title}"); print $q->ul( $q->li( $q->a({ href => $resource_url }, $resource_url) ) );};

Create (part 2)

Page 108: Making Your Perl REST

# ...

# Figure out an ID, this is either the ISBN or a generated ID my $id = $book->{isbn} ? $book->{isbn} : next_id;

# Store the ID for reference within the record $book->{id} = $id;

# Save the resource eval { YAML::DumpFile(get_local_path($id), $book) }; barf 500, 'I Am Broke', $@ if $@;

# Note the success to the end-user my $resource_url = absolute_url('/=/model/book/id/'.$id); print $q->header( -status => 201, -type => 'text/html', -location => $resource_url, ); print $q->h1("Created $book->{title}"); print $q->ul( $q->li( $q->a({ href => $resource_url }, $resource_url) ) );};

Create (part 2)

Page 109: Making Your Perl REST

# book update <id> <filename> - updates the book file <id> using <filename>subcommand 'update' => sub { my $id = shift @ARGV; my $file = shift @ARGV;

# Slurp up the given file name my $book_data = slurp $file;

# PUT /=/model/book/id/[id] my $response = $ua->request(PUT HOST.'/=/model/book/id/'.$id, 'Content-Type' => 'text/yaml', Content => $book_data, );

# On success, just announce success if ($response->is_success) { print "Updated $id\n"; }

# On failure, barf else { barf $response; }};

Update

Page 110: Making Your Perl REST

# book update <id> <filename> - updates the book file <id> using <filename>subcommand 'update' => sub { my $id = shift @ARGV; my $file = shift @ARGV;

# Slurp up the given file name my $book_data = slurp $file;

# PUT /=/model/book/id/[id] my $response = $ua->request(PUT HOST.'/=/model/book/id/'.$id, 'Content-Type' => 'text/yaml', Content => $book_data, );

# On success, just announce success if ($response->is_success) { print "Updated $id\n"; }

# On failure, barf else { barf $response; }};

Update

Page 111: Making Your Perl REST

# book update <id> <filename> - updates the book file <id> using <filename>subcommand 'update' => sub { my $id = shift @ARGV; my $file = shift @ARGV;

# Slurp up the given file name my $book_data = slurp $file;

# PUT /=/model/book/id/[id] my $response = $ua->request(PUT HOST.'/=/model/book/id/'.$id, 'Content-Type' => 'text/yaml', Content => $book_data, );

# On success, just announce success if ($response->is_success) { print "Updated $id\n"; }

# On failure, barf else { barf $response; }};

Update

Page 112: Making Your Perl REST

# book update <id> <filename> - updates the book file <id> using <filename>subcommand 'update' => sub { my $id = shift @ARGV; my $file = shift @ARGV;

# Slurp up the given file name my $book_data = slurp $file;

# PUT /=/model/book/id/[id] my $response = $ua->request(PUT HOST.'/=/model/book/id/'.$id, 'Content-Type' => 'text/yaml', Content => $book_data, );

# On success, just announce success if ($response->is_success) { print "Updated $id\n"; }

# On failure, barf else { barf $response; }};

Update

Page 113: Making Your Perl REST

# book update <id> <filename> - updates the book file <id> using <filename>subcommand 'update' => sub { my $id = shift @ARGV; my $file = shift @ARGV;

# Slurp up the given file name my $book_data = slurp $file;

# PUT /=/model/book/id/[id] my $response = $ua->request(PUT HOST.'/=/model/book/id/'.$id, 'Content-Type' => 'text/yaml', Content => $book_data, );

# On success, just announce success if ($response->is_success) { print "Updated $id\n"; }

# On failure, barf else { barf $response; }};

Update

Page 114: Making Your Perl REST

# book update <id> <filename> - updates the book file <id> using <filename>subcommand 'update' => sub { my $id = shift @ARGV; my $file = shift @ARGV;

# Slurp up the given file name my $book_data = slurp $file;

# PUT /=/model/book/id/[id] my $response = $ua->request(PUT HOST.'/=/model/book/id/'.$id, 'Content-Type' => 'text/yaml', Content => $book_data, );

# On success, just announce success if ($response->is_success) { print "Updated $id\n"; }

# On failure, barf else { barf $response; }};

Update

Page 115: Making Your Perl REST

# Handle updates to booksPUT qr{^/=/model/book/id/([\d-]+)$} => sub { my $id= $1;

# Check to make sure the input book is sane my $book = check_book( $q->param('PUTDATA') );

# Make sure the book already exists or barf my $resource_path= get_local_path($id); unless(-f $resource_path) { barf 500, 'Not Gonna Do It', 'Cannot use PUTs for creating a new resource.'; }

# Make sure the ID is set $book->{id} = $id;

# Save the resource eval { YAML::DumpFile($resource_path, $book) }; barf 500, 'I Am Broke', $@ if $@;

# Note the success to the end-user print $q->header('text/html'); print $q->h1("Updated $book->{title}");};

Update

Page 116: Making Your Perl REST

# Handle updates to booksPUT qr{^/=/model/book/id/([\d-]+)$} => sub { my $id= $1;

# Check to make sure the input book is sane my $book = check_book( $q->param('PUTDATA') );

# Make sure the book already exists or barf my $resource_path= get_local_path($id); unless(-f $resource_path) { barf 500, 'Not Gonna Do It', 'Cannot use PUTs for creating a new resource.'; }

# Make sure the ID is set $book->{id} = $id;

# Save the resource eval { YAML::DumpFile($resource_path, $book) }; barf 500, 'I Am Broke', $@ if $@;

# Note the success to the end-user print $q->header('text/html'); print $q->h1("Updated $book->{title}");};

Update

Page 117: Making Your Perl REST

# Handle updates to booksPUT qr{^/=/model/book/id/([\d-]+)$} => sub { my $id= $1;

# Check to make sure the input book is sane my $book = check_book( $q->param('PUTDATA') );

# Make sure the book already exists or barf my $resource_path= get_local_path($id); unless(-f $resource_path) { barf 500, 'Not Gonna Do It', 'Cannot use PUTs for creating a new resource.'; }

# Make sure the ID is set $book->{id} = $id;

# Save the resource eval { YAML::DumpFile($resource_path, $book) }; barf 500, 'I Am Broke', $@ if $@;

# Note the success to the end-user print $q->header('text/html'); print $q->h1("Updated $book->{title}");};

Update

Page 118: Making Your Perl REST

# Handle updates to booksPUT qr{^/=/model/book/id/([\d-]+)$} => sub { my $id= $1;

# Check to make sure the input book is sane my $book = check_book( $q->param('PUTDATA') );

# Make sure the book already exists or barf my $resource_path= get_local_path($id); unless(-f $resource_path) { barf 500, 'Not Gonna Do It', 'Cannot use PUTs for creating a new resource.'; }

# Make sure the ID is set $book->{id} = $id;

# Save the resource eval { YAML::DumpFile($resource_path, $book) }; barf 500, 'I Am Broke', $@ if $@;

# Note the success to the end-user print $q->header('text/html'); print $q->h1("Updated $book->{title}");};

Update

Page 119: Making Your Perl REST

# Handle updates to booksPUT qr{^/=/model/book/id/([\d-]+)$} => sub { my $id= $1;

# Check to make sure the input book is sane my $book = check_book( $q->param('PUTDATA') );

# Make sure the book already exists or barf my $resource_path= get_local_path($id); unless(-f $resource_path) { barf 500, 'Not Gonna Do It', 'Cannot use PUTs for creating a new resource.'; }

# Make sure the ID is set $book->{id} = $id;

# Save the resource eval { YAML::DumpFile($resource_path, $book) }; barf 500, 'I Am Broke', $@ if $@;

# Note the success to the end-user print $q->header('text/html'); print $q->h1("Updated $book->{title}");};

Update

Page 120: Making Your Perl REST

# Handle updates to booksPUT qr{^/=/model/book/id/([\d-]+)$} => sub { my $id= $1;

# Check to make sure the input book is sane my $book = check_book( $q->param('PUTDATA') );

# Make sure the book already exists or barf my $resource_path= get_local_path($id); unless(-f $resource_path) { barf 500, 'Not Gonna Do It', 'Cannot use PUTs for creating a new resource.'; }

# Make sure the ID is set $book->{id} = $id;

# Save the resource eval { YAML::DumpFile($resource_path, $book) }; barf 500, 'I Am Broke', $@ if $@;

# Note the success to the end-user print $q->header('text/html'); print $q->h1("Updated $book->{title}");};

Update

Page 121: Making Your Perl REST

# book delete <id> - deletes the book resource with ID <id>subcommand 'delete' => sub { my $id = shift @ARGV;

# DELETE /=/model/book/id/[id] my $response = $ua->request( HTTP::Request->new( DELETE => HOST.'/=/model/book/id/'.$id) );

# On success, announce it if ($response->is_success) { print "Deleted $id\n"; }

# On failure, barf else { barf $response; }};

Delete

Page 122: Making Your Perl REST

# book delete <id> - deletes the book resource with ID <id>subcommand 'delete' => sub { my $id = shift @ARGV;

# DELETE /=/model/book/id/[id] my $response = $ua->request( HTTP::Request->new( DELETE => HOST.'/=/model/book/id/'.$id) );

# On success, announce it if ($response->is_success) { print "Deleted $id\n"; }

# On failure, barf else { barf $response; }};

Delete

Page 123: Making Your Perl REST

# book delete <id> - deletes the book resource with ID <id>subcommand 'delete' => sub { my $id = shift @ARGV;

# DELETE /=/model/book/id/[id] my $response = $ua->request( HTTP::Request->new( DELETE => HOST.'/=/model/book/id/'.$id) );

# On success, announce it if ($response->is_success) { print "Deleted $id\n"; }

# On failure, barf else { barf $response; }};

Delete

Page 124: Making Your Perl REST

# book delete <id> - deletes the book resource with ID <id>subcommand 'delete' => sub { my $id = shift @ARGV;

# DELETE /=/model/book/id/[id] my $response = $ua->request( HTTP::Request->new( DELETE => HOST.'/=/model/book/id/'.$id) );

# On success, announce it if ($response->is_success) { print "Deleted $id\n"; }

# On failure, barf else { barf $response; }};

Delete

Page 125: Making Your Perl REST

# book delete <id> - deletes the book resource with ID <id>subcommand 'delete' => sub { my $id = shift @ARGV;

# DELETE /=/model/book/id/[id] my $response = $ua->request( HTTP::Request->new( DELETE => HOST.'/=/model/book/id/'.$id) );

# On success, announce it if ($response->is_success) { print "Deleted $id\n"; }

# On failure, barf else { barf $response; }};

Delete

Page 126: Making Your Perl REST

DELETE qr{^/=/model/book/id/([\d-]+)$} => sub { my $id= $1;

# Make sure the book actually exists my $resource_path = get_local_path($id); unless (-f $resource_path) { barf 404, 'Where is What?', 'Nothing here to delete.'; }

# Baleted! unlink $resource_path;

# Tell me about it. print $q->header('text/html'); print $q->h1("Deleted $id");};

Delete

Page 127: Making Your Perl REST

DELETE qr{^/=/model/book/id/([\d-]+)$} => sub { my $id= $1;

# Make sure the book actually exists my $resource_path = get_local_path($id); unless (-f $resource_path) { barf 404, 'Where is What?', 'Nothing here to delete.'; }

# Baleted! unlink $resource_path;

# Tell me about it. print $q->header('text/html'); print $q->h1("Deleted $id");};

Delete

Page 128: Making Your Perl REST

DELETE qr{^/=/model/book/id/([\d-]+)$} => sub { my $id= $1;

# Make sure the book actually exists my $resource_path = get_local_path($id); unless (-f $resource_path) { barf 404, 'Where is What?', 'Nothing here to delete.'; }

# Baleted! unlink $resource_path;

# Tell me about it. print $q->header('text/html'); print $q->h1("Deleted $id");};

Delete

Page 129: Making Your Perl REST

DELETE qr{^/=/model/book/id/([\d-]+)$} => sub { my $id= $1;

# Make sure the book actually exists my $resource_path = get_local_path($id); unless (-f $resource_path) { barf 404, 'Where is What?', 'Nothing here to delete.'; }

# Baleted! unlink $resource_path;

# Tell me about it. print $q->header('text/html'); print $q->h1("Deleted $id");};

Delete

Page 130: Making Your Perl REST

DELETE qr{^/=/model/book/id/([\d-]+)$} => sub { my $id= $1;

# Make sure the book actually exists my $resource_path = get_local_path($id); unless (-f $resource_path) { barf 404, 'Where is What?', 'Nothing here to delete.'; }

# Baleted! unlink $resource_path;

# Tell me about it. print $q->header('text/html'); print $q->h1("Deleted $id");};

Delete

Page 131: Making Your Perl REST

DELETE qr{^/=/model/book/id/([\d-]+)$} => sub { my $id= $1;

# Make sure the book actually exists my $resource_path = get_local_path($id); unless (-f $resource_path) { barf 404, 'Where is What?', 'Nothing here to delete.'; }

# Baleted! unlink $resource_path;

# Tell me about it. print $q->header('text/html'); print $q->h1("Deleted $id");};

Delete

Page 132: Making Your Perl REST

# Provide some nice documentationGET qr{^/=$} => sub { print $q->header('text/html');

print $q->h1('REST API Documentation');

print $q->p('Here is a list of what you can do:');

print $q->dl( $q->dt('GET /=/model/book/id'), $q->dd('Returns a list of available book IDs.'),

$q->dt('GET /=/model/book/id/[ID]'), $q->dd('ID may be a number or the ISBN. Returns the book.'),

$q->dt('POST /=/model/book'), $q->dd('Create a new book record. Returns the new URL to fetch with.'),

$q->dt('PUT /=/model/book/id/[ID]'), $q->dd('Update a book by posting a complete book file.'),

$q->dt('DELETE /=/model/book/id/[ID]'), $q->dd('Delete a book.'), );

print $q->p('All book resources are stored or fetched in YAML format. The list of books will be fetched in HTML with each LI in the returned listing containing a link to a book resource.');

print $q->p('Here is a sample book. The "title" field is the only required field for books. The "isbn" field should be equal to the "id" field, if the "isbn" is present. The "id" field should be the [ID] used to fetch, updated, or delete the record.');

print $q->pre(q{isbn: 0-7852-1155-1title: "The New Strong's Exhaustive Concordance of the Bible"author: James Strong, LL.D., S.T.D.publisher: Thomas Nelson Publisherscity: Nashville, Tennesseeyear: 1995});};

Built-in Documentation

Page 133: Making Your Perl REST

• Use whatever content types are most appropriate to your audience: XML, YAML, JSON, HTML, RSS/Atom, SQL, CSV, vFiles, PDF

• Don’t be afraid to offer multiple formats using the Accept: headers or even file name suffixes

• Use the full range of HTTP response codes to give clear responses

• Include additional X-blah: headers for metadata

Exercises for the Audience

Page 134: Making Your Perl REST

Recommended Resources

• Sample Code:http://contentment.org/files/onlamp/library.cgihttp://contentment.org/files/onlamp/book

• Original Articles:http://www.onlamp.com/pub/a/onlamp/2008/02/19/developing-restful-web-services-in-perl.htmlhttp://contentment.org/2008/08/developing-restful-web-service.html

• OpenResty - Nice REST middleware server by Agent Zhang:http://search.cpan.org/dist/OpenResty/

• Jifty - I ripped off the style of the REST interface of Jifty for this demo:http://search.cpan.org/dist/Jifty/

• HTTP Specification: http://www.w3.org/Protocols/rfc2616/rfc2616.html

• REST Wiki: http://rest.blueoxen.net/

Page 135: Making Your Perl REST
Page 136: Making Your Perl REST

Thank you!