Fastify basic-auth验证时请求体(req.body)始终为空的问题
Fastify中basic-auth验证无法获取req.body的解决方法
问题场景
在Fastify中使用@fastify/basic-auth插件做身份验证,基础功能正常,但验证逻辑里始终无法获取req.body(值一直为空)。需要结合用户名、密码和请求体中的uuid完成验证,只要加入uuid相关判断就报错,去掉则运行正常。
原代码示例
路由文件
fastify.post('/transactions', { schema: schema.createTransactionSchema, onRequest: fastify.basicAuth, }, async (req, rep) => { // 路由处理逻辑 })
basic-auth插件文件
import fp from 'fastify-plugin' import basicAuth from '@fastify/basic-auth' const plugin = async (fastify, options, done) => { const authenticate = { realm: 'api' } const validateAuth = async (username, password, req, rep) => { console.log(req.body) // 始终为空 const connection = await fastify.mysql.getConnection() let rows = [] try { [rows] = await connection.query( `SELECT api_key, secret, uuid FROM merchants m WHERE m.api_key = :username AND m.secret = :password AND m.uuid = :uuid AND m.state = 1`, { username: username, password: password, uuid: req.body.uuid } ) } catch (e) { console.log(e) throw e } finally { connection.release() } if (rows.length === 0 || username !== rows[0].api_key || password !== rows[0].secret || rows[0].uuid !== req.body.uuid) { console.log('hit') rep.code(401).send({ message: 'Unauthorized access' }) } } fastify.register(basicAuth, { validate: validateAuth, authenticate }) done() } export default fp(plugin)
问题原因
Fastify的钩子执行顺序为:onRequest → preParsing → preValidation → preHandler → 路由处理函数。onRequest钩子在请求刚进入、还未解析请求体时触发,此时req.body还未被填充,自然为空。原代码把fastify.basicAuth放在onRequest阶段,导致验证逻辑无法获取请求体。
解决方案
将身份验证的触发时机从onRequest改为preHandler阶段,该阶段请求体已完成解析,能正常访问req.body。
修改后的代码
路由文件(仅修改钩子阶段)
fastify.post('/transactions', { schema: schema.createTransactionSchema, preHandler: fastify.basicAuth, // 替换onRequest为preHandler }, async (req, rep) => { // 路由处理逻辑 })
插件文件(验证逻辑无需修改,此时req.body可正常获取)
import fp from 'fastify-plugin' import basicAuth from '@fastify/basic-auth' const plugin = async (fastify, options, done) => { const authenticate = { realm: 'api' } const validateAuth = async (username, password, req, rep) => { console.log(req.body) // 现在能正常输出请求体内容 const connection = await fastify.mysql.getConnection() let rows = [] try { [rows] = await connection.query( `SELECT api_key, secret, uuid FROM merchants m WHERE m.api_key = :username AND m.secret = :password AND m.uuid = :uuid AND m.state = 1`, { username: username, password: password, uuid: req.body.uuid } ) } catch (e) { console.log(e) throw e } finally { connection.release() } // 推荐使用Fastify内置HTTP错误,更符合框架错误处理流程 if (rows.length === 0 || username !== rows[0].api_key || password !== rows[0].secret || rows[0].uuid !== req.body.uuid) { throw new fastify.httpErrors.Unauthorized('Unauthorized access') } } fastify.register(basicAuth, { validate: validateAuth, authenticate }) done() } export default fp(plugin)
额外注意事项
- 确保路由schema正确定义了请求体结构,Fastify会根据schema自动解析请求体,示例schema如下:
const createTransactionSchema = { body: { type: 'object', required: ['uuid'], properties: { uuid: { type: 'string' } } } }
- 验证失败时,建议抛出Fastify内置的
httpErrors.Unauthorized错误,而非手动调用rep.code().send(),这样能更好地集成Fastify的错误处理机制。
内容的提问来源于stack exchange,提问作者Dally
相关产品推荐
相关产品推荐

