ПОСРЕДНИК
Посредник позволяет запускать код до завершения запроса. Затем, исходя из входящего запроса, вы можете изменить ответ, переписав его, перенаправив его, изменив заголовки запроса или ответа или напрямую выдав ответ.
Посредник выполняется до кешированного контента и сопоставления маршрутов. Подробнее см. Сопоставление путей.
Сценарии использования
Интеграция посредника в ваше приложение может привести к существенному улучшению производительности, безопасности и пользовательского опыта. Некоторые распространенные сценарии, где посредник особенно эффективен, включают:
- Аутентификация и авторизация: Убедитесь в идентичности пользователя и проверьте куки сессии перед предоставлением доступа к определенным страницам или маршрутам API.
- Перенаправление на стороне сервера: Перенаправляйте пользователей на уровне сервера на основе определенных условий (например, языка, роли пользователя).
- Переписывание пути: Поддерживайте A/B-тестирование, развертывание функций или устаревшие пути, динамически переписывая пути к маршрутам API или страницам на основе свойств запроса.
- Обнаружение ботов: Защитите свои ресурсы, обнаруживая и блокируя трафик ботов.
- Ведение журнала и аналитика: Собирайте и анализируйте данные запроса для получения информации перед обработкой страницей или API.
- Флаговая система функций: Динамически включайте или отключайте функции для беспрепятственного развертывания или тестирования функций.
Важен также анализ ситуаций, где посредник может не быть оптимальным решением. Вот некоторые сценарии, на которые следует обратить внимание:
- Сложная обработка и извлечение данных: Посредник не предназначен для непосредственного извлечения или обработки данных; это следует выполнять в обработчиках маршрутов или утилитах серверной стороны.
- Затратные вычислительные задачи: Посредник должен быть лёгким и быстро реагировать, иначе он может привести к задержкам при загрузке страницы. Затратные вычислительные задачи или длительные процессы должны выполняться в выделенных обработчиках маршрутов.
- Расширенное управление сессиями: Хотя посредник может обрабатывать базовые задачи управления сессиями, расширенное управление сессиями должно осуществляться с помощью специализированных служб аутентификации или внутри обработчиков маршрутов.
- Прямые операции с базой данных: Не рекомендуется выполнять прямые операции с базой данных внутри посредника. Взаимодействие с базой данных должно выполняться в обработчиках маршрутов или утилитах серверной стороны.
Конвенция
Используйте файл middleware.ts (или .js) в корне вашего проекта для определения посредника. Например, на том же уровне, что и pages или app, или внутри src при необходимости.
Примечание: Хотя в проекте поддерживается только один файл
middleware.ts, вы всё равно можете организовывать логику посредника модульно. Разбейте функциональность посредника на отдельные файлы.tsили.jsи импортируйте их в ваш основной файлmiddleware.ts. Это позволяет более организованно управлять посредниками, относящимися к конкретному маршруту, которые объединяются вmiddleware.tsдля централизованного управления. Использование одного файла посредника упрощает конфигурацию, предотвращает потенциальные конфликты и оптимизирует производительность, избегая нескольких слоёв посредников.
Пример
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
// This function can be marked `async` if using `await` inside
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url))
}
// See "Matching Paths" below to learn more
export const config = {
matcher: '/about/:path*',
}Сопоставление путей
Посредник будет вызываться для каждого маршрута в вашем проекте. В связи с этим крайне важно использовать сопоставители для точной настройки или исключения определённых маршрутов. Порядок выполнения:
-
headersизnext.config.js -
redirectsизnext.config.js - Посредник (
rewrites,redirects, и т.д.) -
beforeFiles(rewrites) изnext.config.js - Маршруты файловой системы (
public/,_next/static/,pages/,app/, и т.д.) -
afterFiles(rewrites) изnext.config.js - Динамические маршруты (
/blog/[slug]) -
fallback(rewrites) изnext.config.js
Существует два способа определения путей, на которых будет работать посредник:
Сопоставитель
matcher позволяет фильтровать посредника для запуска по определённым путям.
export const config = {
matcher: '/about/:path*',
}Вы можете сопоставить один путь или несколько путей с помощью синтаксиса массива:
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
}Конфигурация matcher позволяет использовать полные регулярные выражения, поэтому поддерживаются сопоставления, например, с отрицательными предвосхищениями или сопоставлениями символов. Пример использования отрицательного предвосхищения для сопоставления всех путей, кроме определённых, можно увидеть здесь:
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
}Вы также можете обойти посредника для определённых запросов, используя массивы missing или has, или их комбинацию:
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
{
source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
missing: [
{ type: 'header', key: 'next-router-prefetch' },
{ type: 'header', key: 'purpose', value: 'prefetch' },
],
},
{
source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
has: [
{ type: 'header', key: 'next-router-prefetch' },
{ type: 'header', key: 'purpose', value: 'prefetch' },
],
},
{
source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
has: [{ type: 'header', key: 'x-present' }],
missing: [{ type: 'header', key: 'x-missing', value: 'prefetch' }],
},
],
}Важно знать: Значения
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. Следовательно, сопоставитель/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))
}
}NextResponse
API NextResponse позволяет:
-
redirectвходящий запрос на другой URL -
rewriteответ, отобразив заданный URL - Устанавливать заголовки запроса для маршрутов API,
getServerSideProps, и пунктов назначенияrewrite - Устанавливать куки ответа
- Устанавливать заголовки ответа
Для создания ответа из посредника вы можете:
-
rewriteк маршруту (Страница или Маршрут API-шлюза), который генерирует ответ - Возвратить
NextResponseнепосредственно. См. Генерация ответа
Использование куки
Куки — это обычные заголовки. В Request, они хранятся в заголовке Cookie. В Response, они находятся в заголовке Set-Cookie. Next.js предоставляет удобный способ доступа и управления этими куки через расширение cookies в NextRequest и NextResponse.
- Для входящих запросов,
cookiesпоставляется со следующими методами:get,getAll,set, иdeleteкуки. Вы можете проверить наличие куки с помощьюhasили удалить все куки с помощьюclear. - Для исходящих ответов,
cookiesимеют следующие методыget,getAll,set, иdelete.
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// Assume a "Cookie:nextjs=fast" header to be present on the incoming request
// Getting cookies from the request using the `RequestCookies` API
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
// Setting cookies on the response using the `ResponseCookies` API
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: '/' }
// The outgoing response will have a `Set-Cookie:vercel=fast;path=/` header.
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) {
// Clone the request headers and set a new header `x-hello-from-middleware1`
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-hello-from-middleware1', 'hello')
// You can also set request headers in NextResponse.rewrite
const response = NextResponse.next({
request: {
// New request headers
headers: requestHeaders,
},
})
// Set a new response header `x-hello-from-middleware2`
response.headers.set('x-hello-from-middleware2', 'hello')
return response
}Важно знать: Избегайте установки больших заголовков, так как это может привести к ошибке 431 Request Header Fields Too Large, в зависимости от конфигурации вашего сервера веб-приложения.
CORS
Вы можете установить заголовки CORS в посреднике, чтобы разрешить запросы с разных сайтов, включая простые и предварительно запрошенные запросы.
import { NextRequest, NextResponse } from 'next/server'
const allowedOrigins = ['https://acme.com', 'https://my-app.org']
const corsOptions = {
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
}
export function middleware(request: NextRequest) {
// Check the origin from the request
const origin = request.headers.get('origin') ?? ''
const isAllowedOrigin = allowedOrigins.includes(origin)
// Handle preflighted requests
const isPreflight = request.method === 'OPTIONS'
if (isPreflight) {
const preflightHeaders = {
...(isAllowedOrigin && { 'Access-Control-Allow-Origin': origin }),
...corsOptions,
}
return NextResponse.json({}, { headers: preflightHeaders })
}
// Handle simple requests
const response = NextResponse.next()
if (isAllowedOrigin) {
response.headers.set('Access-Control-Allow-Origin', origin)
}
Object.entries(corsOptions).forEach(([key, value]) => {
response.headers.set(key, value)
})
return response
}
export const config = {
matcher: '/api/:path*',
}Генерация ответа
Вы можете сгенерировать ответ из посредника напрямую, вернув экземпляр Response или NextResponse. (Это доступно с Next.js v13.1.0)
import { NextRequest } from 'next/server'
import { isAuthenticated } from '@lib/auth'
// Limit the middleware to paths starting with `/api/`
export const config = {
matcher: '/api/:function*',
}
export function middleware(request: NextRequest) {
// Call our authentication function to check the request
if (!isAuthenticated(request)) {
// Respond with JSON indicating an error message
return Response.json(
{ success: false, message: 'authentication failed' },
{ status: 401 }
)
}
}waitUntil и NextFetchEvent
Объект NextFetchEvent расширяет стандартный объект FetchEvent и включает метод waitUntil().
Метод waitUntil() принимает обещание в качестве аргумента и продлевает время существования посредника до выполнения обещания. Это полезно для выполнения работы в фоновом режиме.
import { NextResponse } from 'next/server'
import type { NextFetchEvent, NextRequest } from 'next/server'
export function middleware(req: NextRequest, event: NextFetchEvent) {
event.waitUntil(
fetch('https://my-analytics-platform.com', {
method: 'POST',
body: JSON.stringify({ pathname: req.nextUrl.pathname }),
})
)
return NextResponse.next()
}Расширенные флаги посредника
В 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()
}
// apply trailing slash handling
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)
// with the flag this now /_next/data/build-id/hello.json
// without the flag this would be normalized to /hello
}Runtime
Middleware в настоящее время поддерживает только Edge runtime. Node.js runtime использовать нельзя.
История версий
| Версия | Изменения |
|---|---|
v13.1.0 |
Добавлены флаги расширенного middleware |
v13.0.0 |
Middleware может изменять заголовки запроса, заголовки ответа и отправлять ответы |
v12.2.0 |
Middleware стабилен, см. руководство по обновлению |
v12.0.9 |
Принудительное использование абсолютных URL в Edge Runtime (PR) |
v12.0.0 |
Добавлен middleware (Бета-версия) |
© 2024 Vercel, Inc.
Licensed under the MIT License.
https://nextjs.org/docs/pages/building-your-application/routing/middleware