英文:
NextJS middleware called multiple times when accessing an ISR page
问题
export async function middleware(
request: NextRequest,
fetchEvent: NextFetchEvent
) {
console.log('request.nextUrl.pathname', request.nextUrl.pathname);
if (request.nextUrl.pathname.split('/').length === 2) {
return linkVisitMiddleware(request, fetchEvent);
}
}
这个中间件在调用时会出现2到3次(不知道为什么这些数字会波动)。
上面的日志会在服务器上打印出以下内容:
request.nextUrl.pathname /slug
request.nextUrl.pathname /slug
request.nextUrl.pathname /slug
奇怪的是,这只会在生产环境 (next start
) 中发生。在本地环境 (next serve
) 中,它会如预期地运行。
我看到了关于 reactStrictMode
的一些提及,但那并没有帮助
编辑:
在 NextJS 版本 13.1.1
、13.1.6
和 13.3.0
上进行了测试。
英文:
I have a NextJS application that hosts an ISR page that fetches data from Supabase. There's a middleware that records a page visit before rendering the page.
export async function middleware(
request: NextRequest,
fetchEvent: NextFetchEvent
) {
console.log('request.nextUrl.pathname', request.nextUrl.pathname);
if (request.nextUrl.pathname.split('/').length === 2) {
return linkVisitMiddleware(request, fetchEvent);
}
}
The issue the middleware is called 2 or 3 times (dont' know why these numbers fluctuate).
The above logging will print the following on the server:
request.nextUrl.pathname /slug
request.nextUrl.pathname /slug
request.nextUrl.pathname /slug
The weird thing, this happens only in production (next start
). Locally (next serve
), it runs once as expected.
I saw a couple of mentions about reactStrictMode
, but that didn't help
Edit:
Tested on NextJS 13.1.1
, 13.1.6
& 13.3.0
答案1
得分: 1
在经过数小时的调试后,我找到了问题所在。
ISR 页面正在使用回退策略 {fallback: true}
,因此它尝试请求 HTML 页面,如果不可用,则再次访问相同的路由以获取数据。
为了规避这个问题,我发现 NextJS 使用头部来区分请求的类型。以下是我的解决方案:
import { linksMiddleware } from 'middleware/links';
import { NextFetchEvent, NextRequest, NextResponse } from 'next/server';
export async function middleware(
request: NextRequest,
fetchEvent: NextFetchEvent
) {
// 这些方法用于访问区域路由时,我们将它们丢弃
if (request.method === 'OPTIONS' || request.method === 'HEAD') {
return NextResponse.next();
}
// 捕获 URL 区域(如果有)和 id。例如:/ar/random-id 或 /random-id
const pathParams = request.nextUrl.pathname.split('/');
if (pathParams.length === 2 && isDataRequest(request)) {
// 这意味着我们正在访问实体主页
return linksMiddleware(request, fetchEvent, 'entity');
}
return NextResponse.next();
}
export const config = {
matcher: [
// 这是为了防止在 API 路由和开发过程中运行中间件
'/((?!api|_next/static|_next/image|favicon.ico|favicon.svg).*)&',
],
};
/**
* 检查请求是否为回退页面的数据请求
* 初始请求中将缺少此标头
*/
const isDataRequest = (request: NextRequest): boolean =>
request.headers.get('x-nextjs-data') === '1' ||
// 在开发模式下,由于没有回退逻辑(类似于 SSR),因此标头将不会出现
process.env.NODE_ENV === 'development';
此外,我还添加了对 HTTP 方法的检查,以排除区域路由的情况,但保留了 OPTIONS
和 HEAD
。
英文:
After hours of debugging, I found the culprit.
The ISR page is using the fallback strategy as {fallback: true}
and because of this, it tries to request the HTML page, and if not available, it accesses the same route again to fetch the data.
To circumvent this, I found that NextJS employs headers to differentiate between the type of the request. Here's my solution:
import { linksMiddleware } from 'middleware/links';
import { NextFetchEvent, NextRequest, NextResponse } from 'next/server';
export async function middleware(
request: NextRequest,
fetchEvent: NextFetchEvent
) {
// These methods are used when accessing locale routes, so we discard them
if (request.method === 'OPTIONS' || request.method === 'HEAD') {
return NextResponse.next();
}
//Capture URL locale (if any) and id. For example: /ar/random-id or /random-id
const pathParams = request.nextUrl.pathname.split('/');
if (pathParams.length === 2 && isDataRequest(request)) {
// This means that we are accessing the entity homepage
return linksMiddleware(request, fetchEvent, 'entity');
}
return NextResponse.next();
}
export const config = {
matcher: [
// This is to prevent the middleware from running on the API routes and during development
'/((?!api|_next/static|_next/image|favicon.ico|favicon.svg).*)',
],
};
/**
* Check if the request is a data request for cases of fallback pages
* This header is absent in the initial request
*/
const isDataRequest = (request: NextRequest): boolean =>
request.headers.get('x-nextjs-data') === '1' ||
// The header is absent in development mode since there's no fallback logic (SSR-like)
process.env.NODE_ENV === 'development';
Also I added a check for the HTTP method for the cases of locale routing excluding OPTIONS
and HEAD
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论