Промежуточное ПО
Промежуточное ПО позволяет выполнять код перед завершением запроса. Затем, в зависимости от входящего запроса, вы можете изменить ответ, переписав его, перенаправив, изменив заголовки запроса или ответа или ответив напрямую.
Промежуточное ПО выполняется перед кэшированным содержимым и сопоставлением маршрутов. Подробнее см. Сопоставление путей.
Примеры использования
Интеграция промежуточного ПО в ваше приложение может привести к существенному улучшению производительности, безопасности и пользовательского опыта. Некоторые распространённые сценарии, в которых промежуточное ПО особенно эффективно, включают:
- Аутентификация и авторизация: Убедитесь в идентификации пользователя и проверьте файлы cookie сеанса перед предоставлением доступа к определённым страницам или маршрутам 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пунктов назначения - Устанавливать файлы cookie ответа
- Устанавливать заголовки ответа
Для получения ответа из промежуточного ПО вы можете:
-
rewriteна маршрут (страницу или обработчик маршрута), который генерирует ответ - возвратить
NextResponseнапрямую. См. Создание ответа
Использование файлов cookie
Файлы cookie — это обычные заголовки. В Request они хранятся в заголовке Cookie . В Response они находятся в заголовке Set-Cookie . Next.js предоставляет удобный способ доступа и управления этими файлами cookie через расширение cookies в NextRequest и NextResponse.
- Для входящих запросов
cookiesпоставляется со следующими методами:get,getAll,set, иdeleteфайлы cookie. Вы можете проверить наличие файла cookie с помощьюhasили удалить все файлы cookie с помощью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 Заголовок запроса слишком большой в зависимости от конфигурации вашего веб-сервера на стороне бэкенда.
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*',
}Важно знать: Вы можете настроить заголовки CORS для отдельных маршрутов в обработчиках маршрутов.
Создание ответа
Вы можете ответить из промежуточного ПО напрямую, вернув экземпляр 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 были добавлены два дополнительных флага для межпроцессорного обмена данными, skipMiddlewareUrlNormalize и skipTrailingSlashRedirect для обработки сложных случаев.
skipTrailingSlashRedirect отключает перенаправления Next.js для добавления или удаления слешей в конце. Это позволяет настраивать обработку внутри межпроцессорного обмена данными для поддержания слеша в конце для некоторых путей, но не для других, что упрощает пошаговые миграции.
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
}Выполнение
В настоящее время межпроцессорный обмен данными поддерживает только среду выполнения на стороне клиента. Среда выполнения Node.js не может быть использована.
История версий
| Версия | Изменения |
|---|---|
v13.1.0 |
Добавлены дополнительные флаги для межпроцессорного обмена данными |
v13.0.0 |
Межпроцессорный обмен данными может изменять заголовки запроса, заголовки ответа и отправлять ответы |
v12.2.0 |
Межпроцессорный обмен данными стабилен, ознакомьтесь с руководством по обновлению |
v12.0.9 |
Принудительное использование абсолютных URL в среде выполнения на стороне клиента (PR) |
v12.0.0 |
Добавлен межпроцессорный обмен данными (бета-версия) |
© 2024 Vercel, Inc.
Licensed under the MIT License.
https://nextjs.org/docs/app/building-your-application/routing/middleware