我正在设计一个API来通过HTTP,我想知道是否使用HTTP POST命令,但只有URL查询参数,没有请求体,是一个很好的方法。
注意事项:
“好的网页设计”要求通过POST发送非幂等动作。这是非幂等的。
当请求参数出现在URL中时,更容易开发和调试这个应用程序。
该API并不打算广泛使用。
似乎要做一个没有正文的POST请求需要更多的工作,例如必须显式添加Content-Length: 0报头。
在我看来,没有正文的POST有点违背大多数开发人员和HTTP框架的期望。
在POST请求中通过URL查询而不是通过请求体发送参数还有什么缺点或优点吗?
编辑:考虑这一点的原因是操作不是幂等的,除了检索之外还有其他副作用。请参阅HTTP规范:
In particular, the convention has been
established that the GET and HEAD
methods SHOULD NOT have the
significance of taking an action other
than retrieval. These methods ought to
be considered "safe". This allows user
agents to represent other methods,
such as POST, PUT and DELETE, in a
special way, so that the user is made
aware of the fact that a possibly
unsafe action is being requested.
...
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. The methods GET, HEAD, PUT
and DELETE share this property. Also,
the methods OPTIONS and TRACE SHOULD
NOT have side effects, and so are
inherently idempotent.
我发现在POST端点上使用查询参数是完全可以接受的,如果它们引用了必须通过POST端点更新的已经存在的资源(而不是创建的)。
例如:
POST /user_settings?user_id=4
{
"use_safe_mode": 1
}
上面的POST有一个引用现有资源的查询参数,镜像GET端点定义以获得相同的资源。
body参数定义如何更新现有资源。
编辑:
我更喜欢这样,而不是像有些人建议的那样,让终点直接指向已经存在的资源:
POST /user_settings/4
{
...
}
原因有三:
我发现它有更好的可读性,因为查询参数是命名的,就像上面的“user_id”,而不是“4”。
通常,还有一个GET端点来获取相同的资源。在这种情况下,端点的路径和查询参数将是相同的,我喜欢这种对称性。
我发现,如果需要多个参数来定义已经存在的资源,嵌套会变得很麻烦,很难读取:
POST /user_settings/{user_id}/{which_settings_id}/{xyz}/{abc}/ ...
{
...
}
每个人都是对的:对于非幂等的请求,坚持使用POST。
如果同时使用URI查询字符串和请求内容呢?它是有效的HTTP(见注释1),所以为什么不呢?!
这也是完全合乎逻辑的:url,包括它们的查询字符串部分,是用来定位资源的。而HTTP方法动词(POST -及其可选的请求内容)是用于指定操作或如何处理资源的。它们应该是正交关系。(但是,对于ContentType=application/x-www-form-urlencoded的特殊情况,它们并不是漂亮的正交关系,参见下面的注释2。)
Note 1: HTTP specification (1.1) does not state that query parameters and content are mutually exclusive for a HTTP server that accepts POST or PUT requests. So any server is free to accept both. I.e. if you write the server there's nothing to stop you choosing to accept both (except maybe an inflexible framework). Generally, the server can interpret query strings according to whatever rules it wants. It can even interpret them with conditional logic that refers to other headers like Content-Type too, which leads to Note 2:
Note 2: if a web browser is the primary way people are accessing your web application, and application/x-www-form-urlencoded is the Content-Type they are posting, then you should follow the rules for that Content-Type. And the rules for application/x-www-form-urlencoded are much more specific (and frankly, unusual): in this case you must interpret the URI as a set of parameters, and not a resource location. [This is the same point of usefulness Powerlord raised; that it may be hard to use web forms to POST content to your server. Just explained a little differently.]
注3:查询字符串最初用于什么?RFC 3986将HTTP查询字符串定义为URI部分,作为一种定位资源的非分层方式。
In case readers asking this question wish to ask what is good RESTful architecture: the RESTful architecture pattern doesn't require URI schemes to work a specific way. RESTful architecture concerns itself with other properties of the system, like cacheability of resources, the design of the resources themselves (their behavior, capabilities, and representations), and whether idempotence is satisfied. Or in other words, achieving a design which is highly compatible with HTTP protocol and its set of HTTP method verbs. :-) (In other words, RESTful architecture is not very presciptive with how the resources are located.)
最后注意:有时查询参数用于其他事情,既不是定位资源也不是编码内容。是否见过类似“PUT=true”或“POST=true”的查询参数?这些是不允许使用PUT和POST方法的浏览器的变通方法。虽然这些参数被视为URL查询字符串的一部分(在网络上),但我认为它们本质上不是URL查询的一部分。