Руководство по обновлению Middleware
В процессе улучшения Middleware для общего доступа (GA) мы внесли некоторые изменения в API Middleware (и в том, как вы определяете Middleware в своем приложении) на основе вашего отклика.
Это руководство по обновлению поможет вам понять изменения, причины их внесения и способ миграции вашего существующего Middleware на новый API. Руководство предназначено для разработчиков Next.js, которые:
- В настоящее время используют бета-функции Next.js Middleware
- Выбирают обновить до следующей стабильной версии Next.js (
v12.2)
Вы можете начать обновление использования Middleware уже сегодня с последней версии (npm i next@latest).
Примечание: Эти изменения, описанные в этом руководстве, включены в Next.js
12.2. Вы можете сохранить текущую структуру сайта, включая вложенные Middleware, до перехода на12.2(илиcanaryсборку Next.js).
Если у вас настроен ESLint, вам потребуется выполнить npm i eslint-config-next@latest --save-dev для обновления вашей конфигурации ESLint, чтобы убедиться, что используется та же версия, что и версия Next.js. Также может потребоваться перезапустить VSCode, чтобы изменения вступили в силу.
Использование Next.js Middleware на Vercel
Если вы используете Next.js на Vercel, ваши существующие развертывания с использованием Middleware будут продолжать работать, и вы сможете продолжить развертывание сайта с использованием Middleware. При обновлении вашего сайта до следующей стабильной версии Next.js (v12.2), вам необходимо будет следовать этому руководству по обновлению, чтобы обновить ваш Middleware.
Изменения, требующие доработки
- Отсутствие вложенных Middleware
- Отсутствует тело ответа
- Переработанный API куков
- Новый помощник User-Agent
- Больше нет данных о сопоставлении страниц
- Выполнение Middleware для внутренних запросов Next.js
Отсутствие вложенных Middleware
Краткое описание изменений
- Определите один файл Middleware рядом с папкой
pages - Больше нет необходимости в префиксе подчеркивания
- Можно использовать пользовательский сопоставитель для определения маршрутов сопоставления с помощью экспортированного объекта конфигурации
Описание
Ранее вы могли создавать файл _middleware.ts в папке pages на любом уровне. Выполнение Middleware основывалось на пути файла, где он был создан.
На основе отзывов клиентов мы заменили этот API на единственный корневой Middleware, что обеспечивает следующие улучшения:
- Более быстрое выполнение с меньшей задержкой: С вложенными Middleware один запрос мог вызывать несколько функций Middleware. Один Middleware означает одно выполнение функции, что более эффективно.
- Меньшие затраты: Использование Middleware оплачивается за каждое обращение. С вложенными Middleware один запрос мог вызывать несколько функций Middleware, что означает несколько платежей за Middleware за запрос. Один Middleware означает одно обращение за запрос и более экономично.
-
Middleware может удобно фильтровать по параметрам, помимо маршрутов: С вложенными Middleware файлы Middleware располагались в папке
pages, и Middleware выполнялся на основе путей запросов. Перейдя к одному корневому Middleware, вы по-прежнему можете выполнять код на основе путей запросов, но теперь вы можете удобнее выполнять Middleware на основе других условий, например,cookies, или наличия заголовка запроса. -
Детерминированный порядок выполнения: С вложенными Middleware один запрос мог соответствовать нескольким функциям Middleware. Например, запрос к
/dashboard/users/*вызывал бы Middleware, определенные как в/dashboard/users/_middleware.ts, так и в/dashboard/_middleware.js. Однако порядок выполнения сложно предсказать. Переход к одному корневому Middleware более явно определяет порядок выполнения. - Поддержка макетов Next.js (RFC): Переход к одному корневому Middleware помогает в поддержке новых макетов (RFC) в Next.js.
Как обновить
Вы должны объявить один файл Middleware в своем приложении, который должен быть расположен рядом с папкой pages и иметь без префикса _. Ваш файл Middleware может по-прежнему иметь расширение .ts или .js.
Middleware будет вызываться для каждого маршрута в приложении, и можно использовать пользовательский сопоставитель для определения фильтров сопоставления. Вот пример для Middleware, который срабатывает для /about/* и /dashboard/:path*, пользовательский сопоставитель определен в экспортированном объекте конфигурации:
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
return NextResponse.rewrite(new URL('/about-2', request.url))
}
// Supports both a single string value or an array of matchers
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
}Конфигурация сопоставителя также поддерживает полные регулярные выражения, поэтому поддерживаются такие сопоставления, как отрицательные просмотры вперед или сопоставление символов. Пример отрицательного просмотра вперед для сопоставления всех, кроме определенных путей, можно увидеть здесь:
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - favicon.ico (favicon file)
*/
'/((?!api|_next/static|favicon.ico).*)',
],
}Хотя вариант конфигурации предпочтительнее, так как он не вызывается для каждого запроса, вы также можете использовать условные операторы, чтобы выполнять Middleware только тогда, когда он соответствует определенным путям. Преимущество использования условных операторов заключается в явном определении порядка выполнения Middleware. Следующий пример показывает, как объединить два ранее вложенных Middleware:
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
if (request.nextUrl.pathname.startsWith('/about')) {
// This logic is only applied to /about
}
if (request.nextUrl.pathname.startsWith('/dashboard')) {
// This logic is only applied to /dashboard
}
}Отсутствует тело ответа
Краткое описание изменений
- Middleware больше не может генерировать тело ответа
- Если ваш Middleware отвечает с телом, будет выброшено исключение времени выполнения
- Перейдите к использованию
rewrite/redirectдля страниц/API, обрабатывающих ответ
Описание
Чтобы учитывать различия в навигации на стороне клиента и сервера, и для обеспечения того, чтобы разработчики не создавали небезопасный Middleware, мы удаляем возможность отправлять тела ответов в Middleware. Это гарантирует, что Middleware используется только для rewrite, redirect, или изменения входящего запроса (например, установка куков).
Следующие шаблоны больше не будут работать:
new Response('a text value')
new Response(streamOrBuffer)
new Response(JSON.stringify(obj), { headers: 'application/json' })
NextResponse.json()
Как обновить
В случаях, когда Middleware используется для ответа (например, авторизация), вы должны перейти к использованию rewrite/redirect для страниц, которые отображают ошибку авторизации, формы входа или API-маршрут.
До обновления
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { isAuthValid } from './lib/auth'
export function middleware(request: NextRequest) {
// Example function to validate auth
if (isAuthValid(request)) {
return NextResponse.next()
}
return NextResponse.json({ message: 'Auth required' }, { status: 401 })
}После обновления
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { isAuthValid } from './lib/auth'
export function middleware(request: NextRequest) {
// Example function to validate auth
if (isAuthValid(request)) {
return NextResponse.next()
}
const loginUrl = new URL('/login', request.url)
loginUrl.searchParams.set('from', request.nextUrl.pathname)
return NextResponse.redirect(loginUrl)
}Маршруты Edge API
Если вы ранее использовали Middleware для пересылки заголовков во внешний API, теперь вы можете использовать маршруты Edge API:
import { type NextRequest } from 'next/server'
export const config = {
runtime: 'edge',
}
export default async function handler(req: NextRequest) {
const authorization = req.cookies.get('authorization')
return fetch('https://backend-api.com/api/protected', {
method: req.method,
headers: {
authorization,
},
redirect: 'manual',
})
}Переработанный API куков
Краткое описание изменений
| Добавленное | Удаленное |
|---|---|
cookies.set |
cookie |
cookies.delete |
clearCookie |
cookies.getWithOptions |
cookies |
Описание
На основе отзывов бета-тестеров, мы изменяем API куков в NextRequest и NextResponse для лучшей адаптации к модели get/set. API Cookies расширяет Map, включая методы, такие как entries и values.
Как обновить
NextResponse теперь имеет экземпляр cookies с:
cookies.deletecookies.setcookies.getWithOptions
А также другие расширенные методы из Map.
До обновления
// pages/_middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// create an instance of the class to access the public methods. This uses `next()`,
// you could use `redirect()` or `rewrite()` as well
let response = NextResponse.next()
// get the cookies from the request
let cookieFromRequest = request.cookies['my-cookie']
// set the `cookie`
response.cookie('hello', 'world')
// set the `cookie` with options
const cookieWithOptions = response.cookie('hello', 'world', {
path: '/',
maxAge: 1000 * 60 * 60 * 24 * 7,
httpOnly: true,
sameSite: 'strict',
domain: 'example.com',
})
// clear the `cookie`
response.clearCookie('hello')
return response
}
После обновления
export function middleware() {
const response = new NextResponse()
// set a cookie
response.cookies.set('vercel', 'fast')
// set another cookie with options
response.cookies.set('nextjs', 'awesome', { path: '/test' })
// get all the details of a cookie
const { value, ...options } = response.cookies.getWithOptions('vercel')
console.log(value) // => 'fast'
console.log(options) // => { name: 'vercel', Path: '/test' }
// deleting a cookie will mark it as expired
response.cookies.delete('vercel')
return response
}Новый помощник User-Agent
Краткое описание изменений
- Доступ к User-Agent больше недоступен в объекте запроса
- Мы добавили нового помощника
userAgentдля уменьшения размера Middleware на17kb.
Описание
Для уменьшения размера вашего Middleware мы извлекли User-Agent из объекта запроса и создали нового помощника userAgent.
Помощник импортируется из next/server и позволяет вам включить использование User-Agent. Помощник предоставляет доступ к тем же свойствам, которые были доступны из объекта запроса.
Как обновить
- Импортируйте помощника
userAgentизnext/server - Распакуйте необходимые свойства для работы
До обновления
import { NextRequest, NextResponse } from 'next/server'
export function middleware(request: NextRequest) {
const url = request.nextUrl
const viewport = request.ua.device.type === 'mobile' ? 'mobile' : 'desktop'
url.searchParams.set('viewport', viewport)
return NextResponse.rewrite(url)
}После обновления
import { NextRequest, NextResponse, userAgent } from 'next/server'
export function middleware(request: NextRequest) {
const url = request.nextUrl
const { device } = userAgent(request)
const viewport = device.type === 'mobile' ? 'mobile' : 'desktop'
url.searchParams.set('viewport', viewport)
return NextResponse.rewrite(url)
}Больше нет данных о сопоставлении страниц
Краткое описание изменений
- Используйте
URLPatternдля проверки вызова Middleware для определенного соответствия страницы
Описание
В настоящее время Middleware оценивает, обслуживаете ли вы ресурс страницы на основе манифеста маршрутов Next.js (внутренняя конфигурация). Это значение отображается через request.page.
Для повышения точности сопоставления страниц и ресурсов мы теперь используем веб-стандартный API URLPattern.
Как обновить
Используйте URLPattern для проверки, вызывается ли Middleware для определённого сопоставления страницы.
До
import { NextResponse } from 'next/server'
import type { NextRequest, NextFetchEvent } from 'next/server'
export function middleware(request: NextRequest, event: NextFetchEvent) {
const { params } = event.request.page
const { locale, slug } = params
if (locale && slug) {
const { search, protocol, host } = request.nextUrl
const url = new URL(`${protocol}//${locale}.${host}/${slug}${search}`)
return NextResponse.redirect(url)
}
}После
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
const PATTERNS = [
[
new URLPattern({ pathname: '/:locale/:slug' }),
({ pathname }) => pathname.groups,
],
]
const params = (url) => {
const input = url.split('?')[0]
let result = {}
for (const [pattern, handler] of PATTERNS) {
const patternResult = pattern.exec(input)
if (patternResult !== null && 'pathname' in patternResult) {
result = handler(patternResult)
break
}
}
return result
}
export function middleware(request: NextRequest) {
const { locale, slug } = params(request.url)
if (locale && slug) {
const { search, protocol, host } = request.nextUrl
const url = new URL(`${protocol}//${locale}.${host}/${slug}${search}`)
return NextResponse.redirect(url)
}
}Выполнение Middleware для внутренних запросов Next.js
Сводка изменений
- Middleware будет выполняться для всех запросов, включая
_next
Описание
До Next.js v12.2, Middleware не выполнялся для _next запросов.
В случаях, когда Middleware используется для авторизации, необходимо мигрировать на использование rewrite/redirect для страниц с ошибкой авторизации, форм входа или для маршрутов API.
См. Нет тела ответа для примера миграции на использование rewrite/redirect.
© 2024 Vercel, Inc.
Licensed under the MIT License.
https://nextjs.org/docs/messages/middleware-upgrade-guide