フラスコでCORSを有効にする方法


92

jqueryを使用してクロスオリジンリクエストを作成しようとしていますが、メッセージで拒否され続けます

XMLHttpRequestはhttp://をロードできません...要求されたリソースに「Access-Control-Allow-Origin」ヘッダーがありません。したがって、Origin ...はアクセスを許可されていません。

フラスコ、heroku、jqueryを使用しています

クライアントコードは次のようになります。

$(document).ready(function() {
    $('#submit_contact').click(function(e){
        e.preventDefault();
        $.ajax({
            type: 'POST',
            url: 'http://...',
            // data: [
            //      { name: "name", value: $('name').val()},
            //      { name: "email", value: $('email').val() },
            //      { name: "phone", value: $('phone').val()},
            //      { name: "description", value: $('desc').val()}
            //
            // ],
            data:"name=3&email=3&phone=3&description=3",
            crossDomain:true,
            success: function(msg) {
                alert(msg);
            }
        });
    }); 
});

Heroku側ではフラスコを使用しています。

from flask import Flask,request
from flask.ext.mandrill import Mandrill
try:
    from flask.ext.cors import CORS  # The typical way to import flask-cors
except ImportError:
    # Path hack allows examples to be run without installation.
    import os
    parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    os.sys.path.insert(0, parentdir)

    from flask.ext.cors import CORS
app = Flask(__name__)

app.config['MANDRILL_API_KEY'] = '...'
app.config['MANDRILL_DEFAULT_FROM']= '...'
app.config['QOLD_SUPPORT_EMAIL']='...'
app.config['CORS_HEADERS'] = 'Content-Type'

mandrill = Mandrill(app)
cors = CORS(app)

@app.route('/email/',methods=['POST'])
def hello_world():
    name=request.form['name']
    email=request.form['email']
    phone=request.form['phone']
    description=request.form['description']

    mandrill.send_email(
        from_email=email,
        from_name=name,
        to=[{'email': app.config['QOLD_SUPPORT_EMAIL']}],
        text="Phone="+phone+"\n\n"+description
    )

    return '200 OK'

if __name__ == '__main__':
    app.run()

回答:


174

Herokuにデプロイしたときにうまくいったことは次のとおりです。

http://flask-cors.readthedocs.org/en/latest/
実行してflask-corsをインストールします- pip install -U flask-cors

from flask import Flask
from flask_cors import CORS, cross_origin
app = Flask(__name__)
cors = CORS(app)
app.config['CORS_HEADERS'] = 'Content-Type'

@app.route("/")
@cross_origin()
def helloWorld():
  return "Hello, cross-origin-world!"

37
こんにちはクロスオリジンワールドのプラス1!
サイモンニコルズ2018年

それは私のために働く唯一の解決策でした。ありがとう!
psc 3720年

2
あなたは命の救世主です!チャームのように働いた。
RohitSwami20年

こんにちは!私の場合に何が起こるかを理解するのを手伝ってくれませんか?Python / Flaskを使用して、ビューすらなく、単純なAPIを作成しました。curlコマンドで到達しました。ここで、短いhtmlページを作成し、JS fetch()メソッドを使用してAPIにリクエストを送信しようとしました。これは、Herokuに基づいており、CORSエラーが発生します。コードを適用した後、端末がJSONではなくHTMLコード(HTTP / 1.1 503 Service Unavailable)で応答し始めました。ここでエラーは何でしょうか?ありがとうございました!!
NikitaBasharkin20年

5
誰かがこのコードをアプリケーションにコピーする前に、これらの行の一部だけが必要なので、ドキュメントチェックしてください。
rovyko

46

OK、galuszkakが言及した公式スニペットをどこでも使用するべきではないと思いhello_worldます。関数などのハンドラー中に何らかのバグがトリガーされる可能性がある場合に注意する必要があります。応答が正しいか正しくないかにかかわらず、Access-Control-Allow-Originヘッダーは私たちが懸念すべきものです。したがって、以下のように、物事は非常に単純です。

@blueprint.after_request # blueprint can also be app~~
def after_request(response):
    header = response.headers
    header['Access-Control-Allow-Origin'] = '*'
    return response

以上です~~


これは、基本的なCRUD操作を伴う小さなプロジェクトにも役立ちました。
特別

34

私はちょうど同じ問題に直面し、他の答えは必要以上に複雑であると信じるようになりました。それで、より多くのライブラリやデコレータに依存したくない人のための私のアプローチは次のとおりです。

CORSリクエストは、実際には2つのHTTPリクエストで構成されています。プリフライトリクエストと、プリフライトが正常に通過した場合にのみ行われる実際のリクエスト。

プリフライトリクエスト

実際のクロスドメインPOSTリクエストの前に、ブラウザはOPTIONSリクエストを発行します。この応答は本文を返す必要はありませんが、このクロスドメインリクエストを実行しても問題がなく、クロスサイトスクリプティング攻撃の一部ではないことをブラウザに通知するいくつかの安心できるヘッダーのみを返します。

モジュールのmake_response関数を使用してこの応答を作成するPython関数を作成しましたflask

