问题
Given the requirement other departments have for our REST API they would like to use POST
not just for CREATE but for UPDATE OR CREATE. I know in a RESTful API PUT
could or should be used for that, but because clients have to update information that is used to build the URI, we cannot use that. It would change the URI and make PUT
not idempotent anymore... (the old URI would not exist after the first PUT
).
tl;dr we cannot use PUT
In the HTTP/1.1 specs POST is defined as
The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI
but also
The action performed by the POST method might not result in a resource that can be identified by a URI.
To stay RESTful I would explain the update functionality as an Deletion of the old element and then a Creation of the new one, which would be an acceptable functionality for POST
I would say.
We would return a #201
when creation was successful and a #200
when it was just an update.
In our API it is "possible" to address the right element without the URI (e.g. for updating it with POST
), because all the URI building primary key parts are in the resource body, so the API knows which element the client wants to access.
Example
(This is just an example of the behavior for POST
. Not for the data structure of the resource. Of course using PUT
would be completely right for /cars/
)
POST /cars/ HTTP/1.1
<car>
<id>7</id>
<status>broken</status>
</car>
response: #201
and then
POST /cars/ HTTP/1.1
<car>
<id>7</id>
<status>fine</status>
</car>
response: #200
now a GET
on /cars/7
would return the following:
<car>
<id>7</id>
<status>fine</status>
</car>
回答1:
"Violating RESTfulness" is not strictly defined.
A good reference to degrees of REST-like behaviour in an interface is the Richardson Maturity Model which splits degrees of support for REST ideals into 4 tiers (with 0 being "not REST at all" and 3 being "completely REST, but hardly anyone does this yet")
Your choice breaks only slightly with RESTful HTTP-based design at level 2 within this model. However, it is the kind of compromise that many real-world projects have to contend with.
POST is open-ended enough that it can be idempotent if you wish, but HTTP does not require it to be. So you have not broken with HTTP, just missed the opportunity to use the more germane PUT method. The reverse situation, using PUT for a non-idempotent part of the API, would be more problematic.
As a personal opinion, I think that if you remain self-consistent when handling the different HTTP methods and routes, and document this change to expectations clearly, then you can reasonably call your API "RESTful". I think the response code split 200/201 is enough that a self-consistent client could be written. Although I would prefer to see both create and update as PUT here, it's not something that would cause me more than a moment's pause.
You might consider supporting update or create on the collection route (POST /cars
in your example) as suggested, plus update on the item route (POST /cars/7
). The concept of "update or create" is common enough in database and ORM frameworks, that it would be easily understood. A client could also use the latter update-only route in confidence that it would not accidentally auto-create a new record.
回答2:
Naturally, it is not possible to ensure that the server does not generate side-effects as a result of performing a GET request; in fact, some dynamic resources consider that a feature. The important distinction here is that the user did not request the side-effects, so therefore cannot be held accountable for them.
Methods can also have the property of "idempotence" in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.
So idempotence is about the server-side side-effects. Your second PUT does not have any other side-effects (because of 404), than the first one, so using PUT is idempotent here.
The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI
In this case you don't create a new resource, you just add a new resource identifier and remove an old resource identifier. So POST is inappropriate.
The action performed by the POST method might not result in a resource that can be identified by a URI.
This can mean anything you want... Ppl use POST for non-creational purposes when they are out of options, but you are certainly not.
In our API it is "possible" to address the right element without the URI (e.g. for updating it with POST), because all the URI building primary key parts are in the resource body, so the API knows which element the client wants to access.
You have to use IRIs (URL) to identify resources if you don't want to violate the uniform interface (identification of resources) constraint.
You have to use hyperlinks if you don't want to violate the uniform interface (hypermedia as the engine of application state) constraint. The IRI structure does not carry any semantics for the clients, so it can be anything if it is up to them. You have to use link relations or some attribute from a vocab to add semantics.
You have to use at least vendor specific MIME types if you don't want to violate the uniform interface (self-descriptive messages) constraint.
By definition a REST webservice meets with all of the REST constraints. The term RESTful was created when ppl - who had no idea about REST - started to call their web services as REST. So after that ppl created the RESTful term, which means that the so called REST service violates none of the REST constraints. After that the term RESTful was exhausted too. Nowadays we distinguish hypermedia API and web API. The hypermedia API means that the web service meets with the HATEOAS (hypermedia as the engine of application state) constraint. The web API can be anything, which claims to be REST.
来源:https://stackoverflow.com/questions/25909086/do-i-violate-restfulness-when-using-post-as-update-or-create