免责声明
如果您想更深入地了解我的代码,这是repo。导航至
apps/ott-platform
。您必须在 Clerk 上创建一个帐户并在 .env
中输入您的 Clerk 密钥。
问题
我有一个 Next.js 应用程序(版本 13.4.19、app-dir-routing、TypeScript、Turbo)。我想使用 Clerk 添加身份验证,并使用 i18next 添加国际化。因此,我必须添加多个目前不支持的中间件功能。我发现可以创建一个链式中间件函数,所以我将我的中间件函数分成单独的文件,以便链式中间件函数可以导入它们(还有一个YT教程)。
但我仍然收到错误消息:
./middleware.ts:5:39
Type error: Type '(req: NextRequest) => NextResponse<unknown>' is not assignable to type 'MiddlewareFactory'.
Types of parameters 'req' and 'middleware' are incompatible.
Type 'NextMiddleware' is not assignable to type 'NextRequest'.
Authentication
已成功导入,但Internationalization
似乎有问题。也许我做错了。我集成了 i18next 的 example repo中的
Internationalization
的中间件功能。
这是
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()
}
这是
authentication.ts
:
import { authMiddleware } from "@clerk/nextjs";
export const Authentication = () => {
return authMiddleware({
publicRoutes: [
"/(.*)",
"/signin(.*)",
"/signup(.*)",
"/sso-callback(.*)",
],
});
};
这是
internationalization.ts
:
import { NextResponse, NextRequest } from "next/server";
import acceptLanguage from "accept-language";
import { fallbackLng, languages, cookieName } from "@/app/i18n/settings";
acceptLanguage.languages(languages);
export function Internationalization(req: NextRequest) {
if (
req.nextUrl.pathname.indexOf("icon") > -1 ||
req.nextUrl.pathname.indexOf("chrome") > -1
)
return NextResponse.next();
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;
// Redirect if lng in path is not supported
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),
);
}
if (req.headers.has("referer")) {
const refererUrl = new URL(req.headers.get("referer"));
const lngInReferer = languages.find((l) =>
refererUrl.pathname.startsWith(`/${l}`),
);
const response = NextResponse.next();
if (lngInReferer) response.cookies.set(cookieName, lngInReferer);
return response;
}
return NextResponse.next();
}
我想要一个可以处理多种中间件功能的
middlware.ts
。我也尝试按照this thread的说明解决它,但没有成功。
我最近偶然发现了同样的事情。这是我的解决方案:
一般进口及类型
import type { NextFetchEvent, NextRequest } from 'next/server';
import { NextResponse } from 'next/server';
import type { NextMiddlewareResult } from 'next/dist/server/web/types';
export type MiddlewareWrapperType = (middleware: ChainMiddlewareType) => ChainMiddlewareType;
src/middlewares/middleware1.ts
export const middleware1: MiddlewareWrapperType = (next) => {
return (req, evt) => {
const res = NextResponse.next();
return next(req, evt, res);
};
};
src/middlewares/middleware2.ts
export const middleware2: MiddlewareWrapperType = (next) => {
return (req, evt, res) => {
res.headers.set('x-middleware', 'custom header');
return next(req, evt, res);
};
};
src/middlewares/middlewareHandler.ts
export type ChainMiddlewareType = (
request: NextRequest,
event: NextFetchEvent,
response: NextResponse
) => NextMiddlewareResult | Promise<NextMiddlewareResult>;
export function middlewareHandler(middlewares: Array<MiddlewareWrapperType>, i = 0): ChainMiddlewareType {
const current = middlewares[i];
if (current) {
const next = middlewareHandler(middlewares, i + 1);
return current(next);
}
return (_req: NextRequest, _evt: NextFetchEvent, res: NextResponse) => {
return res;
};
}
src/middleware.ts
export const middleware = middlewareHandler([middleware1, middleware2]);
export const config = {
matcher: ['/((?!api|fonts|_next/static|_next/image|favicon.ico).*)'],
};
P.s.:我的解决方案受到Hamed Bahram的启发。