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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 03:26:14