Next.js中GET API查询参数的最简推荐验证方案
Next.js API路由中验证GET查询参数的最佳实践
针对你提到的/pages/api/person/[id].ts场景,下面分点给出实用的验证方案,兼顾简洁性与安全性:
一、单个数字ID的极简验证(无需依赖库)
直接用原生JS/TS做运行时验证是最轻量化的方案,但要注意避免isNaN的坑——isNaN('')、isNaN(true)会返回false,导致误判。正确的做法是结合类型检查和整数判断:
import type { NextApiRequest, NextApiResponse } from 'next'; export default function handler(req: NextApiRequest, res: NextApiResponse) { const { id } = req.query; // 处理Next.js中参数可能为数组的情况(如重复传?id=1&id=2) const rawId = Array.isArray(id) ? id[0] : id; // 1. 检查参数是否存在且为字符串 if (!rawId || typeof rawId !== 'string') { return res.status(400).json({ error: 'ID参数缺失或格式错误' }); } // 2. 转换为数字并验证是否为正整数 const numericId = Number(rawId); if (!Number.isInteger(numericId) || numericId <= 0) { return res.status(400).json({ error: 'ID必须为正整数' }); } // 后续逻辑:用numericId查询Prisma等 res.status(200).json({ id: numericId }); }
二、Next.js是否支持路径正则约束?
很遗憾,不管是Pages Router还是App Router,Next.js都没有在路由定义中直接添加正则规则的官方支持。所有参数验证逻辑必须在API handler内部完成。
三、TypeScript的辅助作用
TypeScript是静态类型检查,无法替代运行时验证(因为req.query的类型本质是ParsedUrlQuery,运行时仍可能是任意类型),但可以通过自定义类型守卫让TS自动推断验证后的参数类型:
// 自定义类型守卫函数 function isValidPositiveInteger(id: unknown): id is number { if (typeof id !== 'string') return false; const num = Number(id); return Number.isInteger(num) && num > 0; } // 在handler中使用 export default function handler(req: NextApiRequest, res: NextApiResponse) { const { id } = req.query; if (!isValidPositiveInteger(id)) { return res.status(400).json({ error: '无效的ID格式' }); } // 此处id会被TS自动推断为number类型 console.log(id.toFixed(0)); // 无类型报错 }
四、验证库是否大材小用?
如果只是单个参数,用ajv/joi/yup确实有点过重,但Zod是个例外——它的API极简,单个schema的代码量极少,同时还能提供类型安全:
import { z } from 'zod'; import type { NextApiRequest, NextApiResponse } from 'next'; // 定义单个参数的验证规则 const PersonIdSchema = z.string() .transform(val => Number(val)) // 自动转换为数字 .pipe(z.number().int().positive()); // 验证正整数 export default function handler(req: NextApiRequest, res: NextApiResponse) { try { // 验证并转换参数 const id = PersonIdSchema.parse(req.query.id); // 后续逻辑:id已被确保为正整数,且TS能推断其类型 res.status(200).json({ id }); } catch (err) { res.status(400).json({ error: '无效的ID格式' }); } }
Zod的优势在于,当后续API需要验证多个参数(如分页参数page/limit)时,扩展成本极低,同时保持类型安全,属于“一次投入,长期受益”的方案。
五、能否直接将参数传入Prisma?
虽然Prisma会自动做参数化查询,避免SQL注入风险,但不建议直接传入未验证的参数:
- 无效参数(如非数字ID)会导致Prisma返回空结果或抛出数据库级错误,返回给前端的错误信息不够友好;
- 提前拦截无效请求能减少不必要的数据库查询,提升性能;
- 若后续需要将ID渲染到页面,未验证的参数可能带来XSS风险(虽然GET参数用于查询时风险较低,但提前验证是良好习惯)。
总结推荐
- 单个简单参数:用原生验证+类型守卫,最简洁;
- 多参数/复杂场景:用Zod,兼顾简洁性与类型安全;
- 必须坚持运行时验证,TS仅作为辅助工具;
- 不要跳过参数验证直接传入数据库,即使有ORM防护。
内容的提问来源于stack exchange,提问作者polak polkovski
相关产品推荐
相关产品推荐

