带请求正文的HTTP GET

问题描述 投票:1691回答:18

我正在为您的应用程序开发一个新的RESTful Web服务。

在某些实体上执行GET时,客户端可以请求实体的内容。如果他们想要添加一些参数(例如排序列表),他们可以在查询字符串中添加这些参数。

或者,我希望人们能够在请求正文中指定这些参数。 HTTP/1.1似乎没有明确禁止这一点。这将允许他们指定更多信息,可以更容易指定复杂的XML请求。

我的问题:

  • 这完全是一个好主意吗?
  • HTTP客户端在GET请求中使用请求主体会有问题吗?

http://tools.ietf.org/html/rfc2616

rest http http-get
18个回答
1445
投票

Roy Fielding's comment about including a body with a GET request

是。换句话说,任何HTTP请求消息都允许包含消息体,因此必须解析消息。但是,GET的服务器语义受到限制,使得正文(如果有的话)对请求没有语义含义。解析的要求与方法语义的要求是分开的。

所以,是的,你可以使用GET发送一个正文,不,这样做永远不会有用。

这是HTTP / 1.1的分层设计的一部分,一旦规范被分区(正在进行中),它​​将再次变得清晰。

罗伊....

是的,您可以使用GET发送请求正文,但它不应该有任何意义。如果您通过在服务器上解析它并根据其内容更改响应来赋予它意义,那么您将忽略the HTTP/1.1 spec, section 4.3中的此建议:

[...]如果请求方法不包含entity-body的定义语义,则在处理请求时将忽略message-body SHOULD

the HTTP/1.1 spec, section 9.3中的GET方法的描述:

GET方法意味着检索Request-URI标识的任何信息([...])。

其中声明请求主体不是GET请求中资源标识的一部分,只是请求URI。

更新引用为“HTTP / 1.1规范”的RFC2616现已过时。 2014年,它被RFC 7230-7237取代。引用“处理请求时应该忽略消息体”已被删除。它现在只是“请求消息框架独立于方法语义,即使该方法没有定义消息体的任何用途”第二个引用“GET方法意味着检索任何信息......由Request-URI标识”被删除了。 - 来自评论


14
投票

我把这个问题提交给了IETF HTTP WG。 Roy Fielding的评论(1998年http / 1.1文件的作者)就是这样

“......除了解析和丢弃该机构以外,还可以执行任何其他操作”

RFC 7213(HTTPbis)声明:

“GET请求消息中的有效负载没有定义的语义;”

现在似乎很清楚,目的是禁止GET请求体上的语义,这意味着请求体不能用于影响结果。

如果你在GET上包含一个正文,那里有代理肯定会以各种方式破坏你的请求。

总而言之,不要这样做。


7
投票

如果您真的想将可缓存的JSON / XML主体发送到Web应用程序,那么放置数据的唯一合理位置是使用RFC4648: Base 64 Encoding with URL and Filename Safe Alphabet编码的查询字符串。当然你可以只是urlencode JSON并将put放在URL param的值中,但Base64会给出较小的结果。请记住,有URL大小限制,请参阅What is the maximum length of a URL in different browsers?

您可能认为Base64的填充=字符可能对URL的参数值不好,但似乎没有 - 请参阅此讨论:http://mail.python.org/pipermail/python-bugs-list/2007-February/037195.html。但是,您不应该将编码数据放在没有参数名称的情况下,因为带有填充的编码字符串将被解释为具有空值的参数键。我会用像?_b64=<encodeddata>这样的东西。


7
投票

根据XMLHttpRequest,它无效。来自standard

4.5.6 The send() method

client . send([body = null])

发起请求。可选参数提供请求正文。如果请求方法是GETHEAD,则忽略该参数。

如果未打开任何状态或设置了InvalidStateError标志,则引发send()异常。

send(body)方法必须运行以下步骤:

  1. 如果未打开状态,则抛出InvalidStateError异常。
  2. 如果设置了send()标志,则抛出InvalidStateError异常。
  3. 如果请求方法是GETHEAD,请将body设置为null。
  4. 如果body为null,请转到下一步。

虽然,我认为不应该因为GET请求可能需要大量的内容。

因此,如果您依赖浏览器的XMLHttpRequest,它很可能无法正常工作。


5
投票

例如,它适用于Curl,Apache和PHP。

PHP文件:

<?php
echo $_SERVER['REQUEST_METHOD'] . PHP_EOL;
echo file_get_contents('php://input') . PHP_EOL;

控制台命令:

$ curl -X GET -H "Content-Type: application/json" -d '{"the": "body"}' 'http://localhost/test/get.php'

输出:

GET
{"the": "body"}

4
投票

那么不合格的base64编码头呢? “SOMETHINGAPP-PARAMS:sdfSD45fdg45 / AS”

长度限制hm。你不能让你的POST处理区分意义吗?如果你想要简单的参数,如排序,我不明白为什么这将是一个问题。我想你确实很担心。


4
投票

我很不高兴REST协议不支持OOP和Get方法就是证据。作为解决方案,您可以将DTO序列化为JSON,然后创建查询字符串。在服务器端,您可以将查询字符串反序列化为DTO。

看看:

基于消息的方法可以帮助您解决Get方法限制。您可以像请求正文一样发送任何DTO

Nelibur web service framework provides functionality which you can use

var client = new JsonServiceClient(Settings.Default.ServiceAddress);
var request = new GetClientRequest
    {
        Id = new Guid("2217239b0e-b35b-4d32-95c7-5db43e2bd573")
    };
