環境についてざっくり
クライアント側
- ブラウザ: Google Chrome バージョン 87.0.4280.141 (Official Build) (64ビット)
- OS: Windows 10 Home 19041.746
- HTTPクライアント: axios v0.21.1
サーバ側
- OS: Raspbian GNU/Linux 10 (buster)
- webフレームワーク: actix-web v3.3.2
現象
Access to XMLHttpRequest ... has been blocked by CORS policy
というエラーが出力される
Raspberry Pi 4 Model B上でバックエンド(actix-web)とフロントエンド(Vue CLI)を稼働させている。WindowsPCからブラウザを使ってフロントエンドにアクセスし、axios
を使ってバックエンド(actix-web)にGETリクエストを実行した。すると、ブラウザのコンソールに以下のようなエラーが出力された。(ローカルIPが記述されていたところは一部編集している。)
Access to XMLHttpRequest at 'http://<localIP>:50001/' from origin 'http://<localIP>:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
問題発生時のソースコード
この時のサーバ側(actix-web)のコードは以下の通り。(ローカルIPとメールアドレスが記述されていたところは一部編集している。)
main.rs
Cargo.toml
この時クライアント側はJavaScript
から以下のようにaxios
を実行していた。(ローカルIPが記述されていたところは一部編集している。)
調査及び解決方法
actix_cors
を利用し、GETリクエストについてはエラーが解消した
エラーログを見ると、CORSポリシーによって、リソースへのアクセスがブロックされているようだ。CORSポリシーに対応するため、サーバ側のプログラムを修正していく。web上で"actix CORS"あたりで調べてみる。すると、actix_corsというcrateがあった。actix_cors
を使うために、main.rs
とCargo.toml
について、以下のように修正した。(ローカルIPが記述されていたところは一部編集している。)
main.rs
+ use actix_cors::Cors;
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};
...
HttpServer::new(|| {
+ let cors = Cors::default()
+ .allowed_origin("http://<localIP>:8080")
+ .allowed_methods(vec!["GET", "POST"]);
+ App::new().wrap(cors).service(index).service(post_operations)
- App::new().service(index).service(post_operations)
})
Cargo.toml
[dependencies]
actix-web = "3"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
+ actix-cors = "0.5.4"
この状態で再度確認してみた。GETリクエストは正常に処理されるようになった。しかし、POSTリクエストはまだCORSエラーが出る状態だった。なお、ブラウザ経由ではなく、Postmanを利用してリクエストを投げた場合、特にエラーなくレスポンスが返ってきた。
デバッグログを確認したところ、ブラウザからPOSTする前にOPTIONSリクエストが実行されていた
ブラウザを経由したPOSTリクエストのみエラーが出続ける問題について、web上で調べても自分の検索力では解決のための情報を見つけられなかった。そこで、サーバ側のデバッグログを出力し、その出力から原因を調べることにした。
デバッグログ出力のためにactix_web::middleware::Loggerを使用した。main.rs
とCargo.toml
について、以下のように修正した。(ローカルIPが記述されていたところは一部編集している。)
main.rs
+ extern crate env_logger;
use actix_cors::Cors;
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};
use serde::{Deserialize, Serialize};
+ use actix_web::middleware::Logger;
...
async fn main() -> std::io::Result<()> {
+ std::env::set_var("RUST_LOG", "actix_web=debug");
+ env_logger::init();
HttpServer::new(|| {
let cors = Cors::default()
.allowed_origin("http://<localIP>:8080")
.allowed_methods(vec!["GET", "POST"]);
+ App::new().wrap(cors).wrap(Logger::default()).service(index).service(post_operations)
- App::new().wrap(cors).service(index).service(post_operations)
})
Cargo.toml
[dependencies]
actix-web = "3"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
actix-cors = "0.5.4"
+ env_logger = "0.8.2
上記のようにサーバ側のソースを変更し、ログを確認した。するとPOSTリクエスト実行時はOPTIONSリクエストが最初に実行されていることが分かった。(ログ内容について一部を省略・編集している。)また、OPTIONSリクエスト時にHeaderNotAllowed
というエラーが発生している。
ログの内容
[2021-01-16T05:56:21Z DEBUG actix_web::middleware::logger] Error in response: HeadersNotAllowed
[2021-01-16T05:56:21Z INFO actix_web::middleware::logger] ... "OPTIONS /operations HTTP/1.1" 400 44 "http://<localIP>:8080/" ...
content-type
ヘッダを許可するように修正することで、エラーが解消された
ChromeのデベロッパーツールのNetworkタブから、OPTIONSリクエスト実行時のヘッダーを見てみる。Access-Control-Request-Headers
ヘッダーに以下のような設定がされていた。
Access-Control-Request-Headers: content-type
恐らくこれがエラーの原因と思われる。content-type
ヘッダーを許可するように、main.rs
について、以下のように修正した。(ローカルIPが記述されていたところは一部編集している。)
main.rs
...
use actix_cors::Cors;
+ use actix_web::{get, post, http, web, App, HttpResponse, HttpServer, Responder};
- use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};
use serde::{Deserialize, Serialize};
...
HttpServer::new(|| {
let cors = Cors::default()
.allowed_origin("http://<localIP>:8080")
+ .allowed_methods(vec!["GET", "POST"])
- .allowed_methods(vec!["GET", "POST"]);
+ .allowed_header(http::header::CONTENT_TYPE);
App::new().wrap(cors).wrap(Logger::default()).service(index).service(post_operations)
})
...
ここまでの変更で、ようやくChromeからのPOSTリクエストが通るようになった。最初GETだけ通ってPOSTが通らなかったのは、ヘッダー許可の設定が足りていなかったということだった。
結論
actix-web
に対してブラウザからHTTPリクエストを実行した際、Access to XMLHttpRequest ... has been blocked by CORS policy
というエラーが発生した場合は、actix_cors等を使ってCORSポリシーに対応させる必要がある。
[備考]OPTIONSリクエストについて
以下が参考になった。
アフィリエイト