Google Formへaxiosで非同期通信しようとした時に出るCROSエラーの回避

GoogleFormをカスタマイズして自サイト(Vue.js + Firebase)で利用しようとしてハマったこと。
フォーム内容送信時にGoogleForm専用の送信完了画面に飛ぶのが嫌
→とりあえず非同期通信(axios)で送信してみる
→CROSのエラー↓が出る
「Failed to load https://docs.google.com/forms/XXXXXX/formResponse: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.」

まず、エラーが出てても非同期通信はうまくいきます。
参考:Googleフォームをカスタマイズして導入、Ajax処理を行う

でもエラーハンドリングできないの嫌だよね。回避しよう。


そもそもなぜエラーが出るのか

ブラウザ(特にChrome)では、セキュリティ対策としてオリジン (ドメイン、プロトコル、ポート番号) の違うクライアントとサーバで通信することに制限かけているらしい。クロスドメインってやつですね。
基本的にはCROSを許可する(=サーバサイドのHTTPヘッダーで特定のオリジンからのアクセスを許可する)ことでこのエラーは解決できるみたいなんだけど、どうやらGoogle FormではHTTPヘッダーの設定ができないらしい。(探しきれなかったけどもしかしたらGASでは方法があるかも。要検討)
CROS詳しくはここがよかった
オリジン間リソース共有 (CORS)


回避方法:CROSプロキシを経由して通信を行う

似たような事例の場合、自分でローカルにプロキシサーバ立てて経由するのが一般的みたいだけど、今回はサーバレス(Firebase)で開発してたので公開されてるCORSプロキシを使用してみた。セキュリティにそこまで気を使わなくてもいい個人のWebサイト程度ならこの方法でいいんじゃないかなーと思う。
今回使用したのは以下のプロキシ
cors-anywhere


実際のソースはこんな感じになった。submitするurlの前にプロキシurl連結しただけ。


その他回避方法

フリーのプロキシを経由することに抵抗がある場合、「submit時の遷移先を非表示のiframeに設定することでエラーを起こさせない」という方法もある。
非同期通信せずに通常通りフォームの内容をsubmitして、送信完了画面をiframeに表示するように設定し、更にそのiframeを非表示に設定することによってエラーを回避する。
formタグにtarget属性を設定することによって遷移先を新規ウィンドウorフレームに表示することができる
詳細はここで
結果が表示されるウィンドウを指定する


コードはこんな感じで

シンプル。エラーハンドリングはできないけど、とりあえずエラー回避だけするって場合はこれでもいい。



あとは未検証なんだけど、非同期通信で送信するデータの形式を「json」ではなく「jsonp」にすることでCROSエラーを回避できる場合があるらしい。
ただしaxiosではデータ形式にjsonpは設定できないので、やる場合はaxiosの公式ドキュメントで推奨されてる方法が良さそう。