HTTP 协议 qeury 参数的说明

·5min·李岩

大模型就像是一个无所不知的助手,给它的任何任务都可以快速的完成。通过收集一些和大模型交互过程中不符合预期的案例,既可以提升和大模型的协作效率,也可以对自己的知识进行检验。

最近遇到的一个案例是,需要提供一个HTTP服务的接口,同事结合大模型给出的接口定义示例是:

GET /path?a=1&b=2&a=2&a=3

这里的问题,在query参数中出现了a的多次赋值。这个接口很可能是大模型结合代码中实际使用的服务框架给出来的,服务框架中对query参数重复时,可能会返回所有的值而非第一个或者最后一个。本文章就对HTTPquery参数进行说明吧。

协议约定

HTTP的结构在rfc3986#page-16中约定如下所示,query部分,也就是URI?之后、#之前的这部分,则是我们要关注的。但是在[rfc3968]中没有进一步的对qeury的约定。

  foo://example.com:8042/over/there?name=ferret#nose
  \_/   \______________/\_________/ \_________/ \__/
   |           |            |            |        |
scheme     authority       path        query   fragment
   |   _____________________|__
  / \ /                        \
  urn:example:animal:ferret:nose

HTTP/1.1的约定则可以在rfc7230中找到,其中定义的HTTP协议如下的(1)所示。通过对HTTP-message继续拆解,本次我们关注的query则是在request-target部分。

(1)
HTTP-message   = start-line
                 *( header-field CRLF )
                 CRLF
                 [ message-body ]

(2)
start-line     = request-line / status-line

(3)
request-line   = method SP request-target SP HTTP-version CRLF

(4)
+---------+-------------------------------------------------+-------+
| Method  | Description                                     | Sec.  |
+---------+-------------------------------------------------+-------+
| GET     | Transfer a current representation of the target | 4.3.1 |
|         | resource.                                       |       |
| HEAD    | Same as GET, but only transfer the status line  | 4.3.2 |
|         | and header section.                             |       |
| POST    | Perform resource-specific processing on the     | 4.3.3 |
|         | request payload.                                |       |
| PUT     | Replace all current representations of the      | 4.3.4 |
|         | target resource with the request payload.       |       |
| DELETE  | Remove all current representations of the       | 4.3.5 |
|         | target resource.                                |       |
| CONNECT | Establish a tunnel to the server identified by  | 4.3.6 |
|         | the target resource.                            |       |
| OPTIONS | Describe the communication options for the      | 4.3.7 |
|         | target resource.                                |       |
| TRACE   | Perform a message loop-back test along the path | 4.3.8 |
|         | to the target resource.                         |       |
+---------+-------------------------------------------------+-------+

(5)
request-target = origin-form
               / absolute-form
               / authority-form
               / asterisk-form
               
(6)
origin-form    = absolute-path [ "?" query ]

请求 http://www.example.org/where?q=now 的 HTTP-message 为:
GET /where?q=now HTTP/1.1
Host: www.example.org

request-target部分在定义在rfc7230#5.3,这里列举了四种request-target类型。一般业务使用时填充的是origin-form,在设置了代理的情况下会使用absolute-form

这里的问题是,HTTP原始约定中没有对query的格式进行约定。query的具体解析方式依赖于http-server的具体实现,这就会产生一些细微的差异。

http-server 实现

通过大模型的汇总,常见的处理模式如下:

处理方式示例典型框架
最后值优先a=1&a=2a=2PHP(默认)、Express.js
第一值优先a=1&a=2a=1较少见
数组/列表a=1&a=2a=[1, 2]Python、Ruby、Java Spring
合并为字符串a=1&a=2a="1,2"某些旧系统
报错返回 400 Bad Request严格验证的 API

可以看到,具体的语言、框架可能对query中重复参数的处理不同。

影响

这个重复参数的接口约定没有实际使用,我们最终采取了POST+json request body的方式来约定了一版新的接口,使用了更为确定且清晰的约定。假设使用了大模型给出的原始版本,可能客户端的大模型会按照接口的约定来设定请求、服务端的大模型大模型按照接口的约定来进行处理。假设未来对这个服务的接口进行重构,新的语言/框架对于重复参数仅取最后一个值,而客户端感知不到这一变更,这就会产生功能上的混淆。

或许在使用大模型进行编程时,至少需要对接口的约定进行详尽的沟通,或者让大模型判断下潜在的兼容性影响。可能添加一些SKILL会好一些。大模型使用带来的一个影响是代码量的膨胀、服务数量的膨胀,在宏观角度下、大模型的SKILL不完全相同,一些细微的概念或者约定的差异可能就会给系统带来影响。又或者,未来程序员需要维护大模型的统一SKILL,其他的agent则在这一共享SKILL的共识下进行协作。