Spec-Zone.ru › Next.js

Промежуточное ПО

Промежуточное ПО позволяет выполнять код перед завершением запроса. Затем, в зависимости от входящего запроса, вы можете изменить ответ, переписав его, перенаправив, изменив заголовки запроса или ответа или ответив напрямую.

Промежуточное ПО выполняется перед кэшированным содержимым и сопоставлением маршрутов. Подробнее см. Сопоставление путей.

Примеры использования

Интеграция промежуточного ПО в ваше приложение может привести к существенному улучшению производительности, безопасности и пользовательского опыта. Некоторые распространённые сценарии, в которых промежуточное ПО особенно эффективно, включают:

  • Аутентификация и авторизация: Убедитесь в идентификации пользователя и проверьте файлы 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*',
}

Сопоставление путей

Промежуточное ПО будет вызываться для каждого маршрута в вашем проекте. Учитывая это, крайне важно использовать сопоставители для точного указания или исключения конкретных маршрутов. Порядок выполнения:

  1. headers из next.config.js
  2. redirects из next.config.js
  3. Промежуточное ПО (rewrites, redirects, и т. д.)
  4. beforeFiles (rewrites) из next.config.js
  5. Маршруты файловой системы (public/, _next/static/, pages/, app/, и т. д.)
  6. afterFiles (rewrites) из next.config.js
  7. Динамические маршруты (/blog/[slug])
  8. fallback (rewrites) из next.config.js

Существует два способа определения путей, для которых будет работать промежуточное ПО:

  1. Настройка пользовательского сопоставителя
  2. Условные операторы

Сопоставитель

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 должны быть константами, чтобы их можно было статически проанализировать во время сборки. Динамические значения, такие как переменные, будут игнорироваться.

Настроенные сопоставители:

  1. ДОЛЖНЫ начинаться с /
  2. Могут включать именованные параметры: /about/:path соответствует /about/a и /about/b, но не /about/a/c
  3. Могут иметь модификаторы именованных параметров (начиная с :) : /about/:path* соответствует /about/a/b/c, потому что * означает «ноль или более». ? означает «ноль или один», а + — «один или более»
  4. Могут использовать регулярные выражения, заключённые в скобки: /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 ответа
  • Устанавливать заголовки ответа

Для получения ответа из промежуточного ПО вы можете:

  1. rewrite на маршрут (страницу или обработчик маршрута), который генерирует ответ
  2. возвратить NextResponse напрямую. См. Создание ответа

Использование файлов cookie

Файлы cookie — это обычные заголовки. В Request они хранятся в заголовке Cookie . В Response они находятся в заголовке Set-Cookie . Next.js предоставляет удобный способ доступа и управления этими файлами cookie через расширение cookies в NextRequest и NextResponse.

  1. Для входящих запросов cookies поставляется со следующими методами: get, getAll, set, и delete файлы cookie. Вы можете проверить наличие файла cookie с помощью has или удалить все файлы cookie с помощью clear.
  2. Для исходящих ответов 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()
}
END_OF_DOCUMENT_MARKER

Дополнительные флаги для межпроцессорного обмена данных

В 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API