Loading...
Flash Player 9 (or above) is needed to view slideshows. We have detected that you do not have it on your computer.To install it, go here
-
mrhsuslide favorited this 1 week ago -
Slideshow Transcript
- Slide 1: Making Your Perl REST Sterling Hanenkamp, PPW 2008
- Slide 2: What is REST?
- Slide 3: What is REST? • Ever heard of...
- Slide 4: What is REST? • Ever heard of... • SOAP?
- Slide 5: What is REST? • Ever heard of... • SOAP? • RPC?
- Slide 6: What is REST? • Ever heard of... • SOAP? • RPC? • XML-RPC?
- Slide 7: What is REST? • Ever heard of... • SOAP? • RPC? • XML-RPC? • WebDAV?
- Slide 8: What is REST? • Ever heard of... • SOAP? • RPC? • XML-RPC? • WebDAV? • It’s kind of like that.
- Slide 9: What is REST? • Ever heard of... • Ever heard of... • SOAP? • RPC? • XML-RPC? • WebDAV? • It’s kind of like that.
- Slide 10: What is REST? • Ever heard of... • Ever heard of... • SOAP? • HTTP? • RPC? • XML-RPC? • WebDAV? • It’s kind of like that.
- Slide 11: What is REST? • Ever heard of... • Ever heard of... • SOAP? • HTTP? • RPC? • XML? • XML-RPC? • WebDAV? • It’s kind of like that.
- Slide 12: What is REST? • Ever heard of... • Ever heard of... • SOAP? • HTTP? • RPC? • XML? • XML-RPC? • YAML? • WebDAV? • It’s kind of like that.
- Slide 13: What is REST? • Ever heard of... • Ever heard of... • SOAP? • HTTP? • RPC? • XML? • XML-RPC? • YAML? • WebDAV? • JSON? • It’s kind of like that.
- Slide 14: What is REST? • Ever heard of... • Ever heard of... • SOAP? • HTTP? • RPC? • XML? • XML-RPC? • YAML? • WebDAV? • JSON? • It’s kind of like that. • It’s kind of like that too
- Slide 15: What are we NOT talking about?
- Slide 16: What are we NOT talking about? • REST is a general purpose term
- Slide 17: What are we NOT talking about? • REST is a general purpose term • I’m not talking about a general notion
- Slide 18: What are we NOT talking about? • REST is a general purpose term • I’m not talking about a general notion • We’re talking about REST APIs
- Slide 19: What are we NOT talking about? • REST is a general purpose term • I’m not talking about a general notion • We’re talking about REST APIs • Web Services
- Slide 20: What are we NOT talking about? • 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!!!
- Slide 21: What are we NOT talking about? • 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!!!
- Slide 22: What are we NOT talking about? • 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...
- Slide 23: The REST Triangle
- Slide 24: The REST Triangle no un ht s tp :// ww w. on lam p.c om content types YAML, XML, JSON, etc. T E LE DE U T, b s ST, P er PO v , G ET
- Slide 25: The REST Triangle no • Nouns (=URL) un ht s tp :// ww w. on lam p.c om content types YAML, XML, JSON, etc. T E LE DE U T, b s ST, P er PO v , G ET
- Slide 26: The REST Triangle no • Nouns (=URL) un ht s tp :// ww • Verbs (=HTTP METHOD) w. on lam p.c om content types YAML, XML, JSON, etc. T E LE DE U T, b s ST, P er PO v , G ET
- Slide 27: The REST Triangle no • Nouns (=URL) un ht s tp :// ww • Verbs (=HTTP METHOD) w. on lam p.c om • Content (=MIME Type) content types YAML, XML, JSON, etc. T E LE DE U T, b s ST, P er PO v , G ET
- Slide 28: REST Web Services
- Slide 29: REST Web Services CRUD
- Slide 30: REST Web Services CRUD Create
- Slide 31: REST Web Services CRUD Create Read
- Slide 32: REST Web Services CRUD Create Read Update
- Slide 33: REST Web Services CRUD Create Read Update Delete
- Slide 34: REST Web Services • Treat your data like a static web page CRUD Create Read Update Delete
- Slide 35: REST Web Services • Treat your data like a static web page CRUD • Add data by POSTing a web page Create Read Update Delete
- Slide 36: REST Web Services • Treat your data like a static web page CRUD • Add data by POSTing a web page Create • Fetch data by GETting a web page Read Update Delete
- Slide 37: REST Web Services • Treat your data like a static web page CRUD • Add data by POSTing a web page Create • Fetch data by GETting a web page Read • Update data by PUTing a web page Update Delete
- Slide 38: REST Web Services • Treat your data like a static web page CRUD • Add data by POSTing a web page Create • Fetch data by GETting a web page Read • Update data by PUTing a web page Update • Delete data by DELETing a web page Delete
- Slide 39: REST Web Services • Treat your data like a static web page CRUD • Add data by POSTing a web page Create • Fetch data by GETting a web page Read • Update data by PUTing a web page Update • Delete data by DELETing a web page • E... it’s missing and it bugs me... Delete
- Slide 40: Who Uses It
- Slide 41: Who Uses It • Google • Socialtext • Yahoo! • Digg • Amazon • Twitter • Intuit • eBay • Best Practical • Technorati • Facebook • Too many others to mention...
- Slide 42: What about other stuff?
- Slide 43: What about other stuff? • RPC is a square peg
- Slide 44: What about other stuff? • RPC is a square peg • REST is round hole
- Slide 45: What about other stuff? • RPC is a square peg • REST is round hole
- Slide 46: What about other stuff? • RPC is a square peg • REST is round hole • But it works!
- Slide 47: What about other stuff? • RPC is a square peg • REST is round hole • But it works! • Getting Info? GET
- Slide 48: What about other stuff? • RPC is a square peg • REST is round hole • But it works! • Getting Info? GET • Modifying Something? POST
- Slide 49: What about other stuff? • RPC is a square peg • REST is round hole • But it works! • Getting Info? GET • Modifying Something? POST • Both? Not sure? POST
- Slide 50: ENOUGH BASICS. LET’S DO SOMETHING.
- Slide 51: # Manage your books from the command-line. Woo-hoo! % ./book list
- Slide 52: # Manage your books from the command-line. Woo-hoo! % ./book list 0-8024-8160-4: .../library.cgi/=/model/book/id/0-8024-8160-4 0-85151-760-9: .../library.cgi/=/model/book/id/0-85151-760-9 0-936083-11-5: .../library.cgi/=/model/book/id/0-936083-11-5
- Slide 53: % ./book read 0-8024-8160-4
- Slide 54: % ./book read 0-8024-8160-4 --- author: David Clotfelter city: Chicago id: 0-8024-8160-4 isbn: 0-8024-8160-4 publisher: Moody Publishers title: > Sinners in the Hands of a Good God: Reconciling Divine Judgment and Mercy' year: 2004
- Slide 56: % ./book create reformation.yml
- Slide 57: % ./book create reformation.yml 0-87552-183-5: .../library.cgi/=/model/book/id/0-87552-183-5
- Slide 58: % ./book create reformation.yml 0-87552-183-5: .../library.cgi/=/model/book/id/0-87552-183-5 % ./book update 0-87552-183-5 reformation2.yml
- Slide 59: % ./book create reformation.yml 0-87552-183-5: .../library.cgi/=/model/book/id/0-87552-183-5 % ./book update 0-87552-183-5 reformation2.yml Updated 0-87552-183-5
- Slide 60: % ./book create reformation.yml 0-87552-183-5: .../library.cgi/=/model/book/id/0-87552-183-5 % ./book update 0-87552-183-5 reformation2.yml Updated 0-87552-183-5 % ./book delete 0-87552-183-5
- Slide 61: % ./book create reformation.yml 0-87552-183-5: .../library.cgi/=/model/book/id/0-87552-183-5 % ./book update 0-87552-183-5 reformation2.yml Updated 0-87552-183-5 % ./book delete 0-87552-183-5 Deleted 0-87552-183-5
- Slide 62: Okay... Putting it together But what did that do?
- Slide 63: Explaining the Nouns (a.k.a. URLs)
- Slide 64: Explaining the Nouns (a.k.a. URLs) Scheme Borrowed from Jifty
- Slide 65: Explaining the Nouns (a.k.a. URLs) Scheme Borrowed from Jifty 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
- Slide 66: Explaining the Nouns (a.k.a. URLs) Scheme Borrowed from Jifty 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
- Slide 67: Explaining the Nouns (a.k.a. URLs) Scheme Borrowed from Jifty 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
- Slide 68: Explaining the Nouns (a.k.a. URLs) Scheme Borrowed from Jifty 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
- Slide 69: Explaining the Nouns (a.k.a. URLs) Scheme Borrowed from Jifty 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
- Slide 70: Explaining the Nouns (a.k.a. URLs) Scheme Borrowed from Jifty 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
- Slide 71: Read (List) # book list - lists all the books the server returns subcommand '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; } };
- Slide 72: Read (List) # book list - lists all the books the server returns subcommand '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; } };
- Slide 73: Read (List) # book list - lists all the books the server returns subcommand '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; } };
- Slide 74: Read (List) # book list - lists all the books the server returns subcommand '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; } };
- Slide 75: Read (List) # book list - lists all the books the server returns subcommand '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; } };
- Slide 76: Read (List) # Get a whole list of available documents GET 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); };
- Slide 77: Read (List) # Get a whole list of available documents GET 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); };
- Slide 78: Read (List) # Get a whole list of available documents GET 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); };
- Slide 79: Read (List) # Get a whole list of available documents GET 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); };
- Slide 80: Read (List) # Get a whole list of available documents GET 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); };
- Slide 81: Read (Single) # 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; } };
- Slide 82: Read (Single) # 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; } };
- Slide 83: Read (Single) # 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; } };
- Slide 84: Read (Single) # 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; } };
- Slide 85: Read (Single) # 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; } };
- Slide 86: Read (Single) # Look up and read a resource GET 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.\"; } };
- Slide 87: Read (Single) # Look up and read a resource GET 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.\"; } };
- Slide 88: Read (Single) # Look up and read a resource GET 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.\"; } };
- Slide 89: Read (Single) # Look up and read a resource GET 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.\"; } };
- Slide 90: Read (Single) # Look up and read a resource GET 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.\"; } };
- Slide 91: Read (Single) # Look up and read a resource GET 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.\"; } };
- Slide 92: Create # book create <filename> - submits the book file in <filename> to the server subcommand '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; } };
- Slide 93: Create # book create <filename> - submits the book file in <filename> to the server subcommand '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; } };
- Slide 94: Create # book create <filename> - submits the book file in <filename> to the server subcommand '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; } };
- Slide 95: Create # book create <filename> - submits the book file in <filename> to the server subcommand '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; } };
- Slide 96: Create # book create <filename> - submits the book file in <filename> to the server subcommand '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; } };
- Slide 97: Create # book create <filename> - submits the book file in <filename> to the server subcommand '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; } };
- Slide 98: Create (part 1) # Handle the creation of new books POST 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! # ...
- Slide 99: Create (part 1) # Handle the creation of new books POST 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! # ...
- Slide 100: Create (part 1) # Handle the creation of new books POST 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! # ...
- Slide 101: Create (part 1) # Handle the creation of new books POST 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! # ...
- Slide 102: Create (part 1) # Handle the creation of new books POST 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! # ...
- Slide 103: Create (part 1) # Handle the creation of new books POST 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! # ...
- Slide 104: Create (part 2) # ... # 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) ) ); };
- Slide 105: Create (part 2) # ... # 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) ) ); };
- Slide 106: Create (part 2) # ... # 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) ) ); };
- Slide 107: Create (part 2) # ... # 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) ) ); };
- Slide 108: Create (part 2) # ... # 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) ) ); };
- Slide 109: Update # 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; } };
- Slide 110: Update # 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; } };
- Slide 111: Update # 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; } };
- Slide 112: Update # 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; } };
- Slide 113: Update # 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; } };
- Slide 114: Update # 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; } };
- Slide 115: Update # Handle updates to books PUT 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}\"); };
- Slide 116: Update # Handle updates to books PUT 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}\"); };
- Slide 117: Update # Handle updates to books PUT 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}\"); };
- Slide 118: Update # Handle updates to books PUT 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}\"); };
- Slide 119: Update # Handle updates to books PUT 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}\"); };
- Slide 120: Update # Handle updates to books PUT 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}\"); };
- Slide 121: Delete # 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; } };
- Slide 122: Delete # 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; } };
- Slide 123: Delete # 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; } };
- Slide 124: Delete # 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; } };
- Slide 125: Delete # 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; } };
- Slide 126: Delete 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\"); };
- Slide 127: Delete 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\"); };
- Slide 128: Delete 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\"); };
- Slide 129: Delete 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\"); };
- Slide 130: Delete 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\"); };
- Slide 131: Delete 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\"); };
- Slide 132: Built-in Documentation # Provide some nice documentation GET 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-1 title: \"The New Strong's Exhaustive Concordance of the Bible\" author: James Strong, LL.D., S.T.D. publisher: Thomas Nelson Publishers city: Nashville, Tennessee year: 1995}); };
- Slide 133: Exercises for the Audience • 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
- Slide 134: Recommended Resources • Sample Code: http://contentment.org/files/onlamp/library.cgi http://contentment.org/files/onlamp/book • Original Articles: http://www.onlamp.com/pub/a/onlamp/2008/02/19/developing-restful-web- services-in-perl.html http://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/
- Slide 136: Thank you!

