NextJS 13(App Router)如何用Zod验证请求头与URL参数?
解决NextJS 13 App Router中Zod验证请求头及URL参数的问题
一、修复请求头验证失败的问题
你当前验证失败的核心原因是:NextJS 13 App Router中req.headers是Headers类实例,并非普通JavaScript对象,Zod无法直接解析这种特殊结构。需要先将其转换为普通对象再验证。
修改后的验证代码如下:
import { z } from "zod"; export async function GET(req: Request) { // 将Headers实例转为普通对象,键会自动转为小写 const headersObj = Object.fromEntries(req.headers.entries()); const requestSchema = z.object({ headers: z.object({ // 确保这里的键是小写,因为请求头在传输中会被标准化为小写 identifier: z.string().min(1, "identifier不能为空"), }), }); const parsedRequest = requestSchema.safeParse({ headers: headersObj }); if (!parsedRequest.success) { return new Response( JSON.stringify({ errorCode: "badRequest", errors: parsedRequest.error.issues, // 可选:返回具体错误信息便于调试 }), { status: 400 } ); } // 验证通过后可正常使用解析后的请求头 const { identifier } = parsedRequest.data.headers; // ...后续业务逻辑 return new Response(JSON.stringify({ success: true, identifier })); }
二、验证URL参数(路径参数+查询参数)
在App Router的API路由中,URL参数分为两种:路径参数(如/api/user/[id]中的id)和查询参数(如/api/user?id=123中的id),以下是两种参数的Zod验证方式:
1. 验证路径参数
假设你的路由文件是app/api/[userId]/route.ts,路径参数userId可以通过params参数获取:
import { z } from "zod"; // 定义路径参数的Zod Schema const paramsSchema = z.object({ userId: z.string().regex(/^\d+$/, "userId必须是数字字符串"), }); export async function GET(req: Request, { params }: { params: { userId: string } }) { // 验证路径参数 const parsedParams = paramsSchema.safeParse(params); if (!parsedParams.success) { return new Response( JSON.stringify({ errorCode: "badRequest", errors: parsedParams.error.issues, }), { status: 400 } ); } const { userId } = parsedParams.data; // ...后续业务逻辑 }
2. 验证查询参数
查询参数可以通过req.nextUrl.searchParams获取,同样需要转为普通对象后验证:
import { z } from "zod"; export async function GET(req: Request) { // 将查询参数转为普通对象 const searchParamsObj = Object.fromEntries(req.nextUrl.searchParams.entries()); // 定义查询参数的Zod Schema const searchParamsSchema = z.object({ page: z.coerce.number().int().min(1).default(1), // 自动将字符串转为数字,默认值1 size: z.coerce.number().int().min(1).max(100).default(10), }); const parsedSearchParams = searchParamsSchema.safeParse(searchParamsObj); if (!parsedSearchParams.success) { return new Response( JSON.stringify({ errorCode: "badRequest", errors: parsedSearchParams.error.issues, }), { status: 400 } ); } const { page, size } = parsedSearchParams.data; // ...后续业务逻辑 }
三、组合验证(请求头+路径参数+查询参数)
如果需要同时验证多种参数,可以将多个Schema合并:
import { z } from "zod"; const requestSchema = z.object({ headers: z.object({ identifier: z.string().min(1), }), params: z.object({ userId: z.string().regex(/^\d+$/), }), searchParams: z.object({ page: z.coerce.number().int().min(1).default(1), }), }); export async function GET(req: Request, { params }: { params: { userId: string } }) { const validationData = { headers: Object.fromEntries(req.headers.entries()), params, searchParams: Object.fromEntries(req.nextUrl.searchParams.entries()), }; const parsedResult = requestSchema.safeParse(validationData); if (!parsedResult.success) { return new Response( JSON.stringify({ errorCode: "badRequest", errors: parsedResult.error.issues, }), { status: 400 } ); } // 解构所有验证通过的参数 const { identifier } = parsedResult.data.headers; const { userId } = parsedResult.data.params; const { page } = parsedResult.data.searchParams; // ...后续业务逻辑 }
内容的提问来源于stack exchange,提问作者Craig Malton
相关产品推荐
相关产品推荐

