What is the appropriate HTTP response code when a client tries to PUT
to an entity that is currently read only by nature?
A toy example is a product shipment. Before the shipment is sent, the details (address, products, quantities) can be changed (e.g. with a PUT
request). However, as soon as the shipment is sent, any PUT
should fail, even if the request format and syntax are correct.
It's possible that the client doesn't know that the shipment has been sent, so it's not a "careless" error on the client side.
400
doesn't seem appropriate, because the input is well formed and the syntax is correct.
405
seems like a good fit. Is this a common response in this case?
403
seems to imply authorization has been revoked, which could be misleading.
422
seems to fit well, but its use seems discouraged if you don't provide WebDAV capabilities (which we don't).
500
makes it sound like someone tripped over a cable, though I hear some developers/frameworks use this status in this case.
Is there a standard practice for this case? What is least likely to cause confusion for the API user (developer) and the end user (person using the UI)?
I would look at 405 Method Not Allowed. It is defined like this:
The 405 (Method Not Allowed) status code indicates that the method received in the request-line is known by the origin server but not supported by the target resource. The origin server MUST generate an Allow header field in a 405 response containing a list of the target resource's currently supported methods.
Your server understands the request perfectly, but it no longer supports writing. In addition, the requirement to return the client the list of supported methods sounds clean.
As an added bonus, the 405 response is cacheable by default, which could make sense in your case.
Another viable alternative is 409 Conflict:
The 409 (Conflict) status code indicates that the request could not be completed due to a conflict with the current state of the target resource.
Arguably the order changed state, in such a way modifying it is no longer possible. Note however that:
This code is used in situations where the user might be able to resolve the conflict and resubmit the request.
…so I would tend toward the other one.