当需要一些复杂的参数时,设计 HTTP 请求的最佳方法是什么?

发布于 2024-07-30 18:21:05 字数 3130 浏览 7 评论 0 原文

我正在编写一些网络服务,并且我正在努力尽可能保持 RESTful。 我使用在 IIS/ASP.NET/SharePoint 内运行的 HTTPHandler 托管这些 Web 服务。

我的大多数服务都需要 HTTP GET。 我有两个只是返回一些数据(即查询)并且将是幂等 ,但参数可能有些复杂。 它们都可能在服务参数中包含至少 URL 的 PATH 部分不允许的字符。

使用 IIS、ASP.NET 和 SharePoint,我发现 URL 路径中的以下字符甚至无法到达我的 HttpHandler,即使 Url 编码也是如此(请求会崩溃,我对此没有任何简单的控制) :

  • % (%25)
  • & (%26)
  • * (%2a,但未进行 URL 编码)
  • + (%2b)
  • : (%3a)
  • < (%3c)
  • <块引用>

    (%3e)

以下字符已到达我的 HttpHandler,但即使 Url 编码,UriTemplate 也无法正确处理它们:

  • (%23)

  • 。 (%2e,但没有对 Url 进行编码;UriTemplate 删除了“.”(如果它是 / 之前的最后一个字符)
  • ? (%3f)
  • / (%2f - 即使 UrlEncoded,UriTemplate 也会因明显原因而失败)
  • \ (%5c)

所以,我已经有点彻底了,但我需要测试查询字符串中的这些 url 编码字符。 看来这在很大程度上是有效的。

在我的一项服务中,作为参数的特殊字符在语义上是查询/过滤器的一部分(实际上是搜索服务的搜索词),但在另一项服务中,它们实际上并不是查询/过滤器的一部分,因此理想情况下它们是查询/过滤器的一部分路径而不是查询字符串。

我的问题是,什么选择最好? 以下是我所知道的一些内容:

  1. 使用 HTTP GET 和查询字符串。任何可能使用特殊字符的内容都应该在查询字符串和 Url 中进行编码。 这就是我的倾向,但我担心极长的查询字符串(IE 有 2083 的限制)

  2. 在路径中使用 HTTP GET 和 base64 编码。 使用修改了 URL 的 Base64,用于可能使用特殊字符的任何参数,并将其保留为如果首选路径。 我已经尝试过了,它确实有效,但有点难看。 仍然担心极长的查询字符串。

  3. 使用 HTTP POST 和消息正文。任何可能使用特殊字符的内容都应位于请求正文中。 似乎是一个不错的解决方案,但帖子被理解为不是幂等< /a> 和(我认为)通常用于更改(而此处没有发生更改)。

  4. 使用 HTTP GET 和消息正文。 任何可能使用特殊字符的内容都应该位于请求正文中。 根据 SO: HTTP GET with request bodyRoy Fielding。

  5. 根据请求的大小,使用 #3 和上面的 #1 或 #2 的组合。

    根据请求的大小,使用 #3 和上面
  6. 其他???

请注意,在某些情况下,我可能能够进行更改以防止出现特殊字符(我可能会这样做),但我无法在所有情况下都这样做。


关于 URI 长度,RFC2616 Sec3.2.1 表示以下内容:

HTTP 协议对 URI 的长度没有任何先验的限制。 服务器必须能够处理它们所服务的任何资源的 URI,并且如果它们提供可以生成此类 URI 的基于 GET 的表单,则应该能够处理无限长度的 URI。 如果 URI 长于服务器可以处理的长度,服务器应该返回 414(请求 URI 太长)状态(请参阅第 10.4.15 节)。

  Note: Servers ought to be cautious about depending on URI lengths
  above 255 bytes, because some older client or proxy
  implementations might not properly support these lengths.

此外,Internet Explorer 中的最大 URL 长度为 2,083 个字符

相关:如何在 REST 中传递复杂查询?

I have some web services that I am writing and I am trying to be as RESTful as possible. I am hosting these web services using a HTTPHandler running inside of IIS/ASP.NET/SharePoint.

Most of my services expect a HTTP GET. I have two of these that are simply returning some data (i.e., a query) and will be Idempotent, but the parameters may be somewhat complex. Both of them could include characters in the parameters of the service that are not allowed for at least the PATH portion of the URL.

Using IIS, ASP.NET, and SharePoint I have found that the following characters in the URL path don't even make it to my HttpHandler even if Url encoded (the request blows up and I don't have any easy control over this):

  • % (%25)
  • & (%26)
  • * (%2a, but didn't Url encode)
  • + (%2b)
  • : (%3a)
  • < (%3c)
  • (%3e)

The following characters made it to my HttpHandler, but the UriTemplate could not handle them properly even if Url encoded:

  • (%23)

  • . (%2e, but didn't Url encode; UriTemplate removed the "." if is is the last character before a /)
  • ? (%3f)
  • / (%2f - UriTemplate fails for obvious reasons even if UrlEncoded)
  • \ (%5c)

So, I've been somewhat thorough, but I need to test these url encoded characters in the query string. It appears that this will work for the most part there.

In one of my services, the special characters that are a parameter are semantically part of a query/filter (actually search terms for a search service), but in another they are not really part of a query/filter so ideally they are part of the path and not the query string.

My question is, what option is best? Here are some I know of:

  1. Use HTTP GET and query string. Anything that may use special characters should be on the query string and Url Encoded. This is where I am leaning, but I am concerned about extremely long query strings (IE has a 2083 limit)

  2. Use HTTP GET and base64 encoding within path. Use a Modified Base64 for URL for any parameters that might use special characters and keep them as part of the path if preferred. I have tried this and it works, but it is kind of ugly. Still a concern about extremely long query strings.

  3. Use HTTP POST and message body. Anything that may use special characters should be in the body of the request. Seems like a decent solution, but posts are understood to not be Idempotent and (I thought) are generally meant for changes (whereas no change is occurring here).

  4. Use HTTP GET and message body. Anything that may use special characters should be in the body of the request. This seems like a bad idea according to SO: HTTP GET with request body and Roy Fielding.

  5. Use a combination of #3 and either #1 or #2 above depending on how large the request can be.

  6. Other???

Note that in some cases I may be able to change things around to prevent special characters (and I may do that), but I won't be able to do this in all cases.


Regarding URI length, RFC2616 Sec3.2.1 says the following:

The HTTP protocol does not place any a priori limit on the length of a URI. Servers MUST be able to handle the URI of any resource they serve, and SHOULD be able to handle URIs of unbounded length if they provide GET-based forms that could generate such URIs. A server SHOULD return 414 (Request-URI Too Long) status if a URI is longer than the server can handle (see section 10.4.15).

  Note: Servers ought to be cautious about depending on URI lengths
  above 255 bytes, because some older client or proxy
  implementations might not properly support these lengths.

In addition the Maximum URL length is 2,083 characters in Internet Explorer.

Related: How to pass complex queries in REST?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

深者入戏 2024-08-06 18:21:06

Roy Fielding 可能会批准在此使用 POST情况,但你得问他。

一般来说,大多数应用程序
涉及用户提供的数据
提供给服务器的不安全。
唯一的例外是信息采用以下形式:
通用查询参数,对于
其中之间存在权衡
GET 和 POST 通常涉及
参数内容的大小
.
GET
仅适用于这些情况
其中参数可以表示
作为有意义的 URI。

Roy Fielding would likely approve of using POST in this situation, but you'd have to ask him.

In general, most applications that
involve user-supplied data being
supplied to the server are not safe.
The only exception is when the information is in the form of
generalized query parameters, for
which there is a trade-off between
GET and POST that usually involves the
size of the parameter content
.
GET
is only desirable for those cases
where the parameters can be expressed
as a meaningful URI.

温柔戏命师 2024-08-06 18:21:06

base64 应该可以做到。 否则使用标准的%符号。

base64 should do it. other wise use the %-sign which is standard.

一身软味 2024-08-06 18:21:06

考虑支持:
- 带有短查询字符串的 GET 请求
- 将长查询字符串的 POST 请求放入正文和 X-HTTP-Method-Override: GET (https:/ /en.wikipedia.org/wiki/List_of_HTTP_header_fields

注意不要将批量创建新订单的“POST /orders”和“POST /orders”与“X-HTTP-Method-Override: GET”混合在一起这就是对秩序的探索。

Consider supporting :
- GET requests with short query string
- POST requests with long query string into the body and X-HTTP-Method-Override: GET (https://en.wikipedia.org/wiki/List_of_HTTP_header_fields)

Beware not mixing "POST /orders" that is a bulk creation of new orders and "POST /orders" with a "X-HTTP-Method-Override: GET" that is a search of order.

无声无音无过去 2024-08-06 18:21:05

没有完美的方法可以做到这一点。

正确的 HTTP/REST 方法是使用 GET 并将 URL 中的所有参数作为查询参数。 您已经发现了此方法的两个实际问题:

  1. 即使 URL 已编码,您的服务器软件也无法正确地将某些字符传递给您。 实际上,这让我感到惊讶,您应该更仔细地看看发生了什么,您甚至无法通过 URL 获得 %。 您的框架是否允许您对 PATH_INFO 或其他未处理的字符进行原始访问? 这可能会给你一个解决方法。
  2. 您的查询字符串可能太长。 您提到了 MSIE 中的 2083 字节限制。 这对您来说可能是也可能不是实际问题,具体取决于 MSIE 是否是您 API 的客户端。 (即:通过 Javascript 调用 JSON API)。 但根据我的经验,很长的 URL 最终会在几个地方神秘地中断; 沿路径的代理缓存,甚至是状态防火墙。 如果您对客户端和网络路径有绝对的控制权,您可能可以忍受长 URL 的危险。 如果它是一个公共API,那就算了。

希望您可以在您的环境中使简单的 GET 工作起来。 您甚至可能需要考虑重构 API 以缩小查询数据。

但是如果你不能让 GET 工作怎么办? 您提出了几种替代方案。 我会立即解雇其中两个。 不要将内容放入 GET 请求正文中; 如果您尝试这样做,太多的软件将会崩溃,而且无论如何,它违反了您试图捕捉的 REST 精神。 而且我不会使用 base64 编码。 它可能会帮助您解决问题 1,即您的服务器无法正确处理 URL 中的某些字符。 但如果应用错误,它实际上会使您的 URL 更长,而不是更短,从而使问题 2 更加复杂。即使您正确执行了 Base64 并包含一些压缩,它也不会使 URL 显着变短,并且会使客户端变得更加复杂。

最实用的解决方案可能是选项 3,即 HTTP POST。 这并不宁静; 您应该使用 GET 进行只读查询。 而且您将失去 REST 方法的一些优势,比如缓存 GET 等。 另一方面,它可以与各种互联网基础设施和软件库一起正确、简单地工作。 然后,您可以通过 multipart/form-data 编码、JSON 或 XML 在 POST 正文中传递任意数量的数据。 (我使用 SOAP 构建了两个主要的公共 Web 服务,它只是 POST 上的 XML。它很丑陋,而且不是 RESTful,但它确实可靠地工作。)

REST 是一个很棒的设计范例。 这是一个指导方针。 如果它不适合您的应用程序,请不要觉得您需要坚持使用它。 HTTP 不擅长通过 GET 将大量数据传递到服务器。 如果您需要巨大的查询参数,请执行其他操作。

There's no perfect way to do this.

The correct HTTP/REST way would be to use a GET and have all your parameters in the URL as query arguments. You've identified two practical problems with this approach

  1. Your server software is not correctly passing some characters to you, even if URL encoded. That surprises me, actually, and you should look more closely at what's going on that you can't even get a % through the URL. Does your framework give you raw access to PATH_INFO or otherwise unprocessed characters? That may give you a workaround.
  2. Your query strings may be too long. You mention the 2083 byte limit in MSIE. That may or may not be a practical problem for you, depending on whether MSIE is a client of your API. (Ie: via Javascript making calls to a JSON API). But in my experience very long URLs will end up breaking mysteriously in several places; proxy caches along the path, even a stateful firewall. If you have absolute control over the clients and network path you can probably live with the dangers of long URLs. If it's a public API, forget it.

Hopefully you can make the straightforward GET work in your environment. You may even want to consider refactoring your API to make the query data smaller.

But what if you can't make the GET work? You propose several alternatives. I would immediately dismiss two of them. Don't put content in the GET request body; too much software will break if you try that, and anyway it violates the very REST spirit you're trying to capture. And I wouldn't use base64 encoding. It may help you work around problem 1, your server not handling some characters in URLs right. But if applied wrong it will actually make your URLs longer, not shorter, compounding problem 2. Even if you do base64 right and include some compression it won't make URLs significantly shorter, and will make the client much more complicated.

Your most practical solution is probably option 3, an HTTP POST. This isn't RESTful; you should be using GETs for read-only queries. And you'll lose some advantages of the REST approach with caching of GETs and the like. On the other hand it will work correctly, and simply, with a large variety of Internet infrastructure and software libraries. You can then pass as much data you want in the POST body either via multipart/form-data encoding, JSON, or XML. (I've built two major public web services using SOAP, which is just XML on POSTs. It's ugly and not RESTful, but it does work reliably.)

REST is a great design paradigm. It's a guideline. If it doesn't fit your app, don't feel you need to stick with it. HTTP is not good at passing large amounts of data to the server with a GET. If you need have giant query parameters, do something else.

酒绊 2024-08-06 18:21:05

如果查询太大而无法放入 URI,请将查询转换为资源(例如已保存的搜索)。
我为一家酒店的预订系统开发了一个 Restful API; 搜索查询有太多参数(首选项、房间列表等),因此我将其转换为发布到服务器的资源。 然后,服务器回复一个唯一标识搜索的 URI,其中主体是发布的查询及其结果:

POST http://hotels.xyz/searches
body <search><query>...</query></search>

响应

201 Created - Location: http://hotels.xyz/searches/someID
Body <search><query>...</query><results>...</results></search>

If the query is too big to go in the URI, turn your query into a resource (like a saved search).
I worked on a restful API for a hotel's booking system; the search query had too many params (preferences, rooming list...etc) so I turned it into a resource that I POST to the server. The server then replies with a URI uniquely identifing the search which body is the posted query + its results:

POST http://hotels.xyz/searches
body <search><query>...</query></search>

Response

201 Created - Location: http://hotels.xyz/searches/someID
Body <search><query>...</query><results>...</results></search>
戈亓 2024-08-06 18:21:05

我建议您阅读 HTTP 1.1 规范,尤其是 3.2 统一资源标识符9.1.1 安全方法。 这些有望回答您的问题。


以下是一些附加信息:

I recommend you to read the HTTP 1.1 specification, especially the sections 3.2 Uniform Resource Identifiers and 9.1.1 Safe Methods. Those will hopefully answer your question.


Here’s some additional information:

貪欢 2024-08-06 18:21:05

如果没有其他解决办法,请使用自定义 HTTP 标头和 HTTP GET。 几乎所有客户端都可以设置 HTTP 标头。

一般来说,最好在查询字符串中使用 URL 参数。 URL参数过多说明需要拆分成更细粒度的服务。

Use custom HTTP headers with HTTP GET if nothing else works out. HTTP headers can be set by nearly all clients.

Generally it is best to use URL parameters in the query string. Too many URL parameters indicates that you need to split into more fine-granular services.

残疾 2024-08-06 18:21:05

我的目标是 HTTP POST。 当它到达 PHP(或您正在使用的任何一个)时,它会被很好地标记化,并且它没有其他版本的大小限制。

I'd aim for HTTP POST. It's nicely tokenized when it gets to PHP (or whichever you're using) and it doesn't have the size limits the others have.

水水月牙 2024-08-06 18:21:05

如果您在服务器上生成这些长网址,则可以利用路径信息压缩。

因此,如果你有类似 /?param1=bla-bla¶m2=bla-bla 的内容,你只需压缩该参数并使 url 看起来像 /?query=ASsadcnfAFFASFscnsdlc

当你收到这样的请求时,你只需解压缩它们并解析参数字符串

If you are generating these long urls on server, you can make use of compression for path info.

So if you have something like /?param1=bla-bla¶m2=bla-bla you just compress that parameters and make url looks like /?query=ASsadcnfAFFASFscnsdlc

When you get such request, you just decompress them and parse parameter string

素年丶 2024-08-06 18:21:05

您应该使用 HTTP GET 请求将参数放置在查询字符串中。 一些较旧的 Web 浏览器中的限制不是问题,因为在 Web 浏览器中浏览 API 的唯一人可能是开发人员(或至少是技术人员)。

请记住,客户端应用程序不应该操纵 API 提供给它们的 URL。 URL 对客户端来说是不透明的标识符,仅用于将客户端引导至可以找到特定资源的位置。

如果出于某种原因这是不可能的,我将使用 POST 请求,并将参数以表单编码到正文中。 它不会完全是 RESTful,但假设您的资源设计正确,对客户端代码的影响应该很小。

You should place the parameters in the query string, using an HTTP GET request. Limits in some older web browsers are not a concern, because the only people browsing through an API in a web browser are likely to be developers (or at least technical).

Remember that client applications should not be manipulating the URLs your API provides them. URLs are opaque identifiers to the clients, used only for directing them to where particular resources may be found.

If this is not possible for whatever reason, I would use a POST request with the parameters form-encoded into the body. It won't be entirely RESTful, but assuming your resources are designed properly the impact on client code should be minimal.

烏雲後面有陽光 2024-08-06 18:21:05

我肯定会从你开始的地方开始:URL 缩短。 我会尝试缩短参数名称 (?a=XXX;b=YYY;c=zzz); 将整个查询重新编码为 Base64; GZip Base64; 霍夫曼编码 GZip; ... 不惜一切代价。 一旦我意识到缩短并不适用于所有情况(你已经有了一些可以无限期添加的动态过滤器创建系统,或者w/e),那么你必须承认也许尝试做所有事情在单个请求中可能不起作用...

我不会建议您使用拆分参数抛出多个 GET 并尝试以这种方式跟踪请求...

我可以建议的唯一“强大”方法是存储/设置一个请求 (POST) 中请求的查询字符串,并让它返回一个固定大小的 ID(或 guid),用于标识数据存储 (filterID) 中的请求参数位置,然后使用 filterID 令牌而不是完整的 GET 请求过滤查询字符串值。 这将允许各种巧妙的事情,例如基于filterID缓存响应,这样您就可以(理论上)稍后重用相同的过滤器(而不是手动重新输入它们,只需将“标签”与过滤器主体一起保存并从最后 5 个过滤器(按标签),或者至少将它们与您的数据一起存储,以便每次刷新页面时都不会重新发送整个过滤器请求。

I'd definitely have started where you started: URL shortening. I'd try to shorten the parameter names (?a=XXX;b=YYY;c=zzz); Reencode the entire query to Base64; GZip the Base64; Huffman encode the GZip; ... whatever it takes. Once I got the inkling that shortening won't work for all cases (you've got some dynamic filter-creating system that can be added onto indefinitely, or w/e), then you've got to admit maybe trying to do everything within a single request might not work...

I'm NOT going to suggest you throw multiple GETs with split parameters and try to keep track across requests that way...

The only 'robust' method I CAN suggest is to store/set the requested querystring in one request (POST) and have it return a fixed-sized ID (or guid) that identifies the request parameter location in your data store (filterID), then make the actual GET request using the filterID token instead of the full filter query string value. This will allow all kinds of neat things like cacheing responses based on filterID so you could (in theory) reuse the same filters later (instead of re-entering them by hand, just save a "label" along with the filter body and select from the last 5 filters by label), or at least keep them stored with your data so that each time you refresh the page it's not re-sending the entire filter request.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文