Fastify处理apiKey请求头时出现循环结构转JSON错误求助
问题:Fastify处理请求头时触发循环引用JSON转换错误
当传入apiKey请求头时,Fastify抛出以下错误:
Converting circular structure to JSON --> starting at object with constructor 'Socket' | property 'parser' -> object with constructor 'HTTPParser' --- property 'socket' closes the circle
即使仅打印请求头或使用自定义插件,都会触发该错误,相关代码如下:
问题代码1:headers-plugin
const fp = require('fastify-plugin') function headersPlugin(fastify, options, next) { fastify.addHook('onRequest', (request, res, next) => { const headerName = options.headerName console.log(options) if(options.convertType) { const converted = convert(headerValue, options.convertType) request.headers[headerName] = converted } if(options.transformations) { options.transformations.forEach(transform => { headerValue = transform(headerValue) request.headers[headerName] = JSON.stringify(headerValue) }) } next() }) next() } module.exports = fp(headersPlugin, { name: 'headers-plugin' }) // 注册插件 fastify.register(headersPlugin, { headerName: 'apiKey', })
问题代码2:auth-plugin
// auth-plugin.js function authPlugin(fastify, options) { fastify.addHook('onRequest', async (request, reply, done) => { if (request.method === 'POST') { const apiKey = request.headers['apiKey']; const validApiKey = options.apiKey; if (!apiKey || apiKey !== validApiKey) { reply.code(401).send({ error: 'Unauthorized' }); } else { done() } } }); } module.exports = authPlugin; // 注册插件 fastify.register(headersPlugin)
错误原因分析
- 直接修改
request.headers对象:Fastify的request.headers并非普通JavaScript对象,它内部关联了Socket、HTTPParser等底层节点对象,直接修改该对象会导致后续Fastify尝试序列化时触发循环引用。 - 异步钩子混用
async和done回调:onRequest钩子使用async声明后,不应再调用done回调,这会打乱Fastify的内部异步流程,引发状态异常。 - 未定义变量
headerValue:headersPlugin中直接使用未声明的headerValue,可能导致意外的全局变量引用或运行时错误,间接触发序列化问题。
修复方案
1. 避免直接修改request.headers
将处理后的头信息存储到request的自定义属性中,而非修改原headers对象:
const fp = require('fastify-plugin') // 示例convert函数,需根据实际需求实现 function convert(value, type) { if (type === 'string') return String(value); return value; } function headersPlugin(fastify, options, next) { fastify.addHook('onRequest', (request, reply, next) => { const headerName = options.headerName; let headerValue = request.headers[headerName]; if (options.convertType && headerValue !== undefined) { const converted = convert(headerValue, options.convertType); // 存储到自定义属性,不修改原headers request.processedApiKey = converted; } if (options.transformations && headerValue !== undefined) { options.transformations.forEach(transform => { headerValue = transform(headerValue); }); request.transformedApiKey = headerValue; } next(); }); next(); } module.exports = fp(headersPlugin, { name: 'headers-plugin' }) // 注册插件 fastify.register(headersPlugin, { headerName: 'apiKey', convertType: 'string', transformations: [val => val.trim()] })
2. 异步钩子规范使用
async钩子直接通过返回或抛出错误处理流程,不再使用done:
// auth-plugin.js function authPlugin(fastify, options) { fastify.addHook('onRequest', async (request, reply) => { if (request.method === 'POST') { const apiKey = request.headers['apiKey']; const validApiKey = options.apiKey; if (!apiKey || apiKey !== validApiKey) { await reply.code(401).send({ error: 'Unauthorized' }); } // 验证通过无需额外操作,钩子自动结束 } }); } module.exports = authPlugin; // 注册插件(修正原代码的注册错误) fastify.register(authPlugin, { apiKey: 'your-valid-api-key' })
3. 打印请求头的正确方式
如果需要打印请求头,先将其转换为普通对象,避免触发循环引用:
fastify.addHook('onRequest', (request, reply, next) => { // 转换为普通对象后打印 console.log('Headers:', Object.fromEntries(Object.entries(request.headers))); next(); });
内容的提问来源于stack exchange,提问作者EVG_m
相关产品推荐
相关产品推荐

