tRPC 批量请求 URL 过长时如何用 methodOverride 将所有 RPC 调用改为 POST
【免费下载链接】trpc🧙♀️ Move Fast and Break Nothing. End-to-end typesafe APIs made easy.项目地址: https://gitcode.com/GitHub_Trending/tr/trpc
使用httpBatchLink的 tRPC 客户端会把同一时刻的多个并行调用合并进一个 HTTP 请求。对于 query,合并后的输入参数(JSON 序列化)会拼在 URL 的input查询参数里,批处理还会额外带上batch=1。当批量里的操作较多或输入较大时,URL 会随之膨胀,触发413 Payload Too Large、414 URI Too Long、404 Not Found这类 HTTP 错误。tRPC 提供的解决方式是给 link 设置methodOverride: 'POST',让所有 RPC 调用(query 和 mutation)都改走 POST,输入随之从 URL 移入请求体。本文给出客户端与服务端两侧的配置,以及如何验证它已生效。
先确认错误现象:批处理的 URL 里装了哪些东西
按 HTTP RPC Specification 的定义,tRPC 的方法映射与批处理格式是:
| HTTP Method | 对应调用 | 说明 |
|---|---|---|
GET | .query() | 输入以 JSON 序列化后放在 query param,例如myQuery?input=${encodeURIComponent(JSON.stringify(input))} |
POST | .mutation() | 输入放在 POST body |
批处理时,同一 HTTP method 的并行调用会被合并为一个请求:
- 各 procedure 的路径名用逗号(
,)拼在pathname里; - 输入参数放在名为
input的 query param 中,形状为Record<number, unknown>; - 同时必须带
batch=1查询参数; - 若各调用返回状态不同,响应会返回
207 Multi-Status。
也就是说,query 越多、输入越大,?batch=1&input=...这一串就越长。当它导致请求失败时,HTTP Batch Link 文档 给出的两个方向是:用maxURLLength限制单批规模(自动拆成多个请求),或者用methodOverride: 'POST'让输入不再走 URL。本文主路径是后者。
客户端:给 batch link 设置 methodOverride: 'POST'
methodOverride是 HTTPLinkOptions 定义的选项,唯一可取值为'POST':
/** * Send all requests as POSTS requests regardless of the procedure type * The server must separately allow overriding the method. */ methodOverride?: 'POST';在客户端创建 client 时把它加到httpBatchLink的 options 里(来自 httpBatchLink 文档 的示例):
import { createTRPCClient, httpBatchLink } from '@trpc/client'; import type { AppRouter } from './server'; const client = createTRPCClient<AppRouter>({ links: [ httpBatchLink({ url: 'http://localhost:3000', methodOverride: 'POST', // alternatively, you can make all RPC-calls to be called with POST }), ], });设置后,所有 query 和 mutation 都会以 POST 请求发往 tRPC Server。客户端的 URL 构造逻辑也印证了这一点:httpUtils.ts 中只有methodOverride !== 'POST'时才会把input=...追加到 URL,同时请求体(body)会携带 JSON 序列化的输入。因此启用后,URL 只剩路径和batch=1,不再有长度问题。
如果你的客户端没有用批处理,rpc.md 中httpLink的用法同样支持这个选项:
import { createTRPCClient, httpLink } from '@trpc/client'; import type { AppRouter } from './server'; // The client can then specify which HTTP method to use for all queries/mutations const client = createTRPCClient<AppRouter>({ links: [ httpLink({ url: `http://localhost:3000`, methodOverride: 'POST', // all queries and mutations will be sent to the tRPC Server as POST requests. }), ], });服务端:必须单独开启 allowMethodOverride
只改客户端是不够的。httpLink的选项说明里明确写着 “The server must separately allow overriding the method”,服务端必须显式允许客户端覆写 HTTP method。rpc.md 给出的 standalone 适配器示例:
import { initTRPC } from '@trpc/server'; import { createHTTPHandler } from '@trpc/server/adapters/standalone'; const t = initTRPC.create(); const router = t.router({}); // Your server must separately allow the client to override the HTTP method const handler = createHTTPHandler({ router: router, allowMethodOverride: true, });两点来自源码的补充:
allowMethodOverride定义在共用的 handler options 类型中(types.ts),因此其他适配器的createHTTP*入口(如 httpBatchLink 文档 中展示的 standalonecreateHTTPServer,以 options 传入maxBatchSize等参数)同样可以传入该选项;- 在 resolveResponse.ts 中,覆写仅在请求本身就是
POST时生效:const allowMethodOverride = (opts.allowMethodOverride ?? false) && req.method === 'POST'。即服务端只把“POST 打到 query procedure”这类原本不允许的组合放行。
验证配置是否生效
仓库中的 methodOverride.test.ts 提供了三个可直接对照的验证场景,均使用q(query)与m(mutation)两个 procedure:
1. 单个 query 以 POST 发送后能正常返回:
// 客户端 linkOptions: { methodOverride: 'POST' },服务端 allowMethodOverride: true expect( await t.client.q.query({ who: 'test1' }), ).toBe('hello test1');2. 批处理场景下,query 与 mutation 混合的Promise.all全部以 POST 走通(测试中的期望输出,文档示例):
// 客户端使用 httpBatchLink 且 linkOptions: { methodOverride: 'POST' },服务端 allowMethodOverride: true expect( await Promise.all([ t.client.q.query({ who: 'test1' }), t.client.q.query({ who: 'test2' }), t.client.m.mutate({ who: 'test3' }), ]), ).toMatchInlineSnapshot(` Array [ "hello test1", "hello test2", "hello test3", ] `);3. 反向验证:服务端未开启allowMethodOverride时,POST 打到 query procedure 会被拒绝。该场景下客户端收到的错误信息为(测试中的期望输出):
[TRPCClientError: Unsupported POST-request to query procedure at path "q"]如果你在启用methodOverride: 'POST'后看到了这条Unsupported POST-request to query procedure错误,说明服务端漏配了allowMethodOverride: true,回到上一步检查即可。
限制与可选分支
- 选项取值固定:
methodOverride只接受'POST',类型上不存在其他取值。 - 替代方案
maxURLLength:如果不想改请求方法,可以给httpBatchLink配置maxURLLength(默认Infinity),它会限制单次批处理的 URL 长度上限,超限的操作自动拆分成多个请求。例如文档中给出maxURLLength: 2083(注释为 “a suitable size”)的示例。 - 与
maxItems/maxBatchSize配合:maxItems限制客户端单个批处理的调用数,应保持小于或等于服务端的maxBatchSize;超过服务端maxBatchSize的请求会被400 Bad Request拒绝。这两项限制的是批的条数,不解决单条输入过大导致的 URL 过长问题,与methodOverride解决的是同一问题的不同侧面,可按需同时使用。
验证路径回到具体结果:客户端请求 URL 不再携带input参数、Promise.all批调用按测试期望返回数据,即为配置成功;若出现Unsupported POST-request to query procedure错误,则为服务端未开启allowMethodOverride。
【免费下载链接】trpc🧙♀️ Move Fast and Break Nothing. End-to-end typesafe APIs made easy.项目地址: https://gitcode.com/GitHub_Trending/tr/trpc
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考