def _build_cors_prelight_response():
    response = make_response()
    response.headers.add("Access-Control-Allow-Origin", "*")
    response.headers.add("Access-Control-Allow-Headers", "*")
    response.headers.add("Access-Control-Allow-Methods", "*")
    return response

この応答は、すべての要求に対して機能するワイルドカード応答です。CORSによって得られる追加のセキュリティが必要な場合は、オリジン、ヘッダー、およびメソッドのホワイトリストを提供する必要があります。

この応答により、(Chrome)ブラウザーが先に進んで実際の要求を実行するように説得されます。

実際のリクエスト

実際のリクエストを処理するときは、CORSヘッダーを1つ追加する必要があります。そうしないと、ブラウザーは呼び出し元のJavaScriptコードへの応答を返しません。代わりに、リクエストはクライアント側で失敗します。jsonifyの例

response = jsonify({"order_id": 123, "status": "shipped"}
response.headers.add("Access-Control-Allow-Origin", "*")
return response

そのための関数も書きました。

def _corsify_actual_response(response):
    response.headers.add("Access-Control-Allow-Origin", "*")
    return response

ワンライナーを返却することができます。

最終コード

from flask import Flask, request, jsonify, make_response
from models import OrderModel

flask_app = Flask(__name__)

@flask_app.route("/api/orders", methods=["POST", "OPTIONS"])
def api_create_order():
    if request.method == "OPTIONS": # CORS preflight
        return _build_cors_prelight_response()
    elif request.method == "POST": # The actual request following the preflight
        order = OrderModel.create(...) # Whatever.
        return _corsify_actual_response(jsonify(order.to_dict()))
    else
        raise RuntimeError("Weird - don't know how to handle method {}".format(request.method))

def _build_cors_prelight_response():
    response = make_response()
    response.headers.add("Access-Control-Allow-Origin", "*")
    response.headers.add('Access-Control-Allow-Headers', "*")
    response.headers.add('Access-Control-Allow-Methods', "*")
    return response

def _corsify_actual_response(response):
    response.headers.add("Access-Control-Allow-Origin", "*")
    return response

@Niels B.に感謝します、あなたは私の時間を節約しました。以前にcors設定を追加しましたが、正しく設定されていません。
GünayGültekin

1
これは、Flaskに関するこのCORSの問題に対する最善の答えです。チャームのように働いた!おかげ@Niels
チャンドラKanth

細かくご説明いただきありがとうございます!! これはとても役に立ちました!
ジョーンズクリス・

CORSやあなたのソリューションを含む多くのソリューションを使用しますが、それらのすべてがawsでは機能しません(この例に従ってください--aws.amazon.com/getting-started/projects/…)、誰かが何が起こっているのか知っていますか?
ステレオマッチング

このソリューションは本当にシンプルでありながらエレガントです!ありがとう、あなたは本当に私の時間を節約しました。
ジェリー

22

すべてのルートでCORSを有効にする場合は、flask_cors拡張機能(pip3 install -U flask_cors)をインストールして、次のappようにラップ しますCORS(app)

それはそれを行うのに十分です(私POSTは画像をアップロードするリクエストでこれをテストしました、そしてそれは私のために働きました):

from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app) # This will enable CORS for all routes

重要な注意:ルートにエラーがある場合、存在しない変数を出力しようとすると、実際にはCORSとは関係のないCORSエラー関連のメッセージが表示されます。


1
どうもありがとう!このシンプルで一般的なソリューションにより、CORSブロックなしでReactWebコードからAPIを呼び出すことができました。
セバスチャンディアス

2
ありがとうございました !重要なメモの部分は私にかなりの時間を節約しました。
ガブリエル

4

次のデコレータを試してください。

@app.route('/email/',methods=['POST', 'OPTIONS']) #Added 'Options'
@crossdomain(origin='*')                          #Added
def hello_world():
    name=request.form['name']
    email=request.form['email']
    phone=request.form['phone']
    description=request.form['description']

    mandrill.send_email(
        from_email=email,
        from_name=name,
        to=[{'email': app.config['QOLD_SUPPORT_EMAIL']}],
        text="Phone="+phone+"\n\n"+description
    )

    return '200 OK'

if __name__ == '__main__':
    app.run()

このデコレータは次のように作成されます。

from datetime import timedelta
from flask import make_response, request, current_app
from functools import update_wrapper


def crossdomain(origin=None, methods=None, headers=None,
                max_age=21600, attach_to_all=True,
                automatic_options=True):

    if methods is not None:
        methods = ', '.join(sorted(x.upper() for x in methods))
    if headers is not None and not isinstance(headers, basestring):
        headers = ', '.join(x.upper() for x in headers)
    if not isinstance(origin, basestring):
        origin = ', '.join(origin)
    if isinstance(max_age, timedelta):
        max_age = max_age.total_seconds()

    def get_methods():
        if methods is not None:
            return methods

        options_resp = current_app.make_default_options_response()
        return options_resp.headers['allow']

    def decorator(f):
        def wrapped_function(*args, **kwargs):
            if automatic_options and request.method == 'OPTIONS':
                resp = current_app.make_default_options_response()
            else:
                resp = make_response(f(*args, **kwargs))
            if not attach_to_all and request.method != 'OPTIONS':
                return resp

            h = resp.headers

            h['Access-Control-Allow-Origin'] = origin
            h['Access-Control-Allow-Methods'] = get_methods()
            h['Access-Control-Max-Age'] = str(max_age)
            if headers is not None:
                h['Access-Control-Allow-Headers'] = headers
            return resp

        f.provide_automatic_options = False
        return update_wrapper(wrapped_function, f)
    return decorator

