CORS対応のまとめ

当サイトの閲覧数で意外と多いのが、CORS関連のページです。

私もFlutter Webアプリを開発していて、最初にXMLHttpRequest errorに出会った時には意味が分からず解決にずいぶん苦労しました。

結論としてサーバアプリ側での対応が必要になりますが、その対応方法についてまとめておきたいと思います。

CORSの仕組みそのものは別のサイトを参照してください。

CORSとは

CORS(Cross-Origin Resource Sharing)は、ウェブサイトが異なるOrigin(プロトコル、ドメイン、ポート)からデータを取得する際のセキュリティに関する仕組みです。

ウェブサイトは、別のサイトからデータを取得したり、ファイルを読み込んだりすることがあります。しかし、セキュリティのために、許可されていないサイトからのデータ取得を防ぐ必要があります。そのため、ブラウザは異なるOrigin間のアクセスを基本的にブロックします。

CORSは、ウェブサイトが他の場所からデータを取得する際に、そのデータ提供元(APIサーバなど)が許可したサイトだけにアクセスを許可するルールを指定します。

Webアプリケーションの場合、リクエスト元の確認を行うのは、Webブラウザです。また許可をするのはサーバです。そのため、Flutterのアプリ内でCORSの処理をする必要はありません。

以下は、Webアプリから呼び出すAPIサーバを開発する際のサーバ側の対応について、プラットフォームごとに説明します。

対応1 Flaskの場合

VPS等でサーバをたて、Flaskで開発した場合には、以下のようにします。

ライブラリをインストールします。

$ pip install flask_cors

既存のFlaskコードに以下を追加します。

from flask_cors import CORS   

app = Flask(__name__)

ーー 以下を追加 ーー
CORS(
    app,
    supports_credentials=True,        // cookieを使う場合に設定。使わない場合は削除
      origins=["https://****.***/"]  // アクセスを許可するオリジンを指定。すべてのアクセスを許可する場合は削除
) 

flask-corsの公式ドキュメントは以下にあります。

対応2 AWS Lambda Chaliceの場合

ChaliceのCORS対応については、公式のドキュメントに詳細に記載されています。

CORS

class CORSConfig(allow_origin='*'allow_headers=Noneexpose_headers=Nonemax_age=Noneallow_credentials=None)

CORS configuration to attach to a route, or globally on app.api.cors.

from chalice import CORSConfig
cors_config = CORSConfig(
    allow_origin='https://foo.example.com',
    allow_headers=['X-Special-Header'],
    max_age=600,
    expose_headers=['X-Special-Header'],
    allow_credentials=True
)

@app.route('/custom_cors', methods=['GET'], cors=cors_config)
def supports_custom_cors():
    return {'cors': True}

New in version 0.8.1.allow_origin

The value of the Access-Control-Allow-Origin to send in the response. Keep in mind that even though the Access-Control-Allow-Origin header can be set to a string that is a space separated list of origins, this behavior does not work on all clients that implement CORS. You should only supply a single origin to the CORSConfig object. If you need to supply multiple origins you will need to define a custom handler for it that accepts OPTIONS requests and matches the Origin header against a whitelist of origins. If the match is successful then return just their Origin back to them in the Access-Control-Allow-Origin header.allow_headers

The list of additional allowed headers. This list is added to list of built in allowed headers: Content-TypeX-Amz-DateAuthorizationX-Api-KeyX-Amz-Security-Token.expose_headers

A list of values to return for the Access-Control-Expose-Headers:max_age

The value for the Access-Control-Max-Ageallow_credentials

A boolean value that sets the value of Access-Control-Allow-Credentials.

https://aws.github.io/chalice/api.html?#cors

上記引用コードにあるように、cors_configを設定し、@app.routeの引数に”cors=cors_config"を加えることで、対応できます。

対応3 Google Cloud Functionsの場合

Google Cloud Functionsの場合も公式のドキュメントにサンプルが掲載されています。

import functions_framework

@functions_framework.http
def cors_enabled_function(request):
    # For more information about CORS and CORS preflight requests, see:
    # https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request

    # Set CORS headers for the preflight request
    if request.method == "OPTIONS":
        # Allows GET requests from any origin with the Content-Type
        # header and caches preflight response for an 3600s
        headers = {
            "Access-Control-Allow-Origin": "*",
            "Access-Control-Allow-Methods": "GET",
            "Access-Control-Allow-Headers": "Content-Type",
            "Access-Control-Max-Age": "3600",
        }

        return ("", 204, headers)

    # Set CORS headers for the main request
    headers = {"Access-Control-Allow-Origin": "*"}

    return ("Hello World!", 200, headers)

headers内の"Access-Control-Allow-Origin": "*" 部分でOriginを指定することができます。