Middleware
Middleware позволяет выполнять код перед завершением запроса. Затем, на основе входящего запроса, вы можете изменить ответ, переписав его, перенаправив, изменив заголовки запроса или ответа, либо ответив напрямую.
Middleware выполняется до сопоставления кэшированного контента и маршрутов. Подробнее см. в разделе Сопоставление путей.
Конвенция
Используйте файл middleware.ts (или .js) в корне вашего проекта для определения Middleware. Например, на том же уровне, что pages или app, или внутри src, если применимо.
Пример
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
// Эта функция может быть помечена как `async`, если внутри используется `await`
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url))
}
// Подробнее см. в разделе "Сопоставление путей"
export const config = {
matcher: '/about/:path*',
}import { NextResponse } from 'next/server'
// Эта функция может быть помечена как `async`, если внутри используется `await`
export function middleware(request) {
return NextResponse.redirect(new URL('/home', request.url))
}
// Подробнее см. в разделе "Сопоставление путей"
export const config = {
matcher: '/about/:path*',
}Сопоставление путей
Middleware будет вызываться для каждого маршрута в вашем проекте. Порядок выполнения следующий:
headersизnext.config.jsredirectsизnext.config.js- Middleware (
rewrites,redirectsи т.д.) beforeFiles(rewrites) изnext.config.js- Файловые маршруты (
public/,_next/static/,pages/,app/и т.д.) afterFiles(rewrites) изnext.config.js- Динамические маршруты (
/blog/[slug]) fallback(rewrites) изnext.config.js
Есть два способа определить, для каких путей будет выполняться Middleware:
Matcher
matcher позволяет фильтровать Middleware для выполнения на определённых путях.
export const config = {
matcher: '/about/:path*',
}Вы можете сопоставить один путь или несколько путей с помощью синтаксиса массива:
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
}Конфигурация matcher поддерживает полные регулярные выражения, включая негативные просмотры вперёд и сопоставление символов. Пример негативного просмотра вперёд для сопоставления всех путей, кроме определённых:
export const config = {
matcher: [
/*
* Сопоставить все пути запросов, кроме начинающихся с:
* - api (API маршруты)
* - _next/static (статические файлы)
* - _next/image (файлы оптимизации изображений)
* - favicon.ico (файл фавиконки)
*/
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
}Важно: Значения
matcherдолжны быть константами, чтобы их можно было статически проанализировать во время сборки. Динамические значения, такие как переменные, будут проигнорированы.
Настроенные matcher:
- ДОЛЖНЫ начинаться с
/ - Могут включать именованные параметры:
/about/:pathсоответствует/about/aи/about/b, но не/about/a/c - Могут иметь модификаторы для именованных параметров (начинаются с
:):/about/:path*соответствует/about/a/b/c, потому что*означает ноль или более.?означает ноль или один, а+означает один или более - Могут использовать регулярные выражения в скобках:
/about/(.*)эквивалентно/about/:path*
Подробнее см. в документации path-to-regexp.
Важно: Для обратной совместимости Next.js всегда рассматривает
/publicкак/public/index. Поэтому matcher/public/:pathбудет соответствовать.
Условные выражения
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
if (request.nextUrl.pathname.startsWith('/about')) {
return NextResponse.rewrite(new URL('/about-2', request.url))
}
if (request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.rewrite(new URL('/dashboard/user', request.url))
}
}import { NextResponse } from 'next/server'
export function middleware(request) {
if (request.nextUrl.pathname.startsWith('/about')) {
return NextResponse.rewrite(new URL('/about-2', request.url))
}
if (request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.rewrite(new URL('/dashboard/user', request.url))
}
}NextResponse
API NextResponse позволяет:
redirectперенаправлять входящий запрос на другой URLrewriteпереписывать ответ, отображая заданный URL- Устанавливать заголовки запросов для API маршрутов,
getServerSidePropsи направленийrewrite - Устанавливать cookies ответа
- Устанавливать заголовки ответа
Для создания ответа из Middleware вы можете:
rewriteна маршрут (Страницу или Edge API маршрут), который генерирует ответ- Вернуть
NextResponseнапрямую. См. Создание ответа
Использование Cookies
Cookies — это обычные заголовки. В Request они хранятся в заголовке Cookie. В Response они находятся в заголовке Set-Cookie. Next.js предоставляет удобный способ доступа и управления этими cookies через расширение cookies в NextRequest и NextResponse.
- Для входящих запросов
cookiesимеет следующие методы:get,getAll,setиdelete. Вы можете проверить наличие cookie с помощьюhasили удалить все cookies с помощьюclear. - Для исходящих ответов
cookiesимеют методыget,getAll,setиdelete.
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// Предположим, что во входящем запросе есть заголовок "Cookie:nextjs=fast"
// Получение cookies из запроса с помощью API `RequestCookies`
let cookie = request.cookies.get('nextjs')
console.log(cookie) // => { name: 'nextjs', value: 'fast', Path: '/' }
const allCookies = request.cookies.getAll()
console.log(allCookies) // => [{ name: 'nextjs', value: 'fast' }]
request.cookies.has('nextjs') // => true
request.cookies.delete('nextjs')
request.cookies.has('nextjs') // => false
// Установка cookies в ответе с помощью API `ResponseCookies`
const response = NextResponse.next()
response.cookies.set('vercel', 'fast')
response.cookies.set({
name: 'vercel',
value: 'fast',
path: '/',
})
cookie = response.cookies.get('vercel')
console.log(cookie) // => { name: 'vercel', value: 'fast', Path: '/' }
// Исходящий ответ будет иметь заголовок `Set-Cookie:vercel=fast;path=/test`.
return response
}import { NextResponse } from 'next/server'
export function middleware(request) {
// Предположим, что во входящем запросе есть заголовок "Cookie:nextjs=fast"
// Получение cookies из запроса с помощью API `RequestCookies`
let cookie = request.cookies.get('nextjs')
console.log(cookie) // => { name: 'nextjs', value: 'fast', Path: '/' }
const allCookies = request.cookies.getAll()
console.log(allCookies) // => [{ name: 'nextjs', value: 'fast' }]
request.cookies.has('nextjs') // => true
request.cookies.delete('nextjs')
request.cookies.has('nextjs') // => false
// Установка cookies в ответе с помощью API `ResponseCookies`
const response = NextResponse.next()
response.cookies.set('vercel', 'fast')
response.cookies.set({
name: 'vercel',
value: 'fast',
path: '/',
})
cookie = response.cookies.get('vercel')
console.log(cookie) // => { name: 'vercel', value: 'fast', Path: '/' }
// Исходящий ответ будет иметь заголовок `Set-Cookie:vercel=fast;path=/test`.
return response
}Установка заголовков
Вы можете устанавливать заголовки запроса и ответа с помощью API NextResponse (установка заголовков запроса доступна начиная с Next.js v13.0.0).
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// Клонируем заголовки запроса и устанавливаем новый заголовок `x-hello-from-middleware1`
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-hello-from-middleware1', 'hello')
// Вы также можете установить заголовки запроса в NextResponse.rewrite
const response = NextResponse.next({
request: {
// Новые заголовки запроса
headers: requestHeaders,
},
})
// Устанавливаем новый заголовок ответа `x-hello-from-middleware2`
response.headers.set('x-hello-from-middleware2', 'hello')
return response
}import { NextResponse } from 'next/server'
export function middleware(request) {
// Клонируем заголовки запроса и устанавливаем новый заголовок `x-hello-from-middleware1`
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-hello-from-middleware1', 'hello')
// Вы также можете установить заголовки запроса в NextResponse.rewrite
const response = NextResponse.next({
request: {
// Новые заголовки запроса
headers: requestHeaders,
},
})
// Устанавливаем новый заголовок ответа `x-hello-from-middleware2`
response.headers.set('x-hello-from-middleware2', 'hello')
return response
}Важно: Избегайте установки больших заголовков, так как это может вызвать ошибку 431 Request Header Fields Too Large в зависимости от конфигурации вашего сервера.
Создание ответа
Вы можете отвечать из Middleware напрямую, возвращая экземпляр Response или NextResponse. (Доступно начиная с Next.js v13.1.0)
import { NextRequest } from 'next/server'
import { isAuthenticated } from '@lib/auth'
// Ограничиваем Middleware путями, начинающимися с `/api/`
export const config = {
matcher: '/api/:function*',
}
export function middleware(request: NextRequest) {
// Вызываем нашу функцию аутентификации для проверки запроса
if (!isAuthenticated(request)) {
// Отвечаем JSON с сообщением об ошибке
return Response.json(
{ success: false, message: 'authentication failed' },
{ status: 401 }
)
}
}import { isAuthenticated } from '@lib/auth'
// Ограничиваем Middleware путями, начинающимися с `/api/`
export const config = {
matcher: '/api/:function*',
}
export function middleware(request) {
// Вызываем нашу функцию аутентификации для проверки запроса
if (!isAuthenticated(request)) {
// Отвечаем JSON с сообщением об ошибке
return Response.json(
{ success: false, message: 'authentication failed' },
{ status: 401 }
)
}
}Расширенные флаги Middleware
В v13.1 Next.js были добавлены два дополнительных флага для middleware: skipMiddlewareUrlNormalize и skipTrailingSlashRedirect для обработки сложных случаев использования.
skipTrailingSlashRedirect позволяет отключить стандартные перенаправления Next.js для добавления или удаления завершающих слэшей, что позволяет обрабатывать их вручную внутри middleware. Это может быть полезно для поддержки завершающего слэша для некоторых путей, но не для других, что упрощает постепенный переход.
module.exports = {
skipTrailingSlashRedirect: true,
}const legacyPrefixes = ['/docs', '/blog']
export default async function middleware(req) {
const { pathname } = req.nextUrl
if (legacyPrefixes.some((prefix) => pathname.startsWith(prefix))) {
return NextResponse.next()
}
// применяем обработку завершающего слэша
if (
!pathname.endsWith('/') &&
!pathname.match(/((?!\.well-known(?:\/.*)?)(?:[^/]+\/)*[^/]+\.\w+)/)
) {
req.nextUrl.pathname += '/'
return NextResponse.redirect(req.nextUrl)
}
}skipMiddlewareUrlNormalize позволяет отключить нормализацию URL, которую Next.js выполняет для унификации обработки прямых посещений и клиентских переходов. Это полезно в сложных случаях, когда требуется полный контроль с использованием оригинального URL.
module.exports = {
skipMiddlewareUrlNormalize: true,
}export default async function middleware(req) {
const { pathname } = req.nextUrl
// GET /_next/data/build-id/hello.json
console.log(pathname)
// с этим флагом будет /_next/data/build-id/hello.json
// без флага это было бы нормализовано в /hello
}История версий
| Версия | Изменения |
|---|---|
v13.1.0 | Добавлены расширенные флаги Middleware |
v13.0.0 | Middleware может изменять заголовки запросов, заголовки ответов и отправлять ответы |
v12.2.0 | Middleware стабилен, см. руководство по обновлению |
v12.0.9 | Принудительное использование абсолютных URL в Edge Runtime (PR) |
v12.0.0 | Добавлен Middleware (Beta) |