RESTful Web Services by Leonard Richardson, Sam Ruby The unconfirmed error reports are from readers. They have not yet been approved or disproved by the author or editor and represent solely the opinion of the reader. Here's a key to the markup: [page-number]: serious technical mistake {page-number}: minor technical mistake : important language/formatting problem (page-number): language change or minor formatting problem ?page-number?: reader question or request for clarification This page was updated July 16, 2008. UNCONFIRMED errors and comments from readers: {various} In the Safari version of this book, it seems URLs have replaced the actual text of some links: A few well-known examples of RESTful, resource-oriented web services include: - Services that expose the ( http://www.ietf.org/html.charters/atompub-charter.html) and its variants such as (http://code.google.com/apis/gdata/) - (http://aws.amazon.com/s3) - Most of (http://developer.yahoo.com/) - Most other read-only web services that don't use SOAP - Static web sites - Many web applications, especially read-only ones like search engines http://search.safaribooksonline.com/9780596529260/ded#X2ludGVybmFsX1NlY3Rpb25Db250ZW50P3htbGlkPTk3ODA1OTY1MjkyNjAvSV9zZWN0MTFfdHQ2Mg== (xvii) 2nd paragraph, the last sentance; "HTTP's weakness is its strength, its simplicity its power." maybe read "HTTP's weakness is its strength, its simplicity (is) its power." [2]Example 1-2; The example - Searching for books with a Ruby script - does not work with the code as is because the dependent library from http://www.caliban.org/ruby/ruby-amazon.shtml has an updated version. Since the API has changed as well, in this new version (4?), getting the example to work for a new person to Ruby is hard. I made some approximations but I don't have it 100% right. I would appreciate if the updated code is posted here. (5)3rd paragraph, above title "HTTP:Documents in Envelopes"; The last sentence ... with the term "REST." the right-quotation should be on the left side of the full stop with the term "REST". {6} Begining on 6th paragraph... and whole book; The authors use the term headers instead of header fields when refering to HTTP envelopes. I think they should stick to the RFC2616 terminology which is: a header is a block of header fields. {7} 3rd paragraph:; "This response has 11 headers" SHOULD BE "...10 headers" [16] Example 1-15; The example does not match the text that describes it. (Looks like the wrong HTTP snippet was used for the example, the example matches the text in the preceding paragraph.) I believe the example should be something like: GET photos/tag/penguin HTTP/1.1 Host: www.flickr.com {31} Code; I receive an authentication failure: Authentication fails. :/InstantRails/ruby/lib/ruby/1.8/net/http.rb:567: warning: using default DH parameters. C:/InstantRails/ruby/lib/ruby/1.8/net/http.rb:586:in `connect': certificate verify failed (OpenSSL::SSL::SSLError) from C:/InstantRails/ruby/lib/ruby/1.8/net/http.rb:586:in `connect' (username and password is provided in lowercase without square brackets and with a space in between (no comma)) Code: See http://ruby.about.com/od/tutorials/ss/delicious_tags_4.htm provides xml output warning: peer certificate won't be verified in this SSL session This code also provides post by changing to posts/recent as in the example of page 31. [31] Example 2-4; Related code: response = open('https://api.del.icio.us/v1/posts/recent', :http_basic_authentication => [username, password]) Error shown: c:/ruby/lib/ruby/1.8/net/http.rb:567: warning: using default DH parameters. c:/ruby/lib/ruby/1.8/net/http.rb:586:in `connect': certificate verify failed (Op enSSL::SSL::SSLError) from c:/ruby/lib/ruby/1.8/net/http.rb:586:in `connect' from c:/ruby/lib/ruby/1.8/net/http.rb:553:in `do_start' from c:/ruby/lib/ruby/1.8/net/http.rb:542:in `start' On Windows XP, the ruby code does not work. Testing the api on safari browser and the authentication works. (32) HTTP feature matrix; Your ditto marks are not clear to me. It seems that they must be read horizontally, e.g. Net:HTTP provides Basic support for Auth methods, No support for Caching, and Yes support for Proxies. I have always only used ditto marks vertically - I've never used them horizontally, so your matrices (not just the one on page 32) are confusing [36] C# code example; I've been working in C# lately, so I tried this example first. In a nutshell, https://api.del.icio.us/v1/ posts/recent failed to respond (within 10 seconds), so the example won't work. I would imagine the other examples (in other languages) won't work either, though I haven't tried them. [36] C# code; I wrote earlier to point out that the C# examples weren't working. I then went for a jog, and as I did it sank in with me that the "username" and "password" must be values that I personally have to get from deli.cio.us. This seems pretty obvious now, but it wasn't when I first tried the examples. The book doesn't seem to say loud and clear that I need to register first. Also, IMHO the C# code should have a comment saying that you have to sign up with deli.cio.us before trying to use the examples. {56} You map the Amazon "Access Key ID" to @@public_key and the "Secret Access Key" to @@private key in your Authorized module. [65] 2nd paragraph; The book states that Amazon S3 uses "public-key cryptography" to authenticate requests. This is totally wrong. S3 uses a message authentication code (MAC), which is symmetric key cryptography. That is, it uses a secret shared by both parties: the client (S3 user) and server (Amazon). The clues that S3 is not using public-key crypto are in the text. When mentioning your "private" key is says "(remember, not truly private: Amazon knows it too)". This mistake is riddled throughout chapter 3. It repeatedly calls the Access Key ID a "public key". It is in no way a public key. It is an identifier, equivalent to a user-id or username. For instance, on page 56 in the code for "module Authorized" includes "@@public_key" & "@@private_key". Change "public key" to "key id". Change "private key" to "secret key". Change "public-key cryptography" to "cryptography". {109} Bottom list; The steps "4.) Expose a Subset of a Uniform Interface" and "5.) Design the Representations Accepted from the Client" are not supposed to be introduced until Chapter 6 p.148 (and are not discussed in Chapter 5). As a result later in Chapter 6 p148 the list is identical to p109 even though it is declared that two steps have been added. (119) 3rd paragraph; This is very picky, but... after stating in the preceding paragraph that you would use comma to separate latitude and longitude, you go on to use a semicolon when you say: /v1/Earth/43.9;-103.46/Mount%20Rushmore would be more precise. Page 120 and 121 use commas, but page 123 uses a semi-colon (in the bullet points). Page 127 uses both in the same code sample at the bottom of the page. There are similar confusions up to at least page 139. [125]Example 5-4; The closing tag of the XML example -- "maps" -- doesn't match the opening tag "planets". {128} First image caption; Given the established convention of including the zoom level on the portion of the URI containing the map type, the caption for figure 5-1 on page 127 should be /road.3/Earth/images/37.0,-95.png instead of what is printed: /road/Earth.3/images/37.0,-95.png {128} 3rd paragraph; Given the established convention of including the zoom level on the portion of the URI containing the map type, the URI in paragraph 3 on page 128 should be: http://maps.example.com/road.8/Earth/images/37.0,-95.png instead of what is printed: http://maps.example.com/road/Earth.8/images/37.0,-95.png Also, the zoom level should be 3 instead of 8 since this URI is meant to be referencing the example on the previous page. p.s. - In the body of the last error report I sent, I mentionded page 127 instead of the correct page for the error 128. (129) First image caption; The image caption for Figure 5-2 on page 129 has a typo. The URI should be /road.8/Earth/images/32.37,-86.30.png instead of /road.8/Earth/images/37.0,-95.png since the former is the example it is meant to illustrate as indicated in the text that follows. (129) First complete paragraph, third line; "32.37" -> "37.0" (twice) "86.30" -> "95" (twice) The coordinates given in the text are in Montgomery, AL, while the printed image claims to represent somewhere on the KS/OK border. (The image is not quite what I see on Google maps when I ask for 37N 95W, but at least it's plausible.) (131) example 5-8 xml example; The given xhtml is not well formed. For example the
  • tags are not closed properly. {131}Example 5-8; To confirm and add to an existing report: the XHTML is poorly formed. For example, the opening
    tag is missing for the "maps" unnumbered list. {133} Example 5-9; The "repeat" attribute on the input element ( ' '' -> '' script/plugin install acts_as_taggable may install a version of the acts_as_taggable plugin that is broken. Use script/plugin install http://svn.rubyonrails.org/rails/plugins/legacy/acts_as_taggable/ instead to obtain a version that works with Rails 1.2.6. It seems that the acts_as_taggable plugin has been deprecated. [170] Example 7-1, "create_table" commands for users and bookmarks; The table definitions for "users" and "bookmarks" are incorrect. The column "user_id" should be in the "users" table, not the "bookmarks" table. Also, in order for the join table "user_bookmarks" to work, the "bookmarks" table should have a primary key column called ""bookmark_id". (172) mid-page; "Finally, the __bundles__ API, which lets the authenticated user group similar tags together." "bundles/" would be more consistent with the context (172)__/recent__?Fetch the most recently posted bookmarks, from all users. This is about text font. It should be in italic to be consistent with the context (174)The User Controller It seems that the last sentence below "The User Controller" did not finish yet, as it was ended with a colon. This should be "a path of least resistance that makes a certain kind of design very easy." (179)under "The Leftovers"; "There's already a solution for this built into HTTP: conditional GET. I cover it briefly in __"Conditional HTTP GET"__ later in this chapter and I'll cover it in more detail in __"Conditional GET,"__ but in this chapter you'll see it implemented. By implementing conditional GET, I can give the time- and bandwidth-saving benefits of posts/update to most of of the resources I'm exposing, not just the single most expensive one." For the first, it should be "Conditional GET" actually. For the second, it should be "Conditional GET in Chapter 8" {182} 3rd Paragraph; Example 7-3; ruby/ch7/service/config/routes.rb is missing in source archive RESTful_Web_Services_sample_code.zip (184) 1st paragraph; The book states of page 183, there would be a form encoded request parameter such as: user[name]=leonardr&user[full-name]=Leonard%20Richardson it then states this would end up being parsed into: { "user[name]" => "leonardr", "user[full_name]" => "Leonard Richardson" } when in fact.. it would be parsed as: { "user" => {"name" => "leonardr", "full_name" => "Leonard Richardson"} Thus.. the syntax params[:user][:name] (shown in the code for the users controller on page 196) makes sense. The print listing, would not actually "work" if that was the actual data structure {192} Example 7-10. application.rb continued: if_found. The "user_id_from_username" filter was not mentioned anywhere in the book. This should be "must_specify_user". {196} Third line of Example 7-13; The "" element appears to be a mistake. It's never closed, and I couldn't find any such element in the Atom spec. (197) Middle of the second paragraph; "(defined in the User) model)" -> "(defined in the User model)" (198) Example 7-15 # There was a problem saving the __bookmark__ to the database. # Send the validation error messages along with a response # code of 400. should be user account actually. (198) Second paragraph; "data validation errors fail" -> "data validation fails" {199} End of the last paragraph of text; "index and show simply delegate to the show_bookmarks action" does not agree with the code. While index calls show_bookmarks, show actually (and somewhat perversely) calls render_bookmarks. [202] Line 10; This appears to be a gaping security hole, allowing SQL injection if the user enters carefully crafted values instead of a valid bookmarks.user_id value. But I don't know Ruby or Rails, so perhaps I'm seeing a security issue where none actually exists. Is the "?" in the code really a placeholder for the raw user input, or does Rails automatically sanitize the input that would get spliced into this SQL query? In any event, I would suggest commenting this code to tell the reader either (a) "not to worry, this is not a SQL injection vulnerability," or (b) "we suggest you sanitize user input before inserting it into this SQL query." {206} 2nd Paragraph; Example 7-24. app/models/user.rb; Digest::SHA1.new(password).to_s is no longer supported under Ruby 1.8.6. To work, app/models/user.rb would have to be updated to: # Performs a one-way hash of some data. def self.hashed(password) Digest::SHA1.digest("string").to_s end Alternately some additional code in the config/environment.rb as shown in http://coderrr.wordpress.com/2007/09/14/incompatibility-between-rails-116-and-ruby-186/ may resolve the problem. Code in ruby/ch3/S3lib.rb ruby/ch8/calculate-wsse-digest.rb may be similarly affected (217) the first sentence of the 4th paragraph "In the bookmarking service from Chapter 7, I exposed two representations of a set of bookmarks: a generic XML representation at /v1/users/leonardr/bookmarks.xml, and an __Atome__ representation at /v1/users/leonardr/bookmarks.atom. should be "Atom" instead of "Atome" (225) middle of page; "the link text indicate" should be "the link text indicates" [233] 3rd paragraph under URI Design; "Use semicolons when the order doesn't matter: /color-blends/red;blue." The use of semicolons to separate unordered parameters in a URI causes IE to strip the parameters off ServletRequestImpl.pathInfo starting at first semicolon. The remainder of the string is moved to the ServletRequestImpl.pathParameter field, so semicolons should either be avoided or used with a caveat. {241} the 3rd paragraph qop-auth --> qop=auth {245} "Printed" output for two requests, near the middle of the page; The response to the second request has a different Etag than the first. Since the second response indicates that the entity was not modified, shouldn't the tags be the same? In a testing with a few Web sites, I found that the Etag in a 304 response always matched the Etag in the request. (256) the last sentence of the 5th paragraph his --> her (257) 3rd sentence of the 2nd paragraph authorization --> authentication {279} 3rd last line; The URL in the "Location" header should presumably match the URL in the text on the following page. WRONG: Location: http://www.example.com/leonardr/photos/my-guinea-pig.atom RIGHT: Location: http://www.example.com/leonardr/photos/20070124-1.atom The right URL is mentioned in the middle paragraph on page 280 as the URI to which the document can be PUT if modified. There seems to be no where else the .../20070124-1.atom URL could have come from. {280} 1st line; Delete the initial " element does not match the name in the resulting search URLs. WRONG: RIGHT: as the next paragraph mentions generated URLs: http://search.example.com/search?q=jellyfish and http://search.example.com/search?q=chocolate which include "q=", not "query=". {292-4} Example 9-10, 9-12; The example wadl file for del.icio.us uses a mix of two different wadl specifications: 20061109 (https:// wadl.dev.java.net/wadl20061109.pdf) and 20060802 (https://wadl.dev.java.net/wadl20060802.pdf). The example does not validate with either schema. The application has the namespace for the earlier version, but the "header" value for param node's "style" attribute is not defined in this version. It is used in example 9-10. For the later revision, "header" is defined, but the "style" attribute is required for all params. The param in example 9-12 would need a "style" attribute. (305) First complete paragraph (after Example 10-4), penultimate sentence; "SOAP messages, send over HTTP" -> "SOAP messages, sent over HTTP." (310) Last paragraph; "It's possible to prove the originator of a given message was long after it sent, and that the message was not modified after it was received" -> "It's possible to prove who the originator of a given message was long after it was sent, and that the message was not modified after it was sent." (Of course, if it hasn't been modified since it was received, it hasn't been modified since it was sent, but the recipient generally wants to be sure that the message hasn't been tampered with before receipt.) (311) First paragraph without a bullet; "one-off solutions are possible" -> "one-off solutions are not possible". (333) First sentence; The text refers to "the Ajax application delicious-ajax.html," but I don't think the name is used elsewhere. This seems to be referring to the Ajax client listed in Examples 11-1 through 11-5. (341) Second bulleted item; "app/view/welogs/new.rhtml" -> "app/view/weblogs.rhtml" (353) Example 12-9, near the top of the page; The indenting of the code in the if statement is inconsistent. (357) Two thirds of the way down the page, in the paragraph following Example 12-11; "users/jacobian/tags/python" should be "users/jacob/tags/python" to match the prose that follows, or the prose should change from "jacob" to "jacobian." (384) Last line; '414 ("Request Entity Too Large")' -> '413 ("Request Entity Too Large")'