Next.js 13链式调用认证与国际化中间件的类型错误问题
解决方案:修复Next.js 13多中间件链式调用的类型错误
你的类型错误根源在于中间件结构不符合链式函数要求的MiddlewareFactory类型:chain函数期望每个中间件是一个高阶函数(接收下一个中间件作为参数,返回新的中间件),但你当前的Internationalization和Authentication都是直接处理请求的普通中间件,导致类型不兼容。
以下是修正后的完整代码:
1. 修正authentication.ts
将Clerk认证中间件包装为符合MiddlewareFactory的结构,确保它能链式调用后续中间件:
import { authMiddleware } from "@clerk/nextjs"; import type { NextMiddleware } from 'next/server'; import type { NextRequest } from 'next/server'; export const Authentication = (next: NextMiddleware) => { const clerkMiddleware = authMiddleware({ publicRoutes: [ "/(.*)", "/signin(.*)", "/signup(.*)", "/sso-callback(.*)", ], }); return async (req: NextRequest) => { // 先执行Clerk认证逻辑 const authResponse = await clerkMiddleware(req); // 如果Clerk返回了响应(比如重定向到登录页),直接返回该响应 if (authResponse) return authResponse; // 认证通过,调用下一个中间件 return next(req); }; };
2. 修正internationalization.ts
同样将国际化中间件改为高阶函数结构,支持链式调用:
import { NextResponse, NextRequest } from "next/server"; import acceptLanguage from "accept-language"; import { fallbackLng, languages, cookieName } from "@/app/i18n/settings"; import type { NextMiddleware } from 'next/server'; acceptLanguage.languages(languages); export const Internationalization = (next: NextMiddleware) => { return (req: NextRequest) => { // 静态资源直接跳过,调用下一个中间件 if ( req.nextUrl.pathname.indexOf("icon") > -1 || req.nextUrl.pathname.indexOf("chrome") > -1 ) return next(req); let lng: string; if (req.cookies.has(cookieName)) lng = acceptLanguage.get(req.cookies.get(cookieName).value); if (!lng) lng = acceptLanguage.get(req.headers.get("Accept-Language")); if (!lng) lng = fallbackLng; // 路径不包含支持的语言时,重定向到对应语言路径 if ( !languages.some((loc) => req.nextUrl.pathname.startsWith(`/${loc}`)) && !req.nextUrl.pathname.startsWith("/_next") ) { return NextResponse.redirect( new URL(`/${lng}${req.nextUrl.pathname}`, req.url), ); } let response: NextResponse | undefined; // 从referer中获取语言并设置cookie if (req.headers.has("referer")) { const refererUrl = new URL(req.headers.get("referer")); const lngInReferer = languages.find((l) => refererUrl.pathname.startsWith(`/${l}`), ); response = NextResponse.next(); if (lngInReferer) response.cookies.set(cookieName, lngInReferer); } // 有自定义响应则返回,否则调用下一个中间件 return response ?? next(req); }; };
3. 保持chain.ts和middleware.ts不变
你的链式调用核心逻辑和入口文件无需修改,现在类型会自动匹配:
// middleware.ts import { chain } from '@/middlewares/chain' import { Internationalization } from '@/middlewares/internationalization'; import { Authentication } from '@/middlewares/authentication'; export default chain([Authentication, Internationalization]) export const config = { matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"], };
// chain.ts import { NextResponse } from 'next/server' import type { NextMiddleware } from 'next/server' type MiddlewareFactory = (middleware: NextMiddleware) => NextMiddleware export function chain( functions: MiddlewareFactory[], index = 0 ): NextMiddleware { const current = functions[index] if (current) { const next = chain(functions, index + 1) return current(next) } return () => NextResponse.next() }
关键说明
- 链式中间件的执行顺序由
chain数组的顺序决定:先执行Authentication,再执行Internationalization,如果需要调整顺序,只需调换数组中两个中间件的位置。 - 每个中间件可以通过返回
NextResponse终止链式调用(比如认证失败的重定向),或者调用next(req)将请求传递给下一个中间件。
内容的提问来源于stack exchange,提问作者Vintotan
相关产品推荐
相关产品推荐

