4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GASのCORSエラーを解消する

Last updated at Posted at 2024-11-10

こんにちは、駆け出しエンジニアのtaiyoです。

1. GASのエンドポイントを叩くとCORSエラーが発生した

GAS(Google App Script)を使ってdoPostを含むAPIを作り、Next.jsでブラウザからGASのエンドポイントにfetchを使ってPOSTリクエストを送ったところ、次のような「CORSエラー」が発生しました。今回はこのエラーの意味と解決策がわかったので、共有したいと思います。

Access to fetch at 'https://script.google.com/macros/s/(デプロイID)/exec' from origin 'http://localhost:3000' 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. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

2. CORSとはなにか?その仕組みを知ろう

エラーの解説に入る前に、そもそもCORSの仕組みがわかっていないと解決法を見てもよくわからないと思うので、まずはCORSの基本から抑えていきたいと思います。すでに知っている方は飛ばしていただいても構いません。

1. CORSの基本と目的

CORS(Cross-Origin Resource Sharing)とは、ブラウザが異なるオリジン(ドメイン、ポート、またはプロトコルが異なるリソース)に対してリクエストを送る際のセキュリティチェックを行い、リクエストが許可されているか確認する仕組みのことです。

オリジン(Origin)とは、URLのプロトコル、ドメイン名、ポート番号を組み合わせたもので、例えば以下のように区別されます。

  • https://example.comhttps://api.example.com は別オリジン
  • http://example.com:3000http://example.com:4000 も別オリジン

2. 「安全なリクエスト」と「安全でないリクエスト」

実は、ブラウザからのリクエストには「安全なリクエスト(safe request)」と「安全でない(non-safe request)」の2つがあります。

安全なリクエスト

  • メソッドGETPOST、または HEAD
  • ヘッダーが、以下の「単純ヘッダー」のみを含む:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Typeapplication/x-www-form-urlencodedmultipart/form-data、または text/plain のいずれか

安全でないリクエスト

  • 上記以外のリクエスト(例:PUTDELETE メソッドや application/jsonContent-Type ヘッダーを含む場合など)

実は、CORS処理が発生するのは、この「安全でない(non-safe request)」のときだけです。ブラウザに「安全でない」と判断された場合、本来のリクエストの前に、次のような「プリフライトリクエスト」というものが走ります。

3. プリフライトリクエスト

プリフライトリクエストとは、実際のリクエストの前にブラウザがサーバーに確認するためOPTIONSリクエストです。

OPTIONSリクエストとは、HTTPのメソッドの一つで、ブラウザがサーバーに対してそのリソースにアクセスできるかどうかを確認するためのリクエストです。

プリフライトリクエストの流れは以下の通りです:

  1. ブラウザがOPTIONSリクエストを送信し、「このオリジンからのリクエストを受け入れるかどうか」を確認する。

  2. サーバーが次のようなヘッダーを返し、指定のオリジン(またはワイルドカード*ですべてのオリジン)が許可されている場合、リクエストが通ります。逆にサーバーが許可していない場合、ブラウザはエラーを表示し、リクエストを中断します。

    HTTP/1.1 200 OK
    Content-Type: application/json
    **Access-Control-Allow-Origin: http://localhost:3000**
    Access-Control-Allow-Methods: GET, POST, PUT
    Access-Control-Allow-Headers: Content-Type
    Content-Length: 123
    
    {
      "message": "リクエストは成功しました",
      "data": { "key": "value" }
    }
    

3. エラーの解説

さてさて、CORSやプリフライトリクエストの仕組みがわかったところで、次のエラーの解説に入りたいと思います。

Access to fetch at 'https://script.google.com/macros/s/(デプロイID)/exec' from origin 'http://localhost:3000' 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. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

  1. “Access to fetch at...”

    このメッセージの最初の部分では、localhost:3000からhttps://script.google.com/macros/s/.../exec(GASのエンドポイント)へのリクエストがブロックされたことが示されています。

    つまり、localhost:3000のJavaScriptコードから異なるオリジン(GASのURL)へのリクエストが「CORSポリシーによりブロック」されました。

  2. 原因:プリフライトリクエストの確認が通過しなかった

    Response to preflight request doesn't pass access control check

    プリフライトリクエストをサーバーが受け取りましたが、「アクセスコントロールチェックに失敗した」という意味です。

    ブラウザが送信したOPTIONSリクエストに対して、サーバー側(この場合GASのエンドポイント)がCORSを許可する応答を返せていないことが問題です。

  3. サーバーからの応答にAccess-Control-Allow-Originヘッダーが含まれていない

    No 'Access-Control-Allow-Origin' header is present on the requested resource

    サーバーのレスポンスに、クロスオリジンリクエストを許可するためのAccess-Control-Allow-Originヘッダーが含まれていないため、ブラウザがリクエストを拒否しています。

    このヘッダーがないと、ブラウザは「このリクエストは安全ではない」と判断し、JavaScriptでデータを取得できなくなります。

  4. 最後の部分の解釈

    If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

    ブラウザは、mode: 'no-cors'を設定してfetchリクエストを送信するよう提案していますが、この方法には制限があります。

    no-corsモードでは、CORSのエラーは回避できますが、レスポンスの内容が「opaque」(不透明)となり、データの中身をJavaScriptから確認することはできません。

4. エラーの原因と解決策

では、どうしてこのようなエラーが発生してしまうのでしょうか?

どうやら調べたところ、Google Apps Script(GAS)では、プリフライトリクエストに対する応答をうまく処理できないことがあるそうです。

つまり、プリフライトリクエストを受け取っても、「このリクエストを許可する」という応答を返さないため、ブラウザが「リクエストが安全でない」と判断し、実際のPOSTリクエストがブロックされます。

CORSエラーを解決するための方法の一つは、プリフライトリクエストが発生しないようにリクエストの形式を変更することです。

例えば私の場合、POSTリクエストのContent-Typeapplication/jsonからtext/plainに設定することで、これは「安全なリクエスト」となり、ブラウザがプリフライトリクエストを送信しなくなります。これにより、Google Apps Scriptに直接リクエストを送ることができ、エラーを回避できます。

const response = await fetch('https://script.google.com/macros/s/(デプロイID)/exec', {
  method: 'POST',
  headers: {
    'Content-Type': 'text/plain', // 'application/json'から変更
  },
  body: JSON.stringify(data),
});

また、もう一つの解決策はfetchをサーバーサイドから実行することです。なぜなら、CORS制限はブラウザレベルで適用されるセキュリティ機構のため、ブラウザを介さず直接サーバーからGASのエンドポイントにリクエストを送ることができれば、CORSエラーが発生しません。

5. まとめ

いかがだったでしょうか?GASはかゆいところに手が届くツールではありますが、内部構造がわからないため、仲良くなるにはもう少し時間が必要みたいです。これからもっとGASと仲良くなれるよう頑張ります。

この記事がエラーの解決の一助になれば幸いです。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?