Next.js中如何在请求后执行中间件并校验响应、回滚事务?
在Next.js里用Middleware校验请求响应的可行方案
Next.js的Middleware本来就是请求到路由处理之前运行的拦截器,默认拿不到路由处理后的响应对象。要实现“校验请求生成的响应、无效就回滚事务”的需求,给你几个实际能用的方案:
1. 直接在API路由/Route Handler里处理(最推荐)
如果你的请求是打给API路由或者App Router的Route Handler,直接在路由内部把流程串起来最省心:请求参数校验、执行业务、响应校验、事务回滚一步到位。比如用Prisma的事务示例:
// app/api/your-route/route.ts import { NextResponse } from 'next/server'; import prisma from '@/lib/prisma'; export async function POST(request: Request) { const reqData = await request.json(); // 先做请求参数校验(也可以放Middleware里) if (!reqData.targetId) { return NextResponse.json({ error: '缺参数了' }, { status: 400 }); } try { // 开启事务,所有操作都在tx里执行 const result = await prisma.$transaction(async (tx) => { // 执行你的业务逻辑,比如创建数据 const createdItem = await tx.item.create({ data: reqData }); // 生成响应数据并校验 const resData = { id: createdItem.id, name: createdItem.name }; if (resData.name !== reqData.expectedName) { // 校验失败直接抛错,事务会自动回滚 throw new Error('响应数据不对'); } return resData; }); // 校验通过,返回正常响应 return NextResponse.json(result); } catch (err) { // 捕获错误,返回提示 return NextResponse.json({ error: '响应验证失败,事务已回滚' }, { status: 500 }); } }
2. Middleware里用代理请求捕获响应(适合必须在Middleware处理的场景)
如果一定要在Middleware里做响应校验,可以把请求代理到内部的实际处理路由,拿到响应后再校验。但这种方式需要额外处理事务回滚的逻辑,比如记录事务ID:
// middleware.ts import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; export async function middleware(request: NextRequest) { // 把请求转发到内部的处理路由 const proxyRes = await fetch(new URL('/api/internal-handler', request.url), { method: request.method, headers: request.headers, body: request.body, credentials: 'same-origin', }); // 拿到响应数据和原始请求数据 const resData = await proxyRes.json(); const reqData = await request.clone().json(); // 校验响应是否符合预期 if (resData.status !== 'ok' || resData.data.id !== reqData.id) { // 调用回滚接口(需要内部路由在执行时记录事务ID) await fetch(new URL('/api/rollback', request.url), { method: 'POST', body: JSON.stringify({ txId: resData.txId }), }); return NextResponse.json({ error: '响应不对,事务已经回滚了' }, { status: 500 }); } // 校验通过,返回原始响应 return NextResponse.json(resData, { status: proxyRes.status }); } // 指定要拦截的路由 export const config = { matcher: '/api/need-check/:path*', };
注意:这种方式会多一次内部请求,而且事务回滚需要提前在内部路由里做支持,复杂度会高一些。
3. 自定义响应校验工具(轻量校验用)
如果只是做简单的响应格式校验,不想改太多代码,可以写个工具函数在Route Handler里用,不过这种方式没法回滚事务(因为事务已经提交了),适合对数据格式的校验:
// app/lib/validate-res.ts import { NextResponse } from 'next/server'; export function checkResponse(resData: any, reqData: any) { if (!resData.id || resData.id !== reqData.id) { throw new Error('响应数据无效'); } return NextResponse.json(resData); } // 在Route Handler里用 import { checkResponse } from '@/app/lib/validate-res'; export async function POST(request: Request) { const reqData = await request.json(); const businessRes = await runYourBusiness(reqData); return checkResponse(businessRes, reqData); }
几个关键提醒
- Middleware本来就是做前置处理的(比如认证、路由跳转),硬用来做响应校验有点偏离它的定位,优先选第一种方案。
- 事务回滚一定要用ORM自带的事务机制,比如Prisma的
$transaction,抛错就自动回滚,别自己手动写回滚逻辑,容易出问题。 - 如果是页面路由(不是API请求),Middleware根本拿不到页面渲染后的响应,只能在页面组件或者服务器组件里做数据校验。
内容的提问来源于stack exchange,提问作者corsiKa
相关产品推荐
相关产品推荐

