全栈框架如何处理跨域?

访客 全栈框架 1

本文目录导读:

  1. 核心原理:服务端设置 CORS 响应头
  2. 主流全栈框架的处理方式
  3. 最佳实践建议

这是一个非常经典且关键的全栈问题,处理跨域(CORS,跨源资源共享)是全栈开发中必不可少的一环,不同的全栈框架有不同的处理方式,但核心思路都是在服务端设置正确的响应头,或者通过代理转发请求

下面我会从原理主流框架的实践两个方面来解答。

核心原理:服务端设置 CORS 响应头

浏览器出于安全考虑,限制了跨源 HTTP 请求,要解决这个问题,关键在于服务端返回的响应中包含特定的 HTTP 头,告诉浏览器“这个请求是允许的”。

最关键的几个响应头是:

  • Access-Control-Allow-Origin:允许哪些域名访问( 表示所有域名,或指定具体域名)。
  • Access-Control-Allow-Methods:允许哪些 HTTP 方法(GET、POST、PUT、DELETE 等)。
  • Access-Control-Allow-Headers:允许哪些自定义请求头。
  • Access-Control-Allow-Credentials:是否允许携带 Cookie(设为 true 时,Allow-Origin 不能为 )。
  • Access-Control-Max-Age:预检请求(OPTIONS)的有效期。

简单请求(如 GET、POST 且 Content-Type 为 text/plain 等)浏览器直接发请求,看响应头。 非简单请求(如 PUT、DELETE 或自定义头)浏览器会先发一个 OPTIONS 预检请求,确认服务端允许后,再发真实请求。

主流全栈框架的处理方式

传统 SSR 框架(如 Next.js、Nuxt.js、Remix)

这类框架通常在服务端渲染页面,API 路由也在同一域名下,开发时的跨域问题主要出在前端开发服务器(如 localhost:3000)请求后端 API(如 localhost:4000)。

  • 在 API 路由中手动设置 CORS(生产环境) 在服务端(如 Next.js API 路由、Nuxt 的 server 目录)中,自己设置响应头。

    // Next.js API Route (pages/api/hello.ts)
    import { NextApiRequest, NextApiResponse } from 'next'
    export default function handler(req: NextApiRequest, res: NextApiResponse) {
      // 设置 CORS 头
      res.setHeader('Access-Control-Allow-Origin', 'https://your-frontend-domain.com')
      res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
      res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization')
      // 处理 OPTIONS 预检请求
      if (req.method === 'OPTIONS') {
        return res.status(200).end()
      }
      res.status(200).json({ message: 'Hello' })
    }
  • 使用 cors 中间件(推荐) 这是最简洁的方式,在 Next.js 或 Express 中,直接使用 cors 包。

    // Next.js API Route (app/api/hello/route.ts)
    import { NextRequest, NextResponse } from 'next/server'
    import Cors from 'cors'
    // 初始化 cors 中间件
    const cors = Cors({
      origin: 'https://your-frontend-domain.com',
      methods: ['GET', 'POST'],
    })
    export async function GET(request: NextRequest) {
      // 执行中间件(Next.js 中需要手动运行)
      await new Promise((resolve, reject) => {
        cors(request as any, {} as any, (result) => {
          if (result instanceof Error) reject(result)
          resolve(result)
        })
      })
      return NextResponse.json({ message: 'Hello' })
    }
  • 开发时使用代理(最常用)next.config.jsvite.config.ts 中配置代理,让前端开发服务器把 /api 请求转发到后端,从而不触发跨域

    // next.config.js
    module.exports = {
      async rewrites() {
        return [
          {
            source: '/api/:path*',
            destination: 'http://localhost:4000/api/:path*', // 代理到后端
          },
        ]
      },
    }
    // vite.config.ts (Nuxt 类似)
    export default defineConfig({
      server: {
        proxy: {
          '/api': {
            target: 'http://localhost:4000',
            changeOrigin: true,
          },
        },
      },
    })

全栈语言框架(Next.js、Nuxt、SvelteKit、Remix 等)

这些框架的全栈特性意味着你可以在前端页面里直接写后端代码(如 getServerSidePropsload 函数、+page.server.ts 等),这些代码运行在服务端,服务端发起 HTTP 请求没有跨域问题

<!-- SvelteKit +page.server.ts -->
import { error } from '@sveltejs/kit'
/** @type {import('./$types').PageServerLoad} */
export async function load({ fetch }) {
  // 这个 fetch 运行在服务端,不存在跨域
  const res = await fetch('http://internal-api.example.com/data')
  if (!res.ok) throw error(500, 'API 请求失败')
  return { items: await res.json() }
}

这种方式的优点是避免了 CORS 配置,但缺点是需要后端 API 对服务端环境是可达的(内网或同一网络)。

Node.js + Express / Koa 后端框架

这是最标准的后端处理方式,直接使用 cors 中间件挂载到全局或特定路由。

// Express 后端
const express = require('express')
const cors = require('cors')
const app = express()
// 允许所有来源(开发用,生产环境要指定域名)
app.use(cors({
  origin: 'https://your-frontend.com',
  credentials: true, // 允许携带 Cookie
}))
// 或者只为一个路由启用
// app.get('/api/user', cors(), (req, res) => { ... })
app.listen(3001)

BFF(Backend For Frontend)模式

这种模式专门解决跨域和 API 聚合问题,BFF 层位于前端和后端服务之间,前端只请求 BFF,BFF 向后端服务发送请求(无跨域),再将结果返回前端(BFF 自己设置 CORS)。

// 一个简单的 BFF 服务
import express from 'express'
import cors from 'cors'
import axios from 'axios'
const app = express()
app.use(cors({ origin: 'http://localhost:5173' })) // 只允许前端域名
app.get('/api/user', async (req, res) => {
  const response = await axios.get('http://internal-backend:4000/user')
  res.json(response.data)
})

最佳实践建议

场景 推荐方案
生产环境 后端服务(API 层)明确配置 CORS,只允许受信任的前端域名,不要用 。
开发环境 使用代理(Vite、Next.js rewrites、webpack-dev-server),简单且不影响生产配置。
想要彻底避免 CORS 在服务端渲染/加载数据(如 getServerSidePropsload 函数),请求走服务端。
微服务架构 使用 BFF 模式,前端只请求 BFF,BFF 负责聚合下游服务。
需要携带 Cookie 服务端 CORS 必须指定具体域名(不能是 ),且 credentials: true

一句话总结:尽量让前端请求走服务端代码或同源代理,如果不行,就用 CORS 中间件精确控制允许的来源和方法。

标签: 同源策略

抱歉,评论功能暂时关闭!