在 NextJs 13 中链接多个中间件功能以进行身份验证和国际化

问题描述 投票:0回答:1

免责声明

如果您想更深入地了解我的代码,这是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的说明解决它,但没有成功。

typescript next.js middleware i18next clerk
1个回答
0
投票

我最近偶然发现了同样的事情。这是我的解决方案:

一般进口及类型

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的启发。

© www.soinside.com 2019 - 2024. All rights reserved.