Your Web Service Might Not Be RESTful If…
theamazingrando.com
theamazingrando.com
The broad concepts of REST are good, but this pedantic stuff is so silly. There's a reason that most popular APIs will fail this test -- the people who built them were thinking about more important issues than achieving some kind of religious purity.
My only point of agreement with the author is that web services that aren't RESTful shouldn't describe themselves as RESTful.
What issues are those? The article does give reasons for each recommendation that go beyond an appeal to RESTful Authority. I would be curious to hear how the criticized techniques solve some important issue better than the article's recommendation. For example, is there a specific reason that API tokens are better than Digest authentication?
(honest question, not snark) - How do you handle application specific messages and errors using the normal http error codes?
"Over credit limit" would be "402 Payment Required."
You can always return additional, application-specific details in the body of the error response. But choosing a correct HTTP status code is an important first step because it has well-defined semantics (e.g. caching behavior) for client libraries or proxy software that know nothing about your application, and it allows the client to use a single code-path for all error handling.
I was picking on the Posterous API because each response has up to three "statuses" - the HTTP status code, the rsp@stat attribute, and the rsp/err@code attribute. The docs don't even mention what combinations of HTTP status code and response body are valid. (Will errors be served as 200 OK? Will some responses have 4XX or 5XX status codes but not have an "err" element in the body?)
As it is, Posterous clients may need to handle a 200 response with an error in the body, an error response with an error in the body, and an error response without an error in the body (which is not documented in the spec but probably can't be ruled out). Not all client writers will think of all these cases, and not all will handle them the same. It would be better for the API to define its behavior in terms of HTTP, which would put all error handling in a single place.
Yes, it's probably more or less guaranteed that if you're hitting Posterous's API through proxies (your own local proxy; your ISP's transparent proxy, if you're not in the US and/or make a bad choice of ISP; the proxy in your company's firewall; a reverse proxy deployed on Posterous's side) that proxy will sometimes generate error messages without any knowledge of what Posterous may or may not have documented.
I haven't read Posterous's spec, maybe they have some way of preventing this, like requiring SSL.
Those people weren't "thinking about more important issues", they were being lazy (or just copying what was being done already, without thinking about why). Its akin to javascript before ajax an jQuery, before we knew any better, we just copy and pasted code off the internet that worked, without spending the time to discover there might be a better way.
Because people are "thinking about more important issues", they're making it harder for consumers to write clients of their API, and they're presenting themselves with unmaintainable web services in the future.
- Changing the URIs of some resources in order to load-balance across servers, which is a lot easier if clients get those URIs via hypertext instead of from the protocol spec.
- Screwing up user authentication when you could have just used digest authentication.
- Having your service run really slowly because you can't just use Varnish to cache the cacheable parts. (Maybe you didn't actually say this.)
I can't actually find any others in your article. This discussion thread has added a couple more:
- Getting HTTP error codes from an intermediate proxy server instead of from the intended origin server, which will make you wish you had used those HTTP error codes to indicate errors in your app, so that clients would handle them correctly. (There are a lot of ways proxy servers can get involved.)
- Having random robots (Google Web Accelerator, web crawlers, etc.) trash your database by GETting URLs that have destructive side effects.
- Having people not able to explore your API with their browser.
What are the other scenarios?
As an example, here's how you create a new meeting with Twiddla's (evidently RESTless API). One POST over SSL:
> POST https://www.twiddla.com/new.aspx
> username=billy
password=iLikeRainbows
< HTTPS/1.1 200 OK
< 109232
Now, here's how this author suggests people do it: > GET http://api.twiddla.com/
< [XML packet full of endpoints comes back]
> GET [endpoint you parsed from that list]
< HTTP/1.1 401 Authorization Required
> GET [same endpoint after jumping thru crypto hoops]
< HTTP/1.1 200 OK
< 109232
As an API publisher, I can see absolutely no value in making my users jump through all those hoops just to use my API. The API exists so that people can get things accomplished on my site, and I don't see any reason why it shouldn't be just as usable as the site itself.Please, if you're putting out a simple API, don't make it overly complicated just to please this author and others like him. Make it exactly as complicated as it needs to be to perform the task intended and no more.
In the real world, any HTTP client library worth using already implements HTTP authentication transparently to you, the API consumer.
Regardless, assuming I'm using your library, I'd still need to do a request, some XML parsing to discover the right url, followed by a second request, just to get back the response I want. Compared to 1 request, that's still 3 times the effort for no added benefit.
curl --anyayth --user admin:sekret http://exmaple.com/resource_protected_by_digest_authThere are some added benefits. The load-balancing example he gives in the article is one; a second one is that there's less stuff to get wrong in the client code, generally. Instead of working from some informal equivalent of WSDL, you're writing code that interrogates the service at run-time to find the current definition of the interface.
RestfulApi.new('http://api.twiddla.com/, :user => 'billy', :password => 'ILikeRainbows').post('Meetings')
class User
include DataMapper::Resource
resource_name "AllUsers"
# define some properties
end
User.first(:login => "admin")
http://github.com/absperf/dm-ssbe-adapter/treeI'm going back and forth on this one. On the one hand, it is a pain to go through those steps if you already know which service you want. On the other hand, having that listing available makes the site kind of self documenting, in that you can explore the services available.
What I find more potentially useful is the idea of putting URLs into the XML for subordinate resources instead of some kind of ids or other identifiers. Then the client can follow those URLs to get a user's documents, or whatever it is you are modeling. Anything where there is a relationship but it is too expensive to just dump all the data in a single XML response.
It's pretty intuitive, and arguably easier than just supplying an id that the client must use to construct the appropriate URL.
def find_user(login)
users.select {
|u| u["login"] == login
}.first
end
def users
users_href = services.select {
|s| s["name"] == "AllUsers"
}.first["resource_href"]
JSON.parse(
@http.resource(users_href).get(:accept => SSJ).body
)["items"]
end
def services
JSON.parse(
@http.resource(@services_uri).get(:accept => SSJ).body
)["items"]
end
@http is a handle to an HTTP library, and @services_uri is the well-known URI. So its not nearly as complicated as it might seem at first, and the benefits are tremendous.The other part is discovering of the service locations. By not doing so, you're handcuffing yourself. In your API, you are stuck forever with `/new.aspx`. Should you decide someday to have something else you want clients to be able to create, you can't separate it out into `/new_foo.aspx` and `/new_bar.aspx`.
Doing these kinds of things actually make it less complicated in the long run, just like using jquery is less complicated than copy/pasting 50 lines of javascript of the web.
The API in question has exactly one endpoint, so no possible value could come from having to look it up on a list each time you want it. And it is a POST to create something, which means you'd never cache it.
I picked that example because it is an existing use case that demonstrates that the rules you're proposing don't apply to every situation.
If your app only has a single resource, then there's no reason why it can't be the one well-known resource itself. There's no need to be snarky, these constraints apply perfectly to your application as it is. If you have any intentions of your app growing beyond that single resource, however, its not that complicated to add these constraints to make that future growth easier.
The author also conveniently ignores a glaring detail that always makes me angry when people talk about REST as something people should adopt. The simple fact is that browsers don't support REST — no browser supports the PUT or DELETE methods, not to mention other things in the stack like the web server. Yes, Rails has built-in support for REST, but it's accomplished via a disgusting hack whereby hidden form variables are sent to mimic non-supported verbs.
I've always felt that saying that people should adopt REST when browsers don't even support it is like saying people should switch to wind power when there are no wind farms producing it. Nice in theory, but not practical and far more trouble than it's worth.
Also, REST != PUT + DELETE. Don't get hung up on it, they're just surface-level niceties. The real point is to not mix up GET & POST, to be careful about state and idempotence.
This - specifically, how to do user authentication/access control in a RESTful manner - has always puzzled me. The author suggests HTTP Digest authentication, but that seems to fly in the face Roy Fielding's commandment that a RESTful interface shouldn't depend on the communications protocol being used; HTTP Digest seems pretty tightly coupled to HTTP, no?
Broadly, what are some of the canonical ways to do user authentication while still keeping close to the theoretical/theological underpinnings of REST?
Specifically:
A REST API should not be dependent on any single communication protocol, though its successful mapping to a given protocol may be dependent on the availability of metadata, choice of methods, etc. In general, any protocol element that uses a URI for identification must allow any URI scheme to be used for the sake of that identification. [Failure here implies that identification is not separated from interaction.]
These points are great though and help make better restful apps.
Ponder... something to include in my in-progress library, perhaps?
This is why I'm making this independent of other libraries. Implementing a nice interface to the mess that is XMLHttpRequest is not that difficult - and for what should be a tight, lightweight library, requiring something like Prototype is overkill.
Is there a meaningful difference between:
GET /user/1
GET /search?user=1
GET /?method=search&user=1
Is REST about the URLs you use, or the operations on those URLs, or both?
GET /q
200 OK
{ 'start': '/q/start', 'end': '/q/end', 'shift': '/q/shift', 'pop': '/q/pop' }
POST /q/end
x
200 OK
Location: /q/some-identifier-for-element-x
The queue now contains [x]. POST /q/start
a
200 OK
Location: /q/some-identifier-for-element-a
The queue now contains [a, x]. POST /q/pop
POST /q/shift
With obvious results. I'm not totally happy with the last 2 operations, another solution might be to GET /q/start or /q/end and DELETE the returned queue element URL. If the delete succeeds, do what you want with the data, otherwise assume that somebody got to it before you and attempt another GET-DELETE.Neither method handles "client never got the server's response" very well, I'm not sure what a better solution would be.
Totally generic things like this are unusual, you can usually come up with a better set of resources and operations based on your problem domain.
REST is about the operations on the URLs; the URLs themselves should be totally opaque.
Without knowing anything about how the 3 urls you gave are used, what other operations are possible and how they interact with other resources it is impossible to say whether you're doing REST. The third one looks very suspicious, however (the second one too, though less so).
In fact, this is what XML was actually designed for.
But your browser is attempting to render an XML document, then the server probably has a MIME content-type error someplace.
I do agree that it is nice to be able to test something pretty easily, but the browser is probably the wrong thing to use for this. I always end up mocking up some scripts using curl or just manually typing in commands to the HTTP server using: telnet hostname 80
This is like saying, "See, there's the problem. You're trying to express mathematics with pencil marks. Pencils should write one thing: black marks on paper."
I don't see how "HTML documents" in any way constrains the semantic range of the thing being modeled.
(also the entire world does not revolve around browsers)