var response = client.Get<GetClientRequest, ClientResponse>(request);

as you can see, the GetClientRequest was encoded to the following query string

http://localhost/clients/GetWithResponse?type=GetClientRequest&data=%7B%22Id%22:%2217239b0e-b35b-4d32-95c7-5db43e2bd573%22%7D

4
投票

恕我直言,你可以发送JSON编码(即.encodeURIComponent)在URL,这样你不违反HTTP规格,让你的JSON到服务器。


3
投票

我不建议这样做,它违背了标准做法,并没有提供那么多回报。你想保持身体的内容,而不是选择。


0
投票

您有一个选项列表,它比使用GET请求体更好。

假设您有每个类别的类别和项目。两者都由id(“catid”/“itemid”为此示例)标识。您希望根据特定“顺序”中的另一个参数“sortby”进行排序。您想传递“sortby”和“order”的参数:

您可以:

  1. 使用查询字符串,例如example.com/category/{catid}/item/{itemid}?sortby=itemname&order=asc
  2. 对路径使用mod_rewrite(或类似):example.com/category/{catid}/item/{itemid}/{sortby}/{order}
  3. 使用随请求传递的单个HTTP标头
  4. 使用不同的方法,例如POST,检索资源。

所有这些都有它们的缺点,但远比使用身体的GET要好得多。


260
投票

虽然你可以这样做,但是在HTTP规范没有明确排除的范围内,我建议避免使用它只是因为人们不希望事情以这种方式工作。 HTTP请求链中有许多阶段,虽然它们“大部分”符合HTTP规范,但您唯一可以肯定的是它们的行为与Web浏览器一样。 (我在想透明代理,加速器,A / V工具包等等)

这就是Robustness Principle背后的精神,大致是“你接受的是自由主义,你所发送的是保守的”,你不想在没有充分理由的情况下突破规范的界限。

但是,如果你有充分的理由,那就去吧。


130
投票

如果您尝试利用缓存,则可能会遇到问题。代理人不会在GET主体中查看参数是否会对响应产生影响。


66
投票

restclientREST console都不支持这个,但卷曲确实如此。

HTTP specification在4.3节中说

如果请求方法的规范(第5.1.1节)不允许在请求中发送实体主体,则消息主体不得包含在请求中。

Section 5.1.1将我们重定向到第9.x节的各种方法。他们都没有明确禁止包含消息体。然而...

Section 5.2

通过检查Request-URI和Host头字段来确定Internet请求标识的确切资源。

Section 9.3

GET方法意味着检索由Request-URI标识的任何信息(以实体的形式)。

这一点一起表明,在处理GET请求时,服务器不需要检查Request-URI和Host头字段之外的任何其他内容。

总之,HTTP规范并不会阻止您使用GET发送消息体,但是如果所有服务器都不支持它,那么它就不会让我感到惊讶。


44
投票

Elasticsearch接受带有正文的GET请求。它甚至似乎是首选方式:http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/common-options.html#_request_body_in_query_string

某些客户端库(如Ruby驱动程序)可以在开发模式下将cry命令记录到stdout,并且它正在广泛使用此语法。


29
投票

您尝试实现的目标已经使用更常见的方法完成了很长时间,并且不依赖于使用GET的有效负载。

您可以简单地构建特定的搜索媒体类型,或者如果您想要更加RESTful,请使用类似OpenSearch的内容,并将请求POST到服务器指示的URI,比如/ search。然后,服务器可以生成搜索结果或构建最终URI并使用303重定向。

这具有遵循传统PRG方法的优点,有助于缓存中介缓存结果等。

也就是说,无论如何,URI都会被编码为非ASCII的任何东西,application / x-www-form-urlencoded和multipart / form-data也是如此。如果您的目的是支持ReSTful场景,我建议使用此而不是创建另一种自定义json格式。


21
投票

你可以发送一个GET与一个正文或发送一个POST并放弃RESTish宗教信仰(它不是那么糟糕,5年前只有一个信仰成员 - 他的评论在上面联系)。

也不是很好的决定,但发送GET机构可能会阻止某些客户端和某些服务器出现问题。

执行POST可能会遇到一些RESTish框架的障碍。

Julian Reschke建议使用像“SEARCH”这样的非标准HTTP标头,这可能是一个优雅的解决方案,除了它更不可能得到支持。

列出能够和不能完成上述各项操作的客户可能是最有效的。

无法通过身体发送GET的客户(我知道):

  • XmlHTTPRequest Fiddler

可以使用正文发送GET的客户端:

  • 大多数浏览器

可以从GET检索正文的服务器和库:

  • 阿帕奇
  • PHP

从GET中剥离主体的服务器(和代理):

  • ?

21
投票

哪个服务器会忽略它? - 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实体一起使用。


17
投票

来自RFC 2616, section 4.3,“消息体”:

服务器应该在任何请求上读取和转发消息体;如果请求方法不包含实体主体的定义语义,那么在处理请求时应该忽略消息主体。

也就是说,服务器应始终从网络中读取任何提供的请求主体(检查Content-Length或读取分块的主体等)。此外,代理应转发他们收到的任何此类请求正文。然后,如果RFC为给定方法定义了主体的语义,则服务器实际上可以使用请求主体来生成响应。但是,如果RFC没有为正文定义语义,那么服务器应该忽略它。

这符合上面Fielding的引用。

Section 9.3,“GET”,描述了GET方法的语义,并没有提到请求体。因此,服务器应该忽略它在GET请求上收到的任何请求主体。

© www.soinside.com 2019 - 2024. All rights reserved.