You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

解决http-proxy-middleware无法捕获WebSocket响应问题及实现请求响应拦截与外部服务调用方案

修复WebSocket代理的响应拦截问题

问题根源

你遇到的proxyRes钩子不触发的核心原因是:WebSocket的握手响应(101 Switching Protocols)不会触发常规的proxyRes回调。http-proxy库对WebSocket的升级流程有单独的处理逻辑,常规的proxyRes仅针对普通HTTP请求的响应,不会覆盖WebSocket握手阶段的响应处理。

另外你的代码里还有一个变量作用域的小问题:在if(isWebSocketSuccess)块里重新用const声明了requestId,导致外部的requestId变量始终是undefined,即使后端返回了xyz-req-id头也无法正确获取。

修复方案

我们需要在proxyReqWs钩子中监听后端返回的响应(通过proxyReq的response事件),在这里完成响应头的拦截、修改,以及request-id的提取。

修改后的setupProxy函数代码如下:

// proxy.js
export const setupProxy = (proxyConfig: ProxyConfig): RequestHandler => {
 const target = proxyConfig.endpoint;
 const proxyOptions: Options = {
 target: target,
 changeOrigin: true,
 secure: true,
 ws: true,
 on: {
 proxyReq: (proxyReq, req, res) => {
 log('--------------------------------------');
 logRequest(req as Request, 'Original Request from Client'); // Log original client request
 proxyReq.setHeader('Authorization', `Bearer ${proxyConfig.details.apiKey}`);
 logRequest(proxyReq, 'MODIFIED request to Backend'); // Log modified request
 },
 proxyReqWs: (proxyReq, req, socket, options, head) => {
 log('proxyReqWs event triggered for:', { method: req.method, url: req.url });
 proxyReq.setHeader('Authorization', `Bearer ${proxyConfig.details.apiKey}`);

 // 监听后端返回的WebSocket握手响应
 proxyReq.on('response', (proxyRes) => {
 logResponse(proxyRes, req as Request, 'Original Response from Backend'); // Log response from Backend
 const isWebSocketSuccess = proxyRes.statusCode === 101;
 let requestId: string | undefined;

 if(isWebSocketSuccess) {
 // 修复变量作用域问题:不要重新声明requestId
 requestId = proxyRes.headers['xyz-req-id'] as string | undefined;
 }

 if (requestId) {
 log(`Extracted request-id: ${requestId}`);
 // 修改响应头
 proxyRes.headers['x-xyz-request-id'] = requestId;
 }
 delete proxyRes.headers['xyz-req-id'];

 logResponse(proxyRes, req as Request, 'MODIFIED response to Client'); // Log modified response
 log('--------------------------------------');

 // 可选:调用外部服务处理request-id
 // 注意:这里如果是异步操作,不要阻塞响应,建议用非阻塞的方式
 // 例如:
 // fetch('https://your-external-service.com/log', {
 //   method: 'POST',
 //   body: JSON.stringify({ requestId }),
 //   headers: { 'Content-Type': 'application/json' }
 // }).catch(err => logError('External service call failed:', err));
 });
 },
 error: (err, req, res) => {
 logError('Proxy Error:', err.message, 'Target:', target);
 if (res.writeHead && !res.headersSent) {
 res.writeHead(502, { 'Content-Type': 'application/json' });
 res.end(JSON.stringify({ error: 'Bad Gateway', message: err.message }));
 }
 }
 },
 router: (req) => {
 return proxyConfig.endpoint;
 }
 };
 return createProxyMiddleware(proxyOptions);
};

关键修改点说明

  1. 监听proxyReq的response事件:在proxyReqWs中,proxyReq是发送给后端的HTTP请求对象,当后端返回握手响应时,会触发response事件,我们在这里处理响应头。
  2. 修复变量作用域问题:移除了if块内的const声明,直接给外部的requestId赋值,确保能正确提取并使用后端返回的xyz-req-id。
  3. 异步调用外部服务的注意事项:如果需要在拿到requestId后调用外部服务,建议使用非阻塞的异步操作(比如fetch或axios的异步调用),不要用同步代码阻塞WebSocket握手流程,避免客户端超时。

验证方式

修改完成后,重新启动代理服务器,发起WebSocket连接请求:

  • 你应该能看到proxyReqWs的日志,以及response事件中打印的前后响应日志。
  • 在浏览器开发者工具的网络面板中,客户端收到的响应头应该包含x-xyz-request-id,且不再有xyz-req-id。

内容的提问来源于stack exchange,提问作者S7H

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 10:07:27