我正在构建一个允许客户端存储对象的服务器。这些对象是在客户端完全构造的,对象id在对象的整个生命周期内都是永久的。
我已经定义了API,以便客户端可以使用PUT创建或修改对象:
PUT /objects/{id} HTTP/1.1
...
{json representation of the object}
{id}是对象id,所以它是Request-URI的一部分。
现在,我也在考虑允许客户端使用POST创建对象:
POST /objects/ HTTP/1.1
...
{json representation of the object, including ID}
由于POST意味着“追加”操作,我不确定在对象已经存在的情况下该做什么。我应该把请求作为修改请求,还是应该返回一些错误代码(哪个)?
也许是在游戏后期,但我在尝试创建REST API时偶然发现了这个语义问题。
为了稍微扩展一下wrkken的回答,我认为您可以根据情况使用409 Conflict或403 Forbidden -简而言之,当用户完全无法解决冲突并完成请求时使用403错误(例如,他们不能发送DELETE请求来显式删除资源),或者如果可能做一些事情,则使用409。
10.4.4禁止403
服务器理解请求,但拒绝执行。
授权没有帮助,请求不应该重复。如果
请求方法不是HEAD,服务器希望公开
为什么要求没有被满足,它应该描述原因
对于实体中的拒绝。如果服务器不希望做出
此信息可用于客户端,状态代码404(不是
Found)可以代替。
现在,有人说“403”,就会想到权限或身份验证问题,但规范说,这基本上是服务器告诉客户端它不会这样做,不要再问了,这就是客户端不应该这样做的原因。
至于PUT vs. POST…当用户没有办法或不应该为资源创建标识符时,POST应该用于创建资源的新实例。当资源的标识已知时,使用PUT。
9.6 PUT
...
The fundamental difference between the POST and PUT requests is
reflected in the different meaning of the Request-URI. The URI in a
POST request identifies the resource that will handle the enclosed
entity. That resource might be a data-accepting process, a gateway to
some other protocol, or a separate entity that accepts annotations. In
contrast, the URI in a PUT request identifies the entity enclosed with
the request -- the user agent knows what URI is intended and the
server MUST NOT attempt to apply the request to some other resource.
If the server desires that the request be applied to a different URI,
it MUST send a 301 (Moved Permanently) response; the user agent MAY
then make its own decision regarding whether or not to redirect the
request.
I think for REST, you just have to make a decision on the behavior for that particular system in which case, I think the "right" answer would be one of a couple answers given here. If you want the request to stop and behave as if the client made a mistake that it needs to fix before continuing, then use 409. If the conflict really isn't that important and want to keep the request going, then respond by redirecting the client to the entity that was found. I think proper REST APIs should be redirecting (or at least providing the location header) to the GET endpoint for that resource following a POST anyway, so this behavior would give a consistent experience.
EDIT:
It's also worth noting that you should consider a PUT since you're providing the ID. Then the behavior is simple: "I don't care what's there right now, put this thing there." Meaning, if nothing is there, it'll be created; if something is there it'll be replaced. I think a POST is more appropriate when the server manages that ID. Separating the two concepts basically tells you how to deal with it (i.e. PUT is idempotent so it should always work so long as the payload validates, POST always creates, so if there is a collision of IDs, then a 409 would describe that conflict).