Sound advice - blog

Tales from the homeworld

My current feeds

Sun, 2007-Mar-18

Deprecating POST

Following recent discussion on rest-discuss, I have been experimenting with deprecating the POST method for architectures my organisation controls, and am eyeing DELETE with some suspicion also. I am suggesting that PUT be used wherever possible.

Pendulums and Muddy Waters

I think there is a pendulum in method definition. Every so often you think you need a few more methods to do special things, but the time comes to swing back and consolidate. GET is a clear winner both in the REST and the non-REST camps, but argument rages over use of the other classic REST verbs. POST is particularly problematic because of its non-idempotent nature. If my POST request times out, I can't just try again. I might be submitting my request twice.

I see value in trying to reduce the set of methods down to GET and PUT. I think it could clear the waters a little and result in simpler architectures. At the same time I acknowledge that asking the question can be seen to be muddying the waters in the short term. I hope I don't trigger too much gnashing of teeth with this suggestion, nor with a suggestion I may make soon about cutting down the HTTP response codes to a "useful" subset. I acknowledge also that I might be pushing some anti-URI-construction buttons with this article. However, I see that also as an issue that needs some clarity and guidelines going forwards. Ultimately, I hope to draft some documents for my company's internal use about the subset of HTTP they should implement and about other aspects of RESTful design. I hope that such documents could eventually be made available to a wider audience, also.

The proposal

My current direction is to combine PUT with client-side "form submission". The classic POST request might be as follows:

POST /factoryResource HTTP/1.1
Host: example.com
Content-Length: ....

<PurchaseOrder>
...
</PurchaseOrder>

The server may respond with:

HTTP/1.1 201 Created
Location: https://example.com/newResource
...

In my experimental approach I require the client to provide a globally unique identifier as part of the request URL. An example request might be:

PUT /factoryResource?guid=76fd9473-a270-4aac-8a06-e5265048cbbc HTTP/1.1
Host: example.com
Content-Length: ....

<PurchaseOrder>
...
</PurchaseOrder>

The server may respond with:

HTTP/1.1 201 Created
...

My architecture is highly focused around machine-to-machine comms, so I expect that most servers will simply create a resource at the location the client requests (including the query part). The actual construction by a client of a request url from the base factory url is anticipated to be standard, which is to say "hard-coded". Every factory resource should become a factory resource family with the same query structure. I am following the direction laid down in one of my recent articles of suggesting that the query component of a url constructed by clients should follow content-type rules of having consistent structure between similar services.

Many services will choose to use their own unique identifiers for the created resources. For example, the guid could be substituted for a monotomically-increasing number or other identfier within a particular context. These services should redirect the PUT request to their preferred url:

HTTP/1.1 301 Moved Permanently
Location: https://example.com/newResource
...

Note that there is still a caveat when attempting to POST/PUT Once Exactly (POE). It is important that created resources are not too short-lived. If they are short-lived the server should keep track of the fact they recently existed in order to inform the client that is creation attempt was successful by way of a 410 Gone response to subsequent PUTs. A similar problem arises if the state of the resource is updated soon after resource creation. The client who thinks the creation request failed could blat over the changed state. Perhaps there still does need to be some separation between a method that creates state at a particular url and one that replaces state. Perhaps there does need to be separation between a CREATE method and an UPDATE method. Too late now for HTTP on the Web, perhaps.

Conclusion

Pub/Sub aside, GET is the right way to transfer state from server to client. However, the waters are muddier when transferring from client to server. I suggest that special applications of PUT may be more useful than defining separate methods. PUT, POST, and DELETE currently mean "create or replace state where I tell you", "create state wherever you like", and "eliminate state" or "replace with the null state". My gut suggests that collapsing all of these methods into PUT is a net win. My only hesitation is in the misuse PUT might see if the "deprecate POST" message gets out too far. Therefore, I suggest PUT be used where state is being transferred idempotently. POST should still be used where tunnelling or "process this" semantics are required. POST should be a warning for any code that sees it to tread carefully: The guard ropes have been cut.

Benjamin