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

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)

错误原因分析

  1. 直接修改request.headers对象:Fastify的request.headers并非普通JavaScript对象,它内部关联了Socket、HTTPParser等底层节点对象,直接修改该对象会导致后续Fastify尝试序列化时触发循环引用。
  2. 异步钩子混用async和done回调:onRequest钩子使用async声明后,不应再调用done回调,这会打乱Fastify的内部异步流程,引发状态异常。
  3. 未定义变量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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 22:05:06