続・React→PHP(RESTful API)のCORS回避の話


はじめに

前回、CORS回避と、ReactからAPIへのPOSTリクエストが正常に処理されなかった話という記事を書いたのですが、色々と間違いがあったため改めて書き直そうと思います。
現在当該記事は非公開にしています。

~~~ 追記(2021/01/25) ~~~

注意

この記事はCORSの事を理解していないエンジニアが「適当にやったらとりあえず動いた」程度の事を書いてます。

間違ってるという前提でお読みください!!!

Q.じゃあなんで公開してるの?

A.とりあえず動いたから

~~~ 追記終わり ~~~

目的

ReactからaxiosでPOSTのAPIを叩きたい。

環境

React:ローカルの開発環境 http://client.example.com/
API:PHPで書かれており、開発サーバにおいてある https://api.example.com/***
異なるドメイン上で動作しています。

状況

ReactからaxiosでAPIにPOSTリクエストを投げているが、CORSエラーで弾かれる。

また、axiosからPOSTリクエストが正常に投げられていない、もしくはPHPが正常にPOSTリクエストを受け取れていないような挙動をする。

エラー1 : No Access-Control-Allow-Origin

Access to XMLHttpRequest at 'http://api.example.com/***' from origin 'http://client.example.com' 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.

対処 : APIサーバにAccess-Control-Allow-Originヘッダの追加

CORSの仕組み上、サーバ側が明示的にアクセス許可を行わないと行けない事になっているらしいです。

ということで、APIサーバに下記コードを追加します。

php
  header("Access-Control-Allow-Origin: *");
  header("Access-Control-Allow-Headers: X-Requested-With, Origin, X-Csrftoken, Content-Type, Accept");
  header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH, HEAD");

ただしこれでもエラー解消ならず。

エラー2 : Response to preflight request doesn't pass access control check

Access to XMLHttpRequest at 'http://api.example.com/****' from origin 'http://client.example.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.

適当にかいつまむと、「preflight requestで弾かれたよ!!」とのこと。

先程、API側のヘッダを変更したにも関わらず、ここでエラーが出る原因がわからず。。。

Access-Control-Allow-Originの設定もしたし、Access-Control-Allow-MethodsにもOPTIONS含め色々追加したのになぜ・・・

ちなみに私はここで何を思ったか
『POSTリクエストを送ってるのになぜ200 OKが返ってこないんだ!!』
『もしかしてReactが正常にPOSTリクエストを送れていないのか!?』
『それともPHP側がPOSTリクエストの判別を出来ていないのか!?』
という違うところで足止め食らってました。。。

エラー文はしっかり読みましょう(自戒)

原因 : preflight requestはOPTIONSメソッドで送信される

APIサーバが想定外のメソッド(情報取得なのにPOSTメソッドが送られてくるなど)を弾く設定をしていたため、preflight requestのOPTIONSメソッドも弾かれ400 Bad Requestが返ってきていました。

例、GETメソッド以外は、400_Bad_Request
  if(!is_get_request()){
    throw new Exception("ERROR_INVALID_METHOD");
  }

ということはOPTIONSメソッドも許可すれば良いはず。

対処

APIサーバのコードを編集

php
  header("Access-Control-Allow-Origin: *");
  header("Access-Control-Allow-Headers: X-Requested-With, Origin, X-Csrftoken, Content-Type, Accept");
  header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH, HEAD");
  if( $_SERVER['REQUEST_METHOD'] === "OPTIONS" ){
    exit;
  }

とりあえずpreflight requestはヘッダ情報だけ見られれば良いはずなので、ヘッダ変更直後にexitするように変更します。

結果は↓↓↓

正常に取得できました。
めでたしめでたし

あとがき

ヘッダーにAccess-Control-Allow-Originなど3行を追加するという対処法はいくらか見つかったのですが、エラー2の対処法は(探し方が悪いせいか)全く見つかりませんでした。

実はここって誰も躓かないポイントなのでしょうか・・・?

そして今回のように実は他の人は別の方法で回避してるんじゃなかろうかと思ったり。。。

家庭ごとに味噌汁の作り方が違うように、人それぞれプログラムの書き方って違いますよね。
私はプログラミングが専業というわけではないので、他の人のコードを見る機会が少なく我流に走りがちになってしまいます。
もっと色んな人のコードを見たほうが良さそうです。

蛇足

私は野菜なら何でも入れちゃえという考えで味噌汁にピーマンを入れたことがあるのですが、その時周りの人から総ツッコミを食らいました。

まさかあんなに責められるとは思わなかったなあ。

ピーマン美味しいのに。

ピーマン美味しいよねって思ってくれる方はいいねお願いします。