我正在为我们的应用程序开发一个新的RESTful Web服务.
在某些实体上执行GET时,客户端可以请求实体的内容.如果他们想要添加一些参数(例如排序列表),他们可以在查询字符串中添加这些参数.
或者,我希望人们能够在请求正文中指定这些参数. HTTP/1.1似乎没有明确禁止这一点.这将允许他们指定更多信息,可以更容易指定复杂的XML请求.
我的问题:
这完全是一个好主意吗?
HTTP客户端在GET请求中使用请求主体会有问题吗?
http://tools.ietf.org/html/rfc2616
罗伊菲尔丁关于将一个机构纳入GET请求的评论.
是.换句话说,任何HTTP请求消息都允许包含消息体,因此必须解析消息.但是,GET的服务器语义受到限制,使得正文(如果有的话)对请求没有语义含义.解析的要求与方法语义的要求是分开的.
所以,是的,你可以使用GET发送一个正文,不,这样做永远不会有用.
这是HTTP/1.1的分层设计的一部分,一旦规范被分区(正在进行中),它将再次变得清晰.
罗伊....
是的,您可以使用GET发送请求正文,但它不应该有任何意义.如果你通过在服务器上解析它并根据其内容更改响应来赋予它意义,那么你忽略了HTTP/1.1规范第4.3节中的这个建议:
[...]如果请求方法不包含实体主体的定义语义,那么在处理请求时应该忽略消息体.
和HTTP/1.1规范中的GET方法的描述,第9.3节:
GET方法意味着检索Request-URI标识的任何信息([...]).
其中声明请求主体不是GET请求中资源标识的一部分,只是请求URI.
更新 引用为"HTTP/1.1规范"的RFC2616现已过时.2014年,它被RFC 7230-7237取代.引用"处理请求时应该忽略消息体"已被删除.它现在只是"请求消息框架独立于方法语义,即使该方法没有定义消息体的任何用途"第二个引用"GET方法意味着检索任何信息......由Request-URI标识"被删除了. - 来自评论
虽然你可以这样做,但是在HTTP规范没有明确排除的范围内,我建议避免使用它只是因为人们不希望事情以这种方式工作.HTTP请求链中有许多阶段,虽然它们"大部分"符合HTTP规范,但您唯一可以肯定的是它们的行为与Web浏览器一样.(我在想透明代理,加速器,A/V工具包等等)
这就是" 稳健性原则 " 背后的精神,大致是"你接受的是自由的,你所发送的是保守的",你不想在没有充分理由的情况下突破规范的界限.
但是,如果你有充分的理由,那就去吧.
如果您尝试利用缓存,则可能会遇到问题.代理人不会在GET主体中查看参数是否会对响应产生影响.
restclient和REST控制台都不支持这个,但卷曲确实如此.
的HTTP规范说,在第4.3节
如果请求方法的规范(第5.1.1节)不允许在请求中发送实体主体,则消息主体不得包含在请求中.
第5.1.1节将我们重定向到第9.x节的各种方法.他们都没有明确禁止包含消息体.然而...
第5.2节说
通过检查Request-URI和Host头字段来确定Internet请求标识的确切资源.
而9.3节说
GET方法意味着检索由Request-URI标识的任何信息(以实体的形式).
这一点一起表明,在处理GET请求时,服务器不需要检查Request-URI和Host头字段之外的任何其他内容.
总之,HTTP规范并不会阻止您使用GET发送消息体,但是如果所有服务器都不支持它,那么它就不会让我感到惊讶.
Elasticsearch接受带有正文的GET请求.这似乎是首选方式:http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/common-options.html#_request_body_in_query_string
某些客户端库(如Ruby驱动程序)可以在开发模式下将cry命令记录到stdout,并且它正在广泛使用此语法.
您尝试实现的目标已经使用更常见的方法完成了很长时间,并且不依赖于使用GET的有效负载.
您可以简单地构建特定的搜索媒体类型,或者如果您想要更加RESTful,请使用类似OpenSearch的内容,并将请求POST到服务器指示的URI,比如/ search.然后,服务器可以生成搜索结果或构建最终URI并使用303重定向.
这具有遵循传统PRG方法的优点,有助于缓存中介缓存结果等.
也就是说,无论如何,URI都会被编码为非ASCII的任何东西,application/x-www-form-urlencoded和multipart/form-data也是如此.如果您的目的是支持ReSTful场景,我建议使用此而不是创建另一种自定义json格式.
你可以发送一个GET与一个正文或发送一个POST并放弃RESTish宗教信仰(它不是那么糟糕,5年前只有一个信仰成员 - 他的评论在上面联系).
也不是很好的决定,但发送GET机构可能会阻止某些客户端和某些服务器出现问题.
执行POST可能会遇到一些RESTish框架的障碍.
Julian Reschke建议使用像"SEARCH"这样的非标准HTTP标头,这可能是一个优雅的解决方案,除了它更不可能得到支持.
列出能够和不能完成上述各项操作的客户可能是最有效的.
无法通过身体发送GET的客户(我知道):
XmlHTTPRequest Fiddler
可以使用正文发送GET的客户端:
大多数浏览器
可以从GET检索正文的服务器和库:
阿帕奇
PHP
从GET中剥离主体的服务器(和代理):
?
哪个服务器会忽略它? - fijiaaron 2012年8月30日21:27
例如Google比忽略它更糟糕,它会认为这是一个错误!
使用简单的netcat自己尝试:
$ netcat www.google.com 80 GET / HTTP/1.1 Host: www.google.com Content-length: 6 1234
(1234内容后跟CR-LF,所以总共6个字节)
你会得到:
HTTP/1.1 400 Bad Request Server: GFE/2.0 (....) Error 400 (Bad Request) 400. That’s an error. Your client has issued a malformed or illegal request. That’s all we know.
您还可以从Bing,Apple等获得400 Bad Request,这是由AkamaiGhost提供的.
所以我不建议将GET请求与body实体一起使用.
从RFC 2616,第4.3节 "消息正文":
服务器应该在任何请求上读取和转发消息体; 如果请求方法不包含实体主体的定义语义,那么在处理请求时应该忽略消息主体.
也就是说,服务器应始终从网络中读取任何提供的请求主体(检查Content-Length或读取分块的主体等).此外,代理应转发他们收到的任何此类请求正文.然后,如果RFC为给定方法定义了主体的语义,则服务器实际上可以使用请求主体来生成响应.但是,如果RFC 没有为正文定义语义,那么服务器应该忽略它.
这符合上面Fielding的引用.
第9.3节 "GET"描述了GET方法的语义,并没有提到请求体.因此,服务器应该忽略它在GET请求上收到的任何请求主体.
我把这个问题提交给了IETF HTTP WG.Roy Fielding的评论(1998年http/1.1文件的作者)就是这样
"......除了解析和丢弃该机构以外,还可以执行任何其他操作"
RFC 7213(HTTPbis)声明:
"GET请求消息中的有效负载没有定义的语义;"
现在似乎很清楚,目的是禁止GET请求体上的语义,这意味着请求体不能用于影响结果.
如果你在GET上包含一个正文,那里有代理肯定会以各种方式破坏你的请求.
总而言之,不要这样做.
根据XMLHttpRequest,它无效.从标准:
4.5.6
send()
方法client . send([body = null])发起请求.可选参数提供请求正文.如果请求方法是
GET
或,则忽略该参数HEAD
.
InvalidStateError
如果未打开任何状态或send()
设置了标志,则引发异常 .该方法必须运行以下步骤:
send(body)
如果未打开状态,则抛出
InvalidStateError
异常.如果
send()
设置了标志,则抛出InvalidStateError
异常.如果请求方法是
GET
或HEAD
,则将body设置为null.如果body为null,请转到下一步.
虽然,我认为不应该因为GET请求可能需要大量的内容.
因此,如果您依赖浏览器的XMLHttpRequest,它很可能无法正常工作.
如果您真的想要将可缓存的JSON/XML主体发送到Web应用程序,那么放置数据的唯一合理位置是使用RFC4648编码的查询字符串:Base 64 Encoding with URL and Filename Safe Alphabet.当然你可以只是urlencode JSON并将put放在URL param的值中,但Base64会给出较小的结果.请记住,有URL大小限制,请参阅不同浏览器中URL的最大长度是多少?.
您可能认为Base64的填充=
字符可能对URL的param值不利,但似乎没有 - 请参阅此讨论:http://mail.python.org/pipermail/python-bugs-list/2007-February/037195.html.但是,您不应该将编码数据放在没有参数名称的情况下,因为带有填充的编码字符串将被解释为具有空值的参数键.我会用类似的东西?_b64=
.
我不建议这样做,这违反了标准做法,并且没有提供太多回报。您想保留正文,而不是选项。