NextJS + next-intl 中间件获取

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

如果进入网站的用户的IP地址被阻止,我想重定向到其他页面,例如/blocked,并且不访问该网站。我想在我的中间件上创建一个获取请求并根据结果运行其他代码,但是 next-intl 给出了错误“无法找到 next-intl 语言环境,因为中间件没有在此请求上运行。”

我需要做什么才能通过API控制被禁止的用户?等待您的建议

import createMiddleware from 'next-intl/middleware';
import { defaultLocale, locales, pathnames } from "./config/lang-config";
import withAuth from 'next-auth/middleware';
import { NextRequest, NextResponse } from 'next/server';
import { getProfile } from './services/authQueries';

const privatePages = [
    '/profile',
    '/settings',
    '/settings/*',
    '/wallet',
    '/wallet/*',
    '/auth/logout',
];

const intlMiddleware = createMiddleware({
    defaultLocale,
    locales,
    localePrefix: "as-needed",
    pathnames,
    localeDetection: true,
});

const authMiddleware = withAuth(
    // Note that this callback is only invoked if
    // the `authorized` callback has returned `true`
    // and not for pages listed in `pages`.
    function onSuccess(req) {
        return intlMiddleware(req);
    },
    {
        callbacks: {
            authorized: async ({ token }: { token: any }) => {
                const accessToken = token?.tokenData?.token || token?.token as string;
                if (token) {
                    try {
                        const res = await getProfile(accessToken as string, 'en');
                        if (res.isSucceed) {
                            token.user = res.data;
                            return true;
                        } else {
                            throw new Error(res.message);
                        }
                    } catch (error) {
                        if (error instanceof Error) {
                            console.error(error.message);
                        } else {
                            console.error('An unknown error occurred');
                        }
                        return false;
                    }
                } else {
                    return false;
                }
            },
        },
        pages: {
            signIn: '/',
        }
    }
);

const env = process.env.NODE_ENV;

const blockedCountries = ['US', 'UK'];

export default function middleware(req: NextRequest) {
    const res = NextResponse.next()
    const pathname = req.nextUrl.pathname

    // Skip this middleware in development or if the path is international
    if (env !== 'development' && pathname !== '/blocked') {

        const country = req.geo?.country || req.headers.get('cloudfront-viewer-country') || req.headers.get('x-vercel-ip-country');

        if (blockedCountries.includes(country ?? '')) {
            return NextResponse.redirect(new URL('/blocked', req.url))
        }
    }


    const privatePathnameRegex = RegExp(
        `^(/(${locales.join('|')}))?(${privatePages
            .flatMap((p) => {
                // for '*'
                return p.replace(/\*/g, '.*');
            })
            .map((p) => (p === '/' ? ['', '/'] : p))
            .join('|')})/?$`,
        'i'
    );

    const isPrivatePage = privatePathnameRegex.test(req.nextUrl.pathname);

    if (!isPrivatePage) {
        const country = req.geo?.country || req.headers.get('cloudfront-viewer-country') || req.headers.get('x-vercel-ip-country');

        const response = intlMiddleware(req);
        response.cookies.set("client-counry", country || '');
        return response;
    } else {
        return (authMiddleware as any)(req);
    }
}


export const config = {
    matcher: [
        // Enable a redirect to a matching locale at the root
        '/',

        // Set a cookie to remember the previous locale for
        // all requests that have a locale prefix
        '/(en-US)/:path*',

        // Enable redirects that add missing locales
        // (e.g. `/pathnames` -> `/en/pathnames`)
        '/((?!api|_next|_vercel|.*\\..*).*)'
    ]
};

我在 Middleware 上发送了 API 请求,并根据传入的数据使用 NextResponse 重定向用户,但出现以下错误 “无法找到 next-intl 语言环境,因为中间件未根据此请求运行。”

reactjs next.js next-auth nextjs14 next-intl
1个回答
0
投票

出现错误“无法找到 next-intl 区域设置,因为中间件没有在此请求上运行”,因为 Next.js 中间件在边缘运行,并且 next-intl 期望中间件能够一致地应用来确定每个区域的区域设置要求。当在中间件文件中组合重定向、区域设置检测和用户授权时,这可能会很棘手。

要使用 next-intl 和身份验证处理被阻止的用户,同时避免此错误,您可以重构中间件,将基于国家/地区的重定向与基于区域设置的处理分开管理。具体操作方法如下:

第 1 步:将被阻止的用户逻辑与 next-intl 中间件分离

首先,我们将阻止用户检查移至中间件的顶部。这样,它就会在 next-intl 尝试检测区域设置或应用其配置之前执行。

步骤 2:确保 intlMiddleware 一致运行

intlMiddleware 应该是唯一负责处理本地化的函数。这种方法可确保它一致地应用于每个请求,从而解决与区域设置相关的错误。

import createMiddleware from 'next-intl/middleware';
import { defaultLocale, locales, pathnames } from "./config/lang-config";
import withAuth from 'next-auth/middleware';
import { NextRequest, NextResponse } from 'next/server';
import { getProfile } from './services/authQueries';

const privatePages = [
    '/profile',
    '/settings',
    '/settings/*',
    '/wallet',
    '/wallet/*',
    '/auth/logout',
];

const intlMiddleware = createMiddleware({
    defaultLocale,
    locales,
    localePrefix: "as-needed",
    pathnames,
    localeDetection: true,
});

const authMiddleware = withAuth(
    function onSuccess(req) {
        return intlMiddleware(req);
    },
    {
        callbacks: {
            authorized: async ({ token }: { token: any }) => {
                const accessToken = token?.tokenData?.token || token?.token as string;
                if (token) {
                    try {
                        const res = await getProfile(accessToken, 'en');
                        if (res.isSucceed) {
                            token.user = res.data;
                            return true;
                        } else {
                            throw new Error(res.message);
                        }
                    } catch (error) {
                        console.error(error instanceof Error ? error.message : 'An unknown error occurred');
                        return false;
                    }
                }
                return false;
            },
        },
        pages: {
            signIn: '/',
        }
    }
);

const env = process.env.NODE_ENV;
const blockedCountries = ['US', 'UK'];

export default async function middleware(req: NextRequest) {
    // Blocked user check
    const country = req.geo?.country || req.headers.get('cloudfront-viewer-country') || req.headers.get('x-vercel-ip-country');
    if (env !== 'development' && blockedCountries.includes(country ?? '')) {
        return NextResponse.redirect(new URL('/blocked', req.url));
    }

    const pathname = req.nextUrl.pathname;
    const privatePathnameRegex = new RegExp(
        `^(/(${locales.join('|')}))?(${privatePages
            .map((p) => p.replace(/\*/g, '.*'))
            .join('|')})/?$`,
        'i'
    );

    // Apply auth middleware on private pages
    if (privatePathnameRegex.test(pathname)) {
        return authMiddleware(req);
    }

    // Apply intlMiddleware on public pages and set country cookie
    const response = intlMiddleware(req);
    response.cookies.set("client-country", country || '');
    return response;
}

export const config = {
    matcher: [
        '/',
        '/(en-US)/:path*',
        '/((?!api|_next|_vercel|.*\\..*).*)'
    ]
};

被阻止的用户检查: 放置在最开始以确保在 intlMiddleware 运行之前重定向被阻止的国家/地区。

私有页面身份验证检查:我们将私有页面与 authMiddleware 进行匹配,同时将其他路由留给 intlMiddleware 来处理本地化。 一致的区域设置处理:通过像这样构造中间件,intlMiddleware 始终在非私有页面上运行,避免区域设置检测错误。

此设置应将被阻止的用户重定向到 /blocked,处理私有页面上的身份验证,并在公共页面上应用一致的区域设置检测和响应处理。

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