解决openapi-mcp-generator项目中的ERR_HTTP_HEADERS_SENT错误
在使用openapi-mcp-generator项目创建SSE(Server-Sent Events)版本服务器时,开发者可能会遇到一个常见的Node.js错误:ERR_HTTP_HEADERS_SENT。这个错误表明在HTTP响应头已经发送后,程序又尝试再次发送头信息。
错误现象分析
当开发者运行SSE版本的服务器并访问localhost:3000时,控制台会显示以下关键错误信息:
Error [ERR_HTTP_HEADERS_SENT]: Cannot write headers after they are sent to the client
同时,浏览器端会卡在"Connected, waiting for session ID..."状态,而健康检查端点(/health)则能正常响应。
问题根源
这个错误的根本原因在于web-server.ts文件中存在两个关键问题:
- 重复设置响应头:在/sse路由处理中,响应头被多次设置
- 参数传递不完整:handlePostMessage方法调用时没有正确传递请求体参数
解决方案
经过技术分析,需要修改src/web-server.ts文件中的以下部分:
-
移除重复的头设置:让SSEServerTransport完全负责响应头的设置,不在路由处理函数中额外设置
-
修正参数传递:将
await transport.handlePostMessage(req, res)
改为await transport.handlePostMessage(req, res, req.body)
完整修正代码
修正后的核心部分应该如下处理SSE连接和消息:
app.get("/sse", async (req: Request, res: Response) => {
const transport = new SSEServerTransport('/api/messages', res);
const sessionId = transport.sessionId;
transports[sessionId] = transport;
req.on('close', () => {
delete transports[sessionId];
});
try {
await server.connect(transport);
} catch (error) {
console.error(`连接错误: ${sessionId}:`, error);
}
});
app.post("/api/messages", async (req: Request, res: Response) => {
const sessionId = req.query.sessionId as string;
const transport = transports[sessionId];
try {
await transport.handlePostMessage(req, res, req.body);
} catch (error) {
if (!res.headersSent) {
return res.status(500).send('内部服务器错误');
}
}
});
技术要点
-
SSE协议特性:Server-Sent Events是一种服务器向客户端推送数据的机制,它保持一个长连接,因此需要特别注意响应头的设置时机。
-
Express中间件顺序:确保cors()和express.json()中间件在路由处理之前正确配置。
-
错误处理策略:在SSE连接中,一旦响应头发送,就不能再修改,因此错误处理需要特别小心。
最佳实践建议
- 在开发SSE服务时,应该统一由专门的传输类管理响应头
- 添加详细的日志记录,帮助追踪连接状态
- 实现完善的会话管理,及时清理断开连接的资源
- 考虑添加心跳机制保持连接活跃
通过以上修正和优化,可以确保SSE版本的服务器稳定运行,避免ERR_HTTP_HEADERS_SENT错误的发生。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考