Middleware
Le Middleware vous permet d'exécuter du code avant qu'une requête ne soit complétée. Ensuite, en fonction de la requête entrante, vous pouvez modifier la réponse en réécrivant, redirigeant, modifiant les en-têtes de requête ou de réponse, ou en répondant directement.
Le Middleware s'exécute avant que le contenu mis en cache et les routes ne soient appariés. Voir Appariement des chemins pour plus de détails.
Convention
Utilisez le fichier middleware.ts (ou .js) à la racine de votre projet pour définir le Middleware. Par exemple, au même niveau que pages ou app, ou à l'intérieur de src si applicable.
Exemple
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
// Cette fonction peut être marquée `async` si vous utilisez `await` à l'intérieur
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url))
}
// Voir "Appariement des chemins" ci-dessous pour en savoir plus
export const config = {
matcher: '/about/:path*',
}import { NextResponse } from 'next/server'
// Cette fonction peut être marquée `async` si vous utilisez `await` à l'intérieur
export function middleware(request) {
return NextResponse.redirect(new URL('/home', request.url))
}
// Voir "Appariement des chemins" ci-dessous pour en savoir plus
export const config = {
matcher: '/about/:path*',
}Appariement des chemins
Le Middleware sera invoqué pour chaque route de votre projet. Voici l'ordre d'exécution :
headersdepuisnext.config.jsredirectsdepuisnext.config.js- Middleware (
rewrites,redirects, etc.) beforeFiles(rewrites) depuisnext.config.js- Routes du système de fichiers (
public/,_next/static/,pages/,app/, etc.) afterFiles(rewrites) depuisnext.config.js- Routes dynamiques (
/blog/[slug]) fallback(rewrites) depuisnext.config.js
Il existe deux façons de définir sur quels chemins le Middleware s'exécutera :
Matcher
matcher vous permet de filtrer le Middleware pour qu'il s'exécute sur des chemins spécifiques.
export const config = {
matcher: '/about/:path*',
}Vous pouvez apparier un seul chemin ou plusieurs chemins avec une syntaxe de tableau :
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
}La configuration matcher permet des expressions régulières complètes, donc des appariements comme les lookaheads négatifs ou les appariements de caractères sont pris en charge. Un exemple de lookahead négatif pour apparier tous les chemins sauf certains spécifiques peut être vu ici :
export const config = {
matcher: [
/*
* Apparier tous les chemins de requête sauf ceux commençant par :
* - api (routes API)
* - _next/static (fichiers statiques)
* - _next/image (fichiers d'optimisation d'image)
* - favicon.ico (fichier favicon)
*/
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
}Bon à savoir : Les valeurs de
matcherdoivent être des constantes pour pouvoir être analysées statiquement au moment de la construction. Les valeurs dynamiques comme les variables seront ignorées.
Les matchers configurés :
- DOIVENT commencer par
/ - Peuvent inclure des paramètres nommés :
/about/:pathappariera/about/aet/about/bmais pas/about/a/c - Peuvent avoir des modificateurs sur les paramètres nommés (commençant par
:) :/about/:path*appariera/about/a/b/ccar*signifie zéro ou plus.?signifie zéro ou un et+un ou plus - Peuvent utiliser des expressions régulières entre parenthèses :
/about/(.*)est identique à/about/:path*
Lisez plus de détails dans la documentation path-to-regexp.
Bon à savoir : Pour des raisons de compatibilité ascendante, Next.js considère toujours
/publiccomme/public/index. Par conséquent, un matcher de/public/:pathappariera.
Instructions conditionnelles
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
L'API NextResponse vous permet de :
redirectla requête entrante vers une URL différenterewritela réponse en affichant une URL donnée- Définir des en-têtes de requête pour les routes API,
getServerSideProps, et les destinations derewrite - Définir des cookies de réponse
- Définir des en-têtes de réponse
Pour produire une réponse depuis le Middleware, vous pouvez :
rewritevers une route (Page ou Edge API Route) qui produit une réponse- retourner une
NextResponsedirectement. Voir Produire une réponse
Utilisation des cookies
Les cookies sont des en-têtes réguliers. Sur une Request, ils sont stockés dans l'en-tête Cookie. Sur une Response, ils sont dans l'en-tête Set-Cookie. Next.js fournit un moyen pratique d'accéder et de manipuler ces cookies via l'extension cookies sur NextRequest et NextResponse.
- Pour les requêtes entrantes,
cookiesdispose des méthodes suivantes :get,getAll,set, etdeletecookies. Vous pouvez vérifier l'existence d'un cookie avechasou supprimer tous les cookies avecclear. - Pour les réponses sortantes,
cookiesdispose des méthodesget,getAll,set, etdelete.
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// Supposons qu'un en-tête "Cookie:nextjs=fast" soit présent sur la requête entrante
// Récupération des cookies de la requête en utilisant l'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
// Définition des cookies sur la réponse en utilisant l'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: '/' }
// La réponse sortante aura un en-tête `Set-Cookie:vercel=fast;path=/test`.
return response
}import { NextResponse } from 'next/server'
export function middleware(request) {
// Supposons qu'un en-tête "Cookie:nextjs=fast" soit présent sur la requête entrante
// Récupération des cookies de la requête en utilisant l'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
// Définition des cookies sur la réponse en utilisant l'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: '/' }
// La réponse sortante aura un en-tête `Set-Cookie:vercel=fast;path=/test`.
return response
}Définition des en-têtes
Vous pouvez définir des en-têtes de requête et de réponse en utilisant l'API NextResponse (la définition des en-têtes de requête est disponible depuis Next.js v13.0.0).
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// Clonez les en-têtes de requête et définissez un nouvel en-tête `x-hello-from-middleware1`
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-hello-from-middleware1', 'hello')
// Vous pouvez également définir des en-têtes de requête dans NextResponse.rewrite
const response = NextResponse.next({
request: {
// Nouveaux en-têtes de requête
headers: requestHeaders,
},
})
// Définissez un nouvel en-tête de réponse `x-hello-from-middleware2`
response.headers.set('x-hello-from-middleware2', 'hello')
return response
}import { NextResponse } from 'next/server'
export function middleware(request) {
// Clonez les en-têtes de requête et définissez un nouvel en-tête `x-hello-from-middleware1`
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-hello-from-middleware1', 'hello')
// Vous pouvez également définir des en-têtes de requête dans NextResponse.rewrite
const response = NextResponse.next({
request: {
// Nouveaux en-têtes de requête
headers: requestHeaders,
},
})
// Définissez un nouvel en-tête de réponse `x-hello-from-middleware2`
response.headers.set('x-hello-from-middleware2', 'hello')
return response
}Bon à savoir : Évitez de définir des en-têtes volumineux car cela pourrait causer une erreur 431 Request Header Fields Too Large en fonction de la configuration de votre serveur web backend.
Production d'une réponse
Vous pouvez répondre directement depuis le Middleware en retournant une instance Response ou NextResponse. (Ceci est disponible depuis Next.js v13.1.0)
import { NextRequest } from 'next/server'
import { isAuthenticated } from '@lib/auth'
// Limitez le Middleware aux chemins commençant par `/api/`
export const config = {
matcher: '/api/:function*',
}
export function middleware(request: NextRequest) {
// Appelez notre fonction d'authentification pour vérifier la requête
if (!isAuthenticated(request)) {
// Répondez avec un JSON indiquant un message d'erreur
return Response.json(
{ success: false, message: 'Échec de l\'authentification' },
{ status: 401 }
)
}
}import { isAuthenticated } from '@lib/auth'
// Limitez le Middleware aux chemins commençant par `/api/`
export const config = {
matcher: '/api/:function*',
}
export function middleware(request) {
// Appelez notre fonction d'authentification pour vérifier la requête
if (!isAuthenticated(request)) {
// Répondez avec un JSON indiquant un message d'erreur
return Response.json(
{ success: false, message: 'Échec de l\'authentification' },
{ status: 401 }
)
}
}Drapeaux avancés du Middleware
Dans v13.1 de Next.js, deux drapeaux supplémentaires ont été introduits pour le Middleware, skipMiddlewareUrlNormalize et skipTrailingSlashRedirect pour gérer des cas d'utilisation avancés.
skipTrailingSlashRedirect permet de désactiver les redirections par défaut de Next.js pour ajouter ou supprimer des barres obliques finales, permettant une gestion personnalisée dans le Middleware qui peut maintenir la barre oblique finale pour certains chemins mais pas d'autres, facilitant les migrations incrémentielles.
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()
}
// appliquer la gestion des barres obliques finales
if (
!pathname.endsWith('/') &&
!pathname.match(/((?!\.well-known(?:\/.*)?)(?:[^/]+\/)*[^/]+\.\w+)/)
) {
req.nextUrl.pathname += '/'
return NextResponse.redirect(req.nextUrl)
}
}skipMiddlewareUrlNormalize permet de désactiver la normalisation d'URL que Next.js effectue pour rendre la gestion des visites directes et des transitions client identiques. Il existe des cas avancés où vous avez besoin d'un contrôle total en utilisant l'URL originale que cela débloque.
module.exports = {
skipMiddlewareUrlNormalize: true,
}export default async function middleware(req) {
const { pathname } = req.nextUrl
// GET /_next/data/build-id/hello.json
console.log(pathname)
// avec le drapeau ce sera /_next/data/build-id/hello.json
// sans le drapeau ce serait normalisé en /hello
}Historique des versions
| Version | Changements |
|---|---|
v13.1.0 | Drapeaux avancés du Middleware ajoutés |
v13.0.0 | Le Middleware peut modifier les en-têtes de requête, les en-têtes de réponse et envoyer des réponses |
v12.2.0 | Le Middleware est stable, veuillez consulter le guide de mise à niveau |
v12.0.9 | Application des URL absolues dans Edge Runtime (PR) |
v12.0.0 | Middleware (Bêta) ajouté |