views:

380

answers:

4

What's the rule of thumb for passing data in a REST URL in the query string vs. the body of a request?

Ie: You're creating a service to add hockey players. You could go with:

PUT /players 
{ "name": Gretzky }

or

PUT /players?name=Gretzky

If you're passing a lot of data, you would need to go with option #1 as there is a limit on the URL length. But other than this, why not just use the query string to pass data?


Update: Removed comment that you could test option #2 in a browser. Realized (duh) that you can only do GET-s in your browser.

A: 

The url used should identify the resource in the body, either by path components or query parameters, though I would prefer path components for something like a name or id. The body should be a representation; the one you PUT should the same or similar as what you GET from the same url (or can get, the in case of multiple formats)

Example #1 is inappropriate because you are sending a representation for a single player to a url for all players. POST would be more appropriate in this case.

Example #2 would be slightly inappropriate if extended to all fields because you would then be sending representation data in the url.

Justin Love
+1  A: 

My understanding of REST operations is that the URL uniquely identifies the resource, while the body of the request contains the representation of the resource. Given that, it's questionable whether either of your options are truly RESTful.

The first would be, assuming that the resource is named "Players" and a GET on that resource returns a list of players (I won't get into the question of whether that GET returns other resource URLs or not ... Fielding would say that it should, with individual requests to get the resource data).

The second would be, assuming that the request body contained information keyed by name "Gretsky". However, that requires you to generate the keys externally.

kdgregory
For the record, Fielding is right. Your `/players` representation should provide hyperlinks to the representations at `/players/gretsky`, etc. He gave a lot of really good reasons why out-of-band information is a bad idea, and I think history is very slowly proving him right. All of the best internet-scale protocols essentially do this, and the ones that don't tend to be frustrating and difficult to implement clients for.
Bob Aman
@Bob Re-read section 6.2 of Roy's dissertation. URIs are Uniform RESOURCE Identifiers. As Roy states, "URI identifies a concept rather than a document".
Darrel Miller
I've deleted the comment, because I can see why it could be wildly misunderstood, though I still stand by the point that more than one URI can legitimately point at a single logical resource. A URI can identify a representation as well as a resource.
Bob Aman
Yes, you are correct. Multiple urls are often used to point to different representations of the same resource. Some people argue that they become different resources at that point. It is an often debated issues.
Darrel Miller
Regarding returning hyperlinks rather than the literal contents of collections: I have a hard time accepting this. First, because you have to pay overhead to create N new requests. But more important, because the client *always* has to know how the server presents data. So the difference between /Players returning a bunch of /Players/1234 URLs, and /Players returning the actual data is rather like counting angels on a pin.
kdgregory
@Bob - I actually think your original comment re format made sense. I think, however, it requires a definition of "resource" as separate from "entity".
kdgregory
+1  A: 

Option #1 is fine, though probably overkill. Option #1 is not fine because it's not idempotent.

Option #2 is a BAD idea. That would be misusing PUT. PUT should be used primarily when your request data payload is an opaque block of data, usually either large or hierarchical. Smaller, non-hierarchical payloads make more sense as POST.

Also, try to avoid changing state via query parameters. There's nothing technically dangerous about that if it's not a GET request, but it's not really RESTful.

In this case, what you should be doing is:

POST /players HTTP/1.1
Host: www.example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 12

name=Gretsky

This should return a 201 Created response. (There is an exception to this: If you don't create the resource immediately, and it might be rejected at a later time, use 202 Accepted instead.)

Writing a REST web service that uses more of HTTP than POST and GET should only be done after having read the HTTP specification. (It's a very useful read.) That rule is a bit looser if you're using a framework that makes all the decisions for you.

Bob Aman
Option 1 is not fine if the intent is to add a new player to an existing list of players. Where have you seen it suggested that payload size and shape has an impact on the choice between PUT and POST?I am not aware of any REST constraint that would prevent you from using a POST and a query parameter to change state.
Darrel Miller
Yeah... probably not clear what I meant. The point is that PUT should be idempotent and there should be a corresponding ability to GET whatever you PUT. This makes good sense for large or hierarchical data, because you're likely to want to retrieve the same data intact. Whereas with POST you don't have these constraints, and you're free to optimize for simplicity, e.g. `application/x-www-form-urlencoded`.
Bob Aman
Also, you're absolutely right about Option #1. I glazed over it, but yeah, that would violate idempotency.
Bob Aman
"I am not aware of any REST constraint that would prevent you from using a POST and a query parameter to change state." There isn't one. That's more like a misuse of the URI itself.
Bob Aman
@Bob Why it mis-using the URI?
Darrel Miller
How do you feel about something like the following? POST /Bookmarks?url=www.google.com/stuff'
Darrel Miller
Wouldn't do it. For starters, doing that would be an unbelievable headache in terms of escaping, but more importantly, you have `application/x-www-form-urlencoded` at your disposal. Make use of it!
Bob Aman
I will accept that there may be better ways to do it in many cases, but I just want people to be more careful about throwing around the "that's not RESTful" line. If you can't match it to a REST constraint that it violates, then just limit it to saying that you wouldn't do it.
Darrel Miller
@Bob One advantage of being able to put everything in a query string is that it makes implementing hypermedia much easier. The entire URL can be embedded in a previous response and the client just has to POST to the provided link.
Darrel Miller
Hold on... are you designing telepathic APIs? I'm assuming that if you have a POST, that means the client is changing something. How are you supposed to hyperlink to something if the data is in the URI, and the client is the one with the data? That's why it's not RESTful: there's no practical way to hyperlink to that without a URI template, and that's a draft spec. Also in heavy-rewrite mode at the moment. Take your bookmark example: You can't hyperlink directly to `/Bookmarks?url=www.google.com/stuff` because the API doesn't know about `www.google.com/stuff` yet.
Bob Aman
The reason I said it wasn't RESTful is because in most cases, you have to use out-of-band information to perform a POST with the data in the query parameters. And that **is** a REST constraint.
Bob Aman
@Bob Let's say you present to the user a list of resources and ask them to pick their 5 favourites. The server can pre-populate the a hyperlink for each resource with the query parameters indicating which resource that link is referring to. I am not suggesting the client construct urls, using a URI template or any other method, I'm giving the user a set of pre-existing URLS to choose from.
Darrel Miller
Sure, but you do recognize that this means there's an entire class of problems for which your solution won't work, right? More importantly, it's the class of problems for which most APIs are written.
Bob Aman
Don't get me wrong, I'm all for hypermedia. I'm on-board with "hypermedia as the engine of application state", though I'm with Sam Ruby in that I think the phrase is unclear. However, in this context, linking requires you to carry information primarily through the URI, and it requires the application to have the information ahead of time. I continue to assert that there are problems that you may want to solve with an API for which this isn't an appropriate solution. And I'm OK with having an API that can only be called "80% RESTful" by the strictest definitions of what "REST" is.
Bob Aman
I'm not sure why you think what I am suggesting "requires the application to have information ahead of time". I'm am not suggesting that every problem can be solved by putting data into the URL. I'm just don't see any disadvantages to using it when it makes the most sense. I'm OK with an application where some parts of it are RESTful and some parts are not. Roy is quite clear on the fact that REST is not the solution to every problem. I just object to people using the term REST to describe an API when not all the constraints are met.
Darrel Miller
Ok... maybe I misunderstood your "bookmark" example? Was that not intended to be a means of allowing a user to bookmark something? It seems to me that if the client is trying to bookmark `www.google.com/stuff` and the URI for doing that is `/Bookmarks?url=www.google.com/stuff` that would require the client to know ahead of time how to construct that URI. i.e., no hyperlink is possible, and you need out-of-band information, thus not RESTful. If something else was intended... that probably should have been made more clear.
Bob Aman
+4  A: 

Based on HTTP's definition of PUT, your first request is overwriting the list of players with a new list that contains just one player name. It is not adding to the list of players.

The second option does not really make much sense to me. Doing PUT without a body is not really consistent with the meaning of PUT.

Considering that one of the standard definitions of POST is to append to an existing resource, I'm not sure why you wouldn't do

POST /players 
{ "name": Gretzky }

If you are sure that all you player names are going to be unique then you could use PUT like this:

PUT /player/Gretzky
{ "name": Gretzky }

When you decide to do REST on HTTP you are agreeing to use HTTP in the way that is defined in RFC2616. That's what the uniform interface constraint means. And just to be pedantic, there is no such thing as a REST URL and you can't test either option in a browser because without javascript, you can't do a PUT in a browser.

Darrel Miller