Next.js+Sanity Webhook重新验证:遇TypeError及401未授权错误
问题:Next.js 13 + Sanity Webhook 触发重新验证时的错误排查与解决
初始错误:TypeError: resolver is not a function
场景
开发基于Next.js 13(app目录)+ Sanity CMS的项目,部署在Vercel。配置Webhook意图在Sanity的推荐语(testimonial)增删改时,触发页面/组件的增量重新验证,避免全量构建。
代码示例
import { SIGNATURE_HEADER_NAME, isValidSignature } from "@sanity/webhook"; const handler = async (req: any, res: any) => { try { const signature = req.headers[SIGNATURE_HEADER_NAME].toString(); if ( !isValidSignature( JSON.stringify(req.body), signature, process.env.NEXT_PUBLIC_SECRET_TOKEN as string ) ) { return res.status(401).json({ message: "Invalid request" }); } await res.revalidate("/"); res.status(200).json({ revalidated: true }); console.log("testimonial revalidated"); } catch (error) { res.status(500).json({ revalidated: false, error }); } };
错误信息
TypeError: resolver is not a function at /var/task/node_modules/next/dist/server/api-utils/node.js:463:16 // ...
原因与解决
错误核心原因是API路由函数未正确导出。Next.js的API路由要求必须默认导出处理函数,否则框架无法识别请求处理逻辑。
修改代码,在末尾添加导出语句:
export default handler;
更新后问题:401 Unauthorized 签名验证失败
问题描述
解决导出问题后,请求Webhook接口持续返回401,响应内容:
{ "message": "Invalid request" }
排查与解决方案
1. 环境变量命名错误
NEXT_PUBLIC_前缀的变量会暴露给客户端代码,Webhook验证密钥属于敏感信息,禁止添加该前缀。建议改为SANITY_WEBHOOK_SECRET这类仅服务端可用的变量名,代码中对应调整:
process.env.SANITY_WEBHOOK_SECRET as string
2. 请求体处理差异
isValidSignature需要原始请求体字符串,而Next.js默认自动解析请求体后,JSON.stringify(req.body)生成的字符串可能与Sanity发送的原始请求体存在格式差异(如空格、键顺序),导致签名验证失败。
需禁用自动解析,读取原始请求体:
export const config = { api: { bodyParser: false, }, }; const handler = async (req: any, res: any) => { try { // 读取原始请求体 const rawBody = await new Promise<string>((resolve, reject) => { let data = ""; req.on("data", (chunk: Buffer) => { data += chunk; }); req.on("end", () => resolve(data)); req.on("error", reject); }); const signature = req.headers[SIGNATURE_HEADER_NAME] as string; if ( !isValidSignature( rawBody, signature, process.env.SANITY_WEBHOOK_SECRET as string ) ) { return res.status(401).json({ message: "Invalid request" }); } // 仅在testimonial文档变更时触发重新验证 const body = JSON.parse(rawBody); if (body._type === "testimonial") { await res.revalidate("/"); console.log("testimonial revalidated"); } res.status(200).json({ revalidated: true }); } catch (error) { res.status(500).json({ revalidated: false, error: (error as Error).message }); } }; export default handler;
3. Sanity Webhook配置校验
- 确认Sanity后台配置的Webhook签名密钥与Vercel环境变量中的值完全一致,注意大小写、空格、特殊字符。
- 确认Webhook请求方法为
POST,与代码处理逻辑匹配。 - 确认Sanity发送的请求头包含
SANITY-WEBHOOK-SIGNATURE(即SIGNATURE_HEADER_NAME对应的值)。
4. Vercel环境变量生效校验
- 在Vercel控制台重新部署项目,确保修改后的环境变量生效。
- 可临时在代码中打印环境变量(注意不要提交到版本库),确认服务端能正确读取密钥。
优化建议
- 增加文档类型判断,仅当变更的是
testimonial类型文档时才触发重新验证,减少不必要的验证操作。 - 错误处理中捕获具体错误信息,提升调试效率。
内容的提问来源于stack exchange,提问作者Kavindu Wijesekara
相关产品推荐
相关产品推荐