このパッケージFlask-CORSもチェックアウトできます


まだ動作していません。私はすでにそれを試し、Flask-CORSパッケージも使用しました。Flask-CORSはその上に構築されていると思います
Lopes 2014

3

ここで説明されているソリューションの改善:https//stackoverflow.com/a/52875875/10299604

これによりafter_request、エンドポイントにコードを追加することなく、CORS応答ヘッダーを処理できます。

    ### CORS section
    @app.after_request
    def after_request_func(response):
        origin = request.headers.get('Origin')
        if request.method == 'OPTIONS':
            response = make_response()
            response.headers.add('Access-Control-Allow-Credentials', 'true')
            response.headers.add('Access-Control-Allow-Headers', 'Content-Type')
            response.headers.add('Access-Control-Allow-Headers', 'x-csrf-token')
            response.headers.add('Access-Control-Allow-Methods',
                                'GET, POST, OPTIONS, PUT, PATCH, DELETE')
            if origin:
                response.headers.add('Access-Control-Allow-Origin', origin)
        else:
            response.headers.add('Access-Control-Allow-Credentials', 'true')
            if origin:
                response.headers.add('Access-Control-Allow-Origin', origin)

        return response
    ### end CORS section

2

私の解決策はapp.routeのラッパーです:

def corsapp_route(path, origin=('127.0.0.1',), **options):
    """
    Flask app alias with cors
    :return:
    """

    def inner(func):
        def wrapper(*args, **kwargs):
            if request.method == 'OPTIONS':
                response = make_response()
                response.headers.add("Access-Control-Allow-Origin", ', '.join(origin))
                response.headers.add('Access-Control-Allow-Headers', ', '.join(origin))
                response.headers.add('Access-Control-Allow-Methods', ', '.join(origin))
                return response
            else:
                result = func(*args, **kwargs)
            if 'Access-Control-Allow-Origin' not in result.headers:
                result.headers.add("Access-Control-Allow-Origin", ', '.join(origin))
            return result

        wrapper.__name__ = func.__name__

        if 'methods' in options:
            if 'OPTIONS' in options['methods']:
                return app.route(path, **options)(wrapper)
            else:
                options['methods'].append('OPTIONS')
                return app.route(path, **options)(wrapper)

        return wrapper

    return inner

@corsapp_route('/', methods=['POST'], origin=['*'])
def hello_world():
    ...

2

上記のすべての応答は正常に機能しますが、たとえば、入力検証を適切に行っていない場合など、アプリケーションが処理していないエラーをスローした場合、キーエラーなどのCORSエラーが発生する可能性があります。エラーハンドラーを追加して、例外のすべてのインスタンスをキャッチし、サーバー応答にCORS応答ヘッダーを追加できます。

したがって、エラーハンドラーを定義します-errors.py:

from flask import json, make_response, jsonify
from werkzeug.exceptions import HTTPException

# define an error handling function
def init_handler(app):

    # catch every type of exception
    @app.errorhandler(Exception)
    def handle_exception(e):

        #loggit()!          

        # return json response of error
        if isinstance(e, HTTPException):
            response = e.get_response()
            # replace the body with JSON
            response.data = json.dumps({
                "code": e.code,
                "name": e.name,
                "description": e.description,
            })
        else:
            # build response
            response = make_response(jsonify({"message": 'Something went wrong'}), 500)

        # add the CORS header
        response.headers['Access-Control-Allow-Origin'] = '*'
        response.content_type = "application/json"
        return response

次に、ビラルの答えを使用します。

from flask import Flask
from flask_cors import CORS

# import error handling file from where you have defined it
from . import errors

app = Flask(__name__)
CORS(app) # This will enable CORS for all routes
errors.init_handler(app) # initialise error handling 

2

私はフラスコとこのライブラリを使用してPythonでこれと同じ問題を解決しました。 フラスコ_cors

参照:https//flask-cors.readthedocs.io/en/latest/


このリンクは質問に答えることができますが、ここに答えの本質的な部分を含めて、参照用のリンクを提供することをお勧めします。リンクされたページが変更されると、リンクのみの回答が無効になる可能性があります。-レビューから
JasonAller20年

0

問題が見つからず、コードが機能するはずの場合は、リクエストが、herokuでリクエストを実行できる最大時間に達している可能性があります。Herokuは、30秒以上かかる場合、リクエストをキャンセルします。

参照:https//devcenter.heroku.com/articles/request-timeout

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.