Flutter WebでのCORSエラーの解決方法
問題の概要
Flutter Webアプリケーションを開発中、APIリクエストを行う際にCORS(Cross-Origin Resource Sharing)エラーが発生することがあります。この問題はモバイルアプリでは発生せず、Web版でのみ現れる特徴があります。
典型的なエラーメッセージ:
Access to XMLHttpRequest at 'https://api.example.com/endpoint' 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.
CORSとは
CORSはブラウザのセキュリティメカニズムで、異なるオリジン(ドメイン、プロトコル、ポート)間でのリソース共有を制御します。サーバーが適切なCORSヘッダーを返さない場合、ブラウザはセキュリティ上の理由でレスポンスをブロックします。
開発環境での一時的な解決策
重要
これらの方法は開発環境でのデバッグ用途のみに使用し、本番環境では絶対に使用しないでください。
方法1: ブラウザのセキュリティを無効化して実行
Flutter 3.3.0以降では、コマンドラインオプションで直接ブラウザのセキュリティ設定を変更できます:
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"
方法2: Android Studioでの設定
- 実行設定を編集します
- 「Additional run args」に以下を追加します:
--web-browser-flag="--disable-web-security"
方法3: flutter_corsパッケージの使用
# パッケージのインストール
dart pub global activate flutter_cors
# CORS制限の無効化
fluttercors --disable
# 再度有効化する場合
fluttercors --enable
パスが通っていない場合、.zshrc
または.bashrc
に以下を追加します:
export PATH="$PATH":"$HOME/.pub-cache/bin"
本番環境での正しい解決方法
本番環境では、サーバー側で適切なCORS設定を行うことが唯一の正しい解決策です。
サーバー側でのCORS設定例
const express = require('express');
const cors = require('cors');
const app = express();
// すべてのオリジンを許可
app.use(cors());
// または特定のオリジンのみ許可
app.use(cors({
origin: 'https://yourdomain.com'
}));
<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Origin, Content-Type, Accept, Authorization');
header('Access-Control-Max-Age: 1728000');
// OPTIONSリクエストの処理
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
exit(0);
}
// 通常の処理
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app) # すべてのオリジンを許可
# または特定のオリジンのみ許可
# CORS(app, origins=['https://yourdomain.com'])
server {
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';
}
}
Dartコードでのリクエスト設定
サーバー側でCORSが適切に設定されていれば、Dartコードは通常通りで問題ありません:
import 'package:http/http.dart' as http;
import 'dart:convert';
Future<void> makeRequest() async {
final response = await http.post(
Uri.parse('https://api.example.com/endpoint'),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer your_token',
},
body: json.encode({
'key': 'value',
}),
);
if (response.statusCode == 200) {
print('Success: ${response.body}');
} else {
print('Error: ${response.statusCode}');
}
}
その他の注意点
プリフライトリクエスト: 複雑なリクエスト(特定のContent-Typeやカスタムヘッダーを含む)の場合、ブラウザはまずOPTIONSリクエスト(プリフライト)を送信します。サーバーはこのリクエストにも適切に応答する必要があります。
Credentials付きリクエスト: クッキーや認証情報を含むリクエストを送信する場合、より厳格なCORS設定が必要です:
dart// Dart側 headers: { 'Authorization': 'Bearer your_token', 'Content-Type': 'application/json', }, credentials: true, // 必要に応じて
javascript// サーバー側 app.use(cors({ origin: 'https://yourdomain.com', credentials: true }));
Webレンダラーの変更: 場合によってはwebレンダラーを変更することで問題が解決することがあります:
bashflutter run -d chrome --web-renderer html
まとめ
CORSエラーを解決するには:
- 開発中は一時的な解決策(ブラウザのセキュリティ無効化)を使用
- 本番環境では必ずサーバー側で適切なCORS設定を実装
- クライアント側のみでCORSを完全に解決することは不可能
サーバー側の設定ができない場合(外部APIなど)、APIプロバイダーが提供する正式なCORSサポート方法を確認するか、バックエンドを経由したプロキシ処理を検討してください。