如果进入网站的用户的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 语言环境,因为中间件未根据此请求运行。”
出现错误“无法找到 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,处理私有页面上的身份验证,并在公共页面上应用一致的区域设置检测和响应处理。