Flutter webアプリのXMLHttpRequest error(CORS)
FlutterはiOS,Androidだけでなく、Web ,Windows,Macのアプリも共通の環境で開発できることが、特徴です。
今回、iOSの音声チャットボットアプリをベースに、webアプリに改変した場合に遭遇したXMLHttpRequest errorについて調査しました。
追記(2023/8/18)
CORSの対応方法のまとめを書きました。合わせてご参照ください。
現象
音声チャットボットは、応答生成をサーバサイドで行なっています。iOSアプリでは問題なく動作していた、_request()にて、XMLHttpRequest errorが発生しました。
Future<void> _request(String input_word) async {
String url = "<応答生成サーバURL>";
Map<String, String> headers = {"Content-Type": "application/json"};
String body = json.encode({
'inputs': input_word,
});
try {
http.Response resp = await http.post(Uri.parse(url), headers: headers, body: body);
if (resp.statusCode != 200) {
setState(() {
int statusCode = resp.statusCode;
botWords = '''Failed to post $statusCode''';
});
}
setState(() {
< 省略 >
});
} catch (e) {
setState(() {
botWords = e.toString();
});
}
}
わかったこと
同様のエラーに関しての質問が以下のサイトで見つかりました。
FlutterからAPIアクセスする際に、XMLHttpRequest errorのエラーが出る
いくつかの解決方法が示されていますが、根本的な原因がCORSにあるということなので、こちらを参考にしました。
How to solve flutter web api cors error only with dart code?
対策
以下の通り実行することで、エラーは回避されました。
1- Go to flutter\bin\cache and remove a file named: flutter_tools.stamp
2- Go to flutter\packages\flutter_tools\lib\src\web and open the file chrome.dart.
3- Find '--disable-extensions'
4- Add '--disable-web-security'
オリジン間リソース共有 (CORS)とは
以下のサイトにわかりやすく記載されています。
オリジン間リソース共有 (Cross-Origin Resource Sharing, CORS) は、追加の HTTP ヘッダーを使用して、あるオリジンで動作しているウェブアプリケーションに、異なるオリジンにある選択されたリソースへのアクセス権を与えるようブラウザーに指示するための仕組みです。ウェブアプリケーションは、自分とは異なるオリジン (ドメイン、プロトコル、ポート番号) にあるリソースをリクエストするとき、オリジン間 HTTP リクエストを実行します。
オリジン間リクエストとは、例えば
https://domain-a.com
で提供されているウェブアプリケーションのフロントエンド JavaScript コードがXMLHttpRequest
を使用してhttps://domain-b.com/data.json
へリクエストを行うような場合です。セキュリティ上の理由から、ブラウザーは、スクリプトによって開始されるオリジン間 HTTP リクエストを制限しています。例えば、
https://developer.mozilla.org/ja/docs/Web/HTTP/CORSXMLHttpRequest
や Fetch API は同一オリジンポリシー (same-origin policy) に従います。つまり、これらの API を使用するウェブアプリケーションは、そのアプリケーションが読み込まれたのと同じオリジンに対してのみリソースのリクエストを行うことができ、それ以外のオリジンからの場合は正しい CORS ヘッダーを含んでいることが必要です。
本質的には、サーバ側でCORSに対応し、リクエスト側からはCORSヘッダをつけてリクエストを送る必要があるようです。
(追記)サーバ対応
サーバ側でCORSに対応しました。
応答生成サーバはFlask (python)で動作させています。Flask アプリのCORS対応は以下の通りです。
$ pip install flask_cors
from flask_cors import CORS // を追加
app = Flask(__name__)
ーー 以下を追加 ーー
CORS(
app,
supports_credentials=True, // cookieを使う場合のみ。今回は不要
origins=["https://****.app/"] // アクセスを許可するオリジンを指定。今回は不要
)
以上を指定した結果、上記対策を元に戻してもローカルのwebアプリは問題なく動作したことを確認しました。
#次の記事に続く。。