
后端API网关【免费下载链接】http-proxy-middleware:zap: The one-liner node.js http-proxy (httpxy) middleware for connect, express, next.js and more项目地址https://gitcode.com/gh_mirrors/ht/http-proxy-middleware点击查看免费下载导读本文基于官方 recipes 文档 async-response.md 展开聚焦 http-proxy-middleware 中最容易被忽视的能力如何在代理后端响应返回客户端之前异步修改响应头以及如何在代理请求真正发出之前异步准备好请求头。读完本文你将掌握selfHandleResponse开关的正确用法、proxyRes异步回调的编写方式以及通过前置中间件在proxyReq阶段注入异步数据的完整链路可直接用于需要注入动态令牌、时间戳、追踪 ID 等真实业务场景。为什么需要异步处理代理响应中间件模式下http-proxy-middleware 默认会替开发者完成把后端响应转发回客户端的收尾工作。但在某些场景下我们需要在响应转发前插入自己的处理逻辑后端响应头中需要携带一个动态生成的值如签名、会话标识而这个值只能通过异步 IO查询数据库、调用远程服务、setTimeout模拟延迟获取请求头中的某些字段同样依赖异步计算的中间结果而这些结果在proxyReq事件触发时才能写入目标请求。原文档给出了两套对应解法在proxyRes回调里直接使用async处理响应头在代理之前插入一个异步前置中间件把结果暂存到req对象上供后续proxyReq使用。前置知识事件链与 selfHandleResponseproxyReq与proxyRes是中间件基于httpxy原 http-proxy 的 TypeScript 重写版代理服务器的两个核心事件proxyReq在出站请求发往目标服务器创建之后、写入之前触发此时可以修改目标请求的请求头proxyRes在收到目标服务器响应之后触发此时可以读取/修改响应并把响应转发给客户端。事件订阅通过options.on对象声明由 proxy-events.ts 中的proxyEventsPlugin统一注册到内部代理服务器上。其类型签名定义在 types.tsproxyRes?: (proxyRes: TReq, req: TReq, res: TRes) void | Promisevoid;注意proxyRes回调允许返回 Promise这正是在回调内直接await异步任务的类型依据而proxyReq回调签名是(proxyReq, req, res, options) void不支持等待异步结果所以请求头方向的异步化要另寻出路见下文方案二。另一个关键配置是selfHandleResponse。README 中 option.selfHandleResponse 的说明为置为true后httpxy 不再自动调用res.end()由开发者自行监听proxyRes事件并把响应返回给客户端。只要你在proxyRes里自行处理了响应就必须开启它否则会出现你处理完响应头中间件又把响应自动结束的重复写入问题。方案一异步修改响应头proxyRes 内 await原文档的第一个示例完整展示了解法开启selfHandleResponse: true在proxyRes回调中先await一个异步任务再设置动态响应头最后把后端响应pipe给客户端const myProxy createProxyMiddleware({ target: http://www.example.com/api, changeOrigin: true, selfHandleResponse: true, on: { proxyReq: (proxyReq, req, res) { // before proxyReq.setHeader(mpth-1, da); }, proxyRes: async (proxyRes, req, res) { const da await new Promise((resolve, reject) { setTimeout(() { resolve({ wei: wei }); }, 200); }); // add your dynamic header res.setHeader(mpth-2, da.wei); // now pipe the response proxyRes.pipe(res); }, }, }); app.use(/api, myProxy);拆解这段代码的关键点proxyRes回调声明为async得益于 types.ts 中void | Promisevoid的签名await不会造成类型错误异步计算放在await之后这里用setTimeout模拟 200ms 的异步 IO真实场景可替换为await fetch(...)、数据库查询等res.setHeader(mpth-2, da.wei)res是传给客户端的最外层http.ServerResponse在此设置的头部会随响应一起返回proxyRes.pipe(res)是最后一公里既然selfHandleResponse关闭了自动结束若不执行pipe或手动res.end()客户端将一直等待响应而挂起。proxyReq阶段同步写入的mpth-1头、proxyRes阶段异步写入的mpth-2头最终都会出现在客户端收到的响应中。方案二异步修改请求头前置中间件 req.locals请求头方向的异步化不能直接在proxyReq内完成——事件回调是同步派发的即使把回调写成asynchttpxy 也不会等待其中的 Promise。原文档给出的解法是在挂载代理之前先挂一个异步中间件把计算结果暂存到req对象上之后proxyReq回调再同步读取const entryMiddleware async (req, res, next) { const foo await new Promise((resolve, reject) { setTimeout(() { resolve({ da: da }); }, 200); }); req.locals { da: foo.da, }; next(); }; const myProxy createProxyMiddleware({ target: http://www.example.com/api, changeOrigin: true, selfHandleResponse: true, on: { proxyReq: (proxyReq, req, res) { // before // get something async from entry middleware before the proxy kicks in console.log(proxyReq:, req.locals.da); proxyReq.setHeader(mpth-1, req.locals.da); }, proxyRes: async (proxyRes, req, res) { const da await new Promise((resolve, reject) { setTimeout(() { resolve({ wei: wei }); }, 200); }); // end: res.setHeader(mpth-2, da.wei); proxyRes.pipe(res); }, }, }); app.use(/api, entryMiddleware, myProxy);方案二的核心差异挂载顺序app.use(/api, entryMiddleware, myProxy)保证请求先经过entryMiddleware完成异步计算再进入代理中间件。entryMiddleware中await结束并调用next()之后代理才会接管请求因此req.locals.da在proxyReq触发时一定已就绪数据传递载体req.locals是挂在http.IncomingMessage上的自定义属性Express 生态惯用命名也可以是req.customData等任意属性只要前后两个中间件约定一致职责分离异步逻辑集中在入口中间件proxyReq内只做同步的setHeader时序清晰、可测试。从实现上看这种先跑前置中间件、再进入代理的时序与 http-proxy-middleware.ts 中middleware的执行模型完全吻合代理中间件是标准的三参/两参中间件只有next()被调用后或直接返回才会继续而prepareProxyRequest阶段本身也支持router、pathRewrite等异步操作说明整个请求准备链路是异步友好的。两个方案的选型对比维度方案一异步响应头方案二异步请求头异步位置proxyRes事件回调内代理之前的独立中间件类型支持proxyRes签名返回void \| Promisevoid可直接asyncproxyReq签名为同步void需前置处理结果载体直接res.setHeader(...)暂存req.localsproxyReq内同步读取典型场景动态签名头、时间戳、追踪 ID 注入响应依赖鉴权/查询结果的请求头注入两者可同时使用如上例所示覆盖请求头、响应头都需要异步增强的完整闭环。常见坑与最佳实践1. 忘记pipe导致客户端挂起selfHandleResponse: true意味着 httpxy 不再替你结束响应。若proxyRes中只改头不pipe也不res.end()连接将一直悬置直至超时。务必在异步逻辑完成后执行proxyRes.pipe(res)。2. 不要在proxyReq内依赖未就绪的异步数据proxyReq事件派发是同步的写在里面的await不会被等待。异步数据必须像方案二一样由前置中间件保证在next()之前就绪proxyReq里只做同步读取。3. 注意与responseInterceptor的分工如果需求是修改响应体而不是单纯的响应头官方提供了更完善的封装responseInterceptor见 response-interceptor.ts 与 response-interceptor.md。它同样要求selfHandleResponse: true但会自动完成 brotli / gzip / deflate / zstd 解压、拦截 Buffer 回写、重新计算content-length、处理无响应体HEAD、1xx、204、304等细节。手动proxyRes.pipe(res)属于原样透传 头部增强的精简路线改 body 时应优先考虑responseInterceptor。4. 异步回调内的错误处理async回调中的异常不会自动传递给 Express 的错误中间件建议在回调内try/catch并在出错时res.end()或委托on.error事件处理httpxy 的 Promise 式 API 在网络错误时也不会自动 emiterror需要手动兜底http-proxy-middleware.ts 中即通过this.proxy.emit(error, ...)兼容旧插件。5.target必填无论哪种方案target或router都是必需的configuration.ts 中的verifyConfig会在缺失时抛出[HPM] Missing target option错误。仓库内的相关资源完整可运行示例examples/response-interceptor/index.js展示了selfHandleResponse 修改状态码与 content-type 的组合用法可直接node运行在 3000 端口类型与配置定义src/types.ts、src/configuration.ts事件注册实现src/plugins/default/proxy-events.ts中间件主流程src/http-proxy-middleware.ts测试佐证test/e2e/response-interceptor.spec.ts覆盖压缩响应解压、无响应体 204/304、trailer 头处理等边界相关进阶文档recipes/response-interceptor.md、recipes/proxy-events.md原文档还附带了一个基于 Express 的在线可运行示例支持通过 Server Control Panel 重启服务器并观察日志可在本地以同样的createProxyMiddleware配置自行复现本文的两个示例验证mpth-1、mpth-2头的实际注入效果。小结http-proxy-middleware 的异步能力体现在两个层面proxyRes回调原生支持async/await可以先异步计算、再写响应头、最后 pipe 转发而proxyReq阶段的异步化则依赖前置中间件 req对象暂存的标准中间件组合拳。掌握selfHandleResponse的语义与这两条链路即可从容应对动态响应头、动态请求头、甚至两者叠加的真实生产需求。赞分享后端API网关【免费下载链接】http-proxy-middleware:zap: The one-liner node.js http-proxy (httpxy) middleware for connect, express, next.js and more项目地址https://gitcode.com/gh_mirrors/ht/http-proxy-middleware点击查看免费下载相关推荐头部信息处理nginx-proxy请求头修改与传递完整指南头部信息处理nginx proxy请求头修改与传递完整指南 nginx proxy作为Docker容器的自动化nginx代理解决方案在处理HTTP请求头方面后端运维云原生API网关Next.js Middleware 请求头修改实战基于 modify-request-header 示例掌握请求头的增删改Next.js Middleware 请求头修改实战基于 modify request header 示例掌握请求头的增删改 导读 本篇文章围绕仓库 edge示例工程前端后端Chromeless网络请求拦截修改HTTP头与响应的技巧Chromeless网络请求拦截修改HTTP头与响应的技巧 你是否曾遇到需要在自动化测试中模拟不同用户代理是否需要在爬虫项目中隐藏设备标识或者在前端开发时开发工具测试创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考