前面我们学习了路由处理器,本节来聊聊代理。在实际开发中,我们经常需要处理跨域请求、API 代理等问题,Next.js 提供了几种配置代理的方式。
代理概述
Next.js 提供三种配置方式:
- Rewrites(重写)- URL 重写,用户不可见
- Redirects(重定向)- URL 重定向,用户可见
- Middleware(中间件)- 动态代理逻辑
理解这三者的区别和使用场景很重要。
重写(Rewrites)
基本 Rewrite
重写允许你将一个路径映射到另一个路径,而不会改变用户看到的 URL:
// next.config.js
module.exports = {
async rewrites() {
return [
{
source: '/about',
destination: '/about-us',
},
{
source: '/blog/:slug',
destination: '/posts/:slug',
},
]
},
}
API 代理
最常见的用途是代理 API 请求,解决跨域问题:
// next.config.js
module.exports = {
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'https://external-api.com/:path*',
},
]
},
}
这样,所有 /api/* 的请求都会被代理到 https://external-api.com/*。
多个后端服务
module.exports = {
async rewrites() {
return [
{
source: '/api/users/:path*',
destination: 'https://user-service.com/:path*',
},
{
source: '/api/posts/:path*',
destination: 'https://post-service.com/:path*',
},
]
},
}
重定向(Redirects)
基本 Redirect
重定向会改变用户看到的 URL,并返回 307 或 308 状态码:
// next.config.js
module.exports = {
async redirects() {
return [
{
source: '/old-path',
destination: '/new-path',
permanent: false, // 临时重定向(307)
},
{
source: '/legacy/:path*',
destination: '/new/:path*',
permanent: true, // 永久重定向(308)
},
]
},
}
通配符重定向
module.exports = {
async redirects() {
return [
{
source: '/blog/:slug',
destination: '/articles/:slug',
permanent: true,
},
{
source: '/categories/:category/products/:product',
destination: '/products/:product',
permanent: true,
},
]
},
}
带查询参数的重定向
module.exports = {
async redirects() {
return [
{
source: '/old-page',
destination: '/new-page?from=old',
permanent: false,
},
]
},
}
中间件(Middleware)
中间件提供了更灵活的代理方式,可以在请求被处理前执行逻辑。
基本中间件
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// 重写请求
if (request.nextUrl.pathname.startsWith('/api/external')) {
const url = request.nextUrl.clone()
url.href = 'https://external-api.com' + request.nextUrl.pathname
return NextResponse.rewrite(url)
}
return NextResponse.next()
}
export const config = {
matcher: '/api/:path*',
}
动态代理
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const authHeader = request.headers.get('authorization')
// 检查认证
if (!authHeader) {
return NextResponse.redirect(new URL('/login', request.url))
}
// 代理请求到后端
if (request.nextUrl.pathname.startsWith('/api')) {
const url = new URL(request.url)
const backendUrl = `https://backend.com${url.pathname}${url.search}`
const proxyRequest = new Request(backendUrl, {
method: request.method,
headers: request.headers,
body: request.body,
})
return fetch(proxyRequest)
}
return NextResponse.next()
}
处理 CORS
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// 处理 OPTIONS 请求
if (request.method === 'OPTIONS') {
return NextResponse.json({}, {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
})
}
// 处理实际请求
const response = NextResponse.next()
// 添加 CORS 头
response.headers.set('Access-Control-Allow-Origin', '*')
response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization')
return response
}
常见场景
代理第三方 API
// next.config.js
module.exports = {
async rewrites() {
return [
{
source: '/api/external/:path*',
destination: 'https://api.external.com/:path*',
},
]
},
}
解决跨域问题
// next.config.js
module.exports = {
async headers() {
return [
{
source: '/api/:path*',
headers: [
{ key: 'Access-Control-Allow-Origin', value: '*' },
{ key: 'Access-Control-Allow-Methods', value: 'GET, POST, PUT, DELETE, OPTIONS' },
{ key: 'Access-Control-Allow-Headers', value: 'Content-Type, Authorization' },
],
},
]
},
}
移除路径前缀
module.exports = {
async rewrites() {
return [
{
source: '/api/v1/:path*',
destination: '/api/:path*',
},
]
},
}
实用建议
这里分享几个在配置代理时特别有用的技巧。
使用 Rewrites 处理 API 代理
实际开发中,我发现使用 Rewrites 是处理 API 代理最简单的方式:
// 推荐这样做 - 使用 Rewrites
{
source: '/api/:path*',
destination: 'https://backend.com/:path*',
}
// 虽然也可以自己实现代理逻辑,但没必要
使用 Redirects 处理 URL 变更
这里有个小建议:处理 URL 变更时,记得选择合适的配置方式:
// 推荐这样做 - 永久变更使用 Redirects
{
source: '/old-path',
destination: '/new-path',
permanent: true,
}
// 临时变更或不希望用户看到 URL 变化,才用 Rewrites
使用 Middleware 处理复杂逻辑
这个技巧特别有用——处理认证等复杂逻辑时,Middleware 是最佳选择:
// 推荐这样做 - 用 Middleware 处理认证等复杂逻辑
export function middleware(request: NextRequest) {
if (!isAuthenticated(request)) {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}
总结
本节我们学习了 Next.js 的代理配置,包括 Rewrites、Redirects 和 Middleware。合理使用这些配置能让你更好地处理跨域、API 代理等问题。
如果你对本节内容有任何疑问,欢迎在评论区提出来,我们一起学习讨论。