Skip to content

Flutter Web CORS 错误终极解决方案

问题描述

在使用 Flutter Web 开发时,许多开发者会遇到 CORS (跨域资源共享) 错误,特别是在调用第三方 API 时。典型的错误信息如下:

Access to XMLHttpRequest at 'https://api.example.com' from origin 'http://localhost:52700' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

这个问题出现的原因是浏览器出于安全考虑,默认阻止跨域请求。虽然移动应用不受此限制,但 Web 应用必须遵守同源策略。

解决方案概览

解决 CORS 问题主要有三种方法:

  1. 服务端解决方案(推荐):在服务器端配置 CORS 头信息
  2. 开发环境解决方案:临时禁用浏览器安全策略用于开发测试
  3. 请求优化方案:调整请求方式避免触发预检请求

重要提示

生产环境中必须使用服务端解决方案,禁用浏览器安全策略仅适用于开发和测试环境。

服务端解决方案(推荐)

Node.js/Express 服务器

javascript
const express = require('express');
const cors = require('cors');
const app = express();

// 启用 CORS
app.use(cors());

// 或者自定义 CORS 配置
app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Methods", "GET,PUT,PATCH,POST,DELETE");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
});

PHP 服务器

php
<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST');
header("Access-Control-Allow-Headers: X-Requested-With");

// 处理 OPTIONS 预检请求
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
    exit(0);
}

Spring Boot (Java)

java
@CrossOrigin
@PostMapping(path="/upload")
public @ResponseBody ResponseEntity<Void> upload(@RequestBody Object object) {
    // 你的业务逻辑
}

Nginx 配置

nginx
location / {
    # 处理 OPTIONS 预检请求
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'Origin, Content-Type, Accept, Authorization';
        add_header 'Access-Control-Max-Age' 1728000;
        return 204;
    }

    # 添加 CORS 头
    add_header 'Access-Control-Allow-Origin' '*';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
    add_header 'Access-Control-Allow-Headers' 'Origin, Content-Type, Accept, Authorization';
}

开发环境解决方案

Flutter 3.3.0+ 版本

从 Flutter 3.3.0 开始,可以直接通过命令行参数禁用 Web 安全策略:

bash
# 运行应用时禁用安全策略
flutter run -d chrome --web-browser-flag "--disable-web-security"

# 测试时禁用安全策略
flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart -d web-server --web-browser-flag="--disable-web-security"

使用 flutter_cors 包

安装并使用第三方工具简化流程:

bash
# 安装 flutter_cors
dart pub global activate flutter_cors

# 禁用 CORS
fluttercors --disable

# 启用 CORS
fluttercors --enable

如果遇到 command not found 错误,需要将 pub 缓存目录添加到 PATH 环境变量:

bash
# 对于 zsh 用户
echo 'export PATH="$PATH":"$HOME/.pub-cache/bin"' >> ~/.zshrc
source ~/.zshrc

# 对于 bash 用户
echo 'export PATH="$PATH":"$HOME/.pub-cache/bin"' >> ~/.bashrc
source ~/.bashrc

Android Studio 配置

在 Android Studio 中配置运行参数:

  1. 打开 Edit Configurations
  2. Additional run args 中添加:--web-browser-flag "--disable-web-security"

请求优化方案

通过简化请求避免触发预检请求:

dart
// 修改请求头,使用简单请求避免预检
var response = await http.post(
  Uri.parse('https://api.example.com/endpoint'),
  headers: {
    'Content-Type': 'text/plain', // 使用简单内容类型
    'Authorization': 'Bearer your_token',
  },
  body: jsonEncode(yourData),
);

简单请求的条件:

  • 使用 GET、HEAD 或 POST 方法
  • 仅包含安全的头部字段
  • Content-Type 为以下之一:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

常见错误排查

  1. 确保 URL 协议完整

    dart
    // 错误:缺少 http:// 协议
    baseUrl: "localhost:3000/"
    
    // 正确:包含完整协议
    baseUrl: "http://localhost:3000/"
  2. 检查服务器是否正确处理 OPTIONS 请求 确保服务器对 OPTIONS 预检请求返回正确的 CORS 头信息

  3. 验证 CORS 包是否正确配置 如果使用 Express.js,确保 app.use(cors()) 在路由定义之前调用

生产环境建议

生产环境警告

切勿在生产环境中使用禁用浏览器安全策略的方案。这会使你的应用和用户面临安全风险。

生产环境中应该:

  1. 明确指定允许的源,而不是使用通配符 *
  2. 配置合适的 Access-Control-Allow-Methods
  3. 根据需要设置 Access-Control-Allow-Headers
  4. 考虑使用凭证(如果需要携带 cookies 等凭证信息)
javascript
// 生产环境示例:限制特定源
app.use(cors({
  origin: 'https://yourdomain.com',
  methods: ['GET', 'POST'],
  allowedHeaders: ['Content-Type', 'Authorization']
}));

总结

CORS 是浏览器实施的安全特性,无法通过纯 Dart 代码完全解决。最佳实践是:

  1. 开发环境:使用 --disable-web-security 标志或 flutter_cors 工具
  2. 生产环境:在服务器端正确配置 CORS 头信息
  3. 优化请求:设计 API 请求以避免触发预检请求

通过合理配置服务器和优化请求策略,可以彻底解决 Flutter Web 中的 CORS 问题。