Next.js第十六课 - 代理

0 阅读3分钟

前面我们学习了路由处理器,本节来聊聊代理。在实际开发中,我们经常需要处理跨域请求、API 代理等问题,Next.js 提供了几种配置代理的方式。

代理概述

Next.js 提供三种配置方式:

  1. Rewrites(重写)- URL 重写,用户不可见
  2. Redirects(重定向)- URL 重定向,用户可见
  3. 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 代理等问题。

如果你对本节内容有任何疑问,欢迎在评论区提出来,我们一起学习讨论。

原文地址:https://blog.uuhb.cn/archives/next-js-16.html