PUT vs POST

lachoy on 2006-06-16T17:27:53

In reading up on REST principles and arguments over the last couple of weeks, I've run into quite a bit of confusion about what some of the HTTP methods mean. GET and DELETE are straightforward, no confusion. But PUT and POST are another matter.

For instance, in his excellent article How to Create a REST Protocol, Joe Gregorio has the following table:

HTTP Method CRUD Action Description
POST CREATE Create a new resource
GET RETRIEVE Retrieve a representation of a resource
PUT UPDATE Update a resource
DELETE DELETE Delete a resource

While it's useful on the surface to think about mapping HTTP verbs to SQL verbs, I think that because there's overlap between PUT and POST we're hiding an important impedance mismatch between data-as-relational-records and data-as-manipulatable-resources. For instance, the table implies that PUT is like the SQL UPDATE, but by common acceptance PUT shouldn't be used to just update 1 field of 20 as commonly done with SQL UPDATEs. So when you PUT an update with just a change of a customer's first name and a number of other fields are modified (because you didn't supply values for them), you might get a little freaked out.

Here's a similar view from Paul Prescod, who says in his Common REST Mistakes:

Do not overuse POST. POST is in some senses the "most flexible" of HTTP's methods. It has a slightly looser definition than the other methods and it supports sending information in and getting information out at the same time. Therefore there is a tendency to want to use POST for everything. In your first REST Web Service, I would say that you should only use POST when you are creating a new URI. Pretend POST means "create new URI as child of the current URI." As you get more sophisticated, you may decide to use POST for other kinds of mutations on a resource. One rule of thumb is to ask yourself whether you are using POST to do something that is really a GET, DELETE or PUT, or could be decomposed into a combination of methods. (emphasis added)

To muddy the waters even more, here's what the HTTP 1.1 RFC says about POST:

POST is designed to allow a uniform method to cover the following functions:
  • Annotation of existing resources;
  • Posting a message to a bulletin board, newsgroup, mailing list, or similar group of articles;
  • Providing a block of data, such as the result of submitting a form, to a data-handling process;
  • Extending a database through an append operation.

...and about PUT:

The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.

It's easy to see where the confusion is because according to the spec:

  • POST can be used as an UPDATE (annotating an existing resource) and as an INSERT (posting a message to a bulletin board)
  • PUT can be used as a full UPDATE (replacing entirely an existing resource) and as an INSERT (adding a new resource)

So when you're using HTTP methods as an integral part of your application it's important to clarify how you're interpreting PUT and POST.

Here's my first attempt:

  • PUT is create or replace
  • POST is partial or complete update of an existing resource, or adding metadata to an existing resource

Some examples. Add a new customer:

PUT /customer HTTP/1.1
Content-Type: text/xml
<customer>...</customer>

Update all fields of customer with ID 1234:

PUT /customer/1234 HTTP/1.1
Content-Type: text/xml
<customer>...</customer>

Update the 'firstName' field of customer with ID 1234:

POST /customer/1234 HTTP/1.1
Content-Type: text/xml
<customer>
  <firstName>Napoleon</firstName>
</customer>

That's easy. Now, for something more complicated: what if I want to add a new address to a customer? I could do it with either one depending on how the resources are structured. Is an address a separately addressable resource, or is it completely dependent on a customer? Can there be more than one address of a particular type, or can a customer have as many addresses as necessary?

It's probably a good rule of thumb to make every resource separately addressable. You never know how you might want to use them in the future. So we'll use a PUT and include a link to our related resource:

PUT /address HTTP/1.1
Content-Type: text/xml
<address>
  <street-address>111 Main Street<street-address>
  <city>Pittsburgh</city>
  <state>PA</state>
  <postal>15217</postal>
  <customer href="/customer/1234" />
</address>

That seems to work pretty well. There may be cases I haven't thought of yet, we'll see how they can fit into this scheme.

Posted from cwinters.com; read original


Idempotence

jordan on 2006-06-16T18:11:35

I think the most important distinction to remember between PUT vs. POST is that PUT is intended to be idempotent and POST is not.

You should be able to PUT the same resource with the same data and it have no additional effect, after the first time.

Remember also that the agent (client) submitting a PUT request should be able to tell if this is a new resource being created through the 201 response, although in practice this is heavily abused and many programs always return 200 responses for all success.

Re:Idempotence

lachoy on 2006-06-16T20:04:53

Yep, I forgot to mention this. An interesting thing about the idempotence discussion is that people confuse "I submit foo once or twenty times and get the same response" versus "I will have no side effects on the server from submitting foo."

Trouble with using PUT for Create

jjn1056 on 2006-06-17T15:41:33

I usually end up using POST for all CREATES, since I find no good way to pregenerate the URI to the thing I'm creating. Say you have a resource /videos which is the root to a listing of all the videos in your system. Each individual video might have a URI like /video/100, /video/110, etc. But if I want to create a new video using put I have to PUT the file to something like /video/120 but how do I know it's 120? Usually this ends up being autogenerated by a database anyway. So I find it most easy to POST to /video and the HTTP response is a redirect to the newly created resource.

I'm curious how you might get around this problem?

Re:Trouble with using PUT for Create

lachoy on 2006-06-18T00:30:38

From what I've read the common thing to do is PUT to the 'collection' URI -- in your case, you'd do 'PUT /videos'; the server would return a '201 Created' header with the URI of the resource you just uploaded, like: '/video/110'.