วิธีเปิดใช้ 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 สำหรับสวัสดีข้ามกำเนิดโลก!
Simon Nicholls

มันเป็นทางออกเดียวที่เหมาะกับฉัน ขอบคุณ!
psc37

2
คุณเป็นผู้ช่วยชีวิต! ทำงานอย่างมีเสน่ห์
Rohit Swami

ไฮ! คุณช่วยฉันคิดว่าเกิดอะไรขึ้นในกรณีของฉันได้ไหม ฉันเขียน API ง่ายๆโดยใช้ Python / Flask โดยไม่ได้ดูด้วยซ้ำ ฉันไปถึงมันโดยcurlคำสั่ง ตอนนี้ฉันเขียนหน้า html สั้น ๆ และพยายามส่งคำขอด้วยวิธี JS fetch () ไปยัง API ของฉันซึ่งขึ้นอยู่กับ Heroku และฉันมีข้อผิดพลาด CORS หลังจากที่ฉันใช้รหัสของคุณเทอร์มินัลของฉันก็เริ่มตอบสนองฉันด้วยรหัส HTML (HTTP / 1.1 503 Service Unavailable) แทน JSON มีข้อผิดพลาดอะไรที่นี่? ขอขอบคุณ!!
Nikita Basharkin

5
ก่อนที่จะมีใครคัดลอกรหัสนี้ไปยังแอปพลิเคชันของตนโปรดตรวจสอบเอกสารเนื่องจากจำเป็นต้องใช้เพียงบางบรรทัดเท่านั้น
rovyko

46

ตกลงฉันไม่คิดว่าควรใช้ตัวอย่างอย่างเป็นทางการที่ 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 ขั้นพื้นฐาน ไม่จำเป็นต้องมีอะไรแฟนซีเพียงแค่ข้ามข้อผิดพลาด :)
Narshe

34

ฉันเพิ่งประสบปัญหาเดียวกันและฉันก็เชื่อว่าคำตอบอื่น ๆ นั้นซับซ้อนกว่าที่ควรจะเป็นเล็กน้อยดังนั้นนี่คือแนวทางของฉันสำหรับผู้ที่ไม่ต้องการพึ่งพาห้องสมุดหรือมัณฑนากรเพิ่มเติม:

คำขอ CORS ประกอบด้วยคำขอ HTTP สองรายการ คำขอไฟล่วงหน้าและคำขอจริงที่เกิดขึ้นก็ต่อเมื่อไฟส่องเฉพาะจุดผ่านสำเร็จ

คำขอไฟล่วงหน้า

ก่อนที่จะมีPOSTคำขอข้ามโดเมนจริงเบราว์เซอร์จะOPTIONSส่งคำขอ คำตอบนี้ไม่ควรส่งคืนเนื้อความใด ๆ แต่มีเพียงส่วนหัวที่ให้ความมั่นใจบางส่วนเท่านั้นที่บอกเบราว์เซอร์ว่าสามารถทำคำขอข้ามโดเมนนี้ได้และไม่ได้เป็นส่วนหนึ่งของการโจมตีสคริปต์ข้ามไซต์

ฉันเขียนฟังก์ชัน Python เพื่อสร้างการตอบสนองนี้โดยใช้make_responseฟังก์ชันจาก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 หนึ่งรายการมิฉะนั้นเบราว์เซอร์จะไม่ตอบกลับการเรียกใช้โค้ด 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
นี่เป็นคำตอบที่ดีที่สุดสำหรับปัญหา CORS ใน Flask ทำงานอย่างมีเสน่ห์! ขอบคุณ @Niels
Chandra Kanth

ขอบคุณสำหรับคำอธิบายโดยละเอียด !! สิ่งนี้มีประโยชน์มาก!
jones-chris

ใช้โซลูชันมากมายรวมถึง CORS และของคุณ แต่ทั้งหมดนี้ใช้ไม่ได้กับ aws (ทำตามตัวอย่างนี้ - aws.amazon.com/getting-started/projects/… ) มีใครรู้บ้างไหมว่าเกิดอะไรขึ้น
StereoMatching

วิธีนี้เรียบง่าย แต่ดูหรูหรา! ขอบคุณคุณช่วยฉันได้จริงๆ
เจอร์รี่

22

หากคุณต้องการเปิดใช้ ธ สำหรับทุกเส้นทางแล้วเพียงแค่ติดตั้งflask_corsส่วนขยาย ( pip3 install -U flask_cors) และห่อ เช่นนี้appCORS(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
ขอบคุณมาก! โซลูชันที่เรียบง่ายและใช้งานทั่วไปนี้ช่วยให้ฉันสามารถเรียก API ของฉันจากโค้ดเว็บ React ของฉันโดยไม่ต้องบล็อก CORS อีกต่อไป
Sebastian Diaz

2
ขอขอบคุณ ! ส่วนบันทึกสำคัญช่วยให้ฉันประหยัดเวลาได้มากทีเดียว
Gabriel

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

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 ในการตอบสนองของเซิร์ฟเวอร์

ดังนั้นกำหนดตัวจัดการข้อผิดพลาด - error.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

จากนั้นใช้คำตอบของ Billal :

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 โดยใช้ขวดและไลบรารีนี้ flask_cors

อ้างอิง: https://flask-cors.readthedocs.io/en/latest/


แม้ว่าลิงก์นี้อาจตอบคำถามได้ แต่ควรรวมส่วนสำคัญของคำตอบไว้ที่นี่และระบุลิงก์เพื่อการอ้างอิง คำตอบแบบลิงก์เท่านั้นอาจไม่ถูกต้องหากหน้าที่เชื่อมโยงเปลี่ยนไป - จากรีวิว
Jason Aller

0

หากคุณไม่พบปัญหาของคุณและรหัสของคุณควรใช้งานได้อาจเป็นไปได้ว่าคำขอของคุณกำลังจะถึงขีด จำกัด เวลาสูงสุดที่ heroku อนุญาตให้คุณร้องขอได้ Heroku ยกเลิกคำขอหากใช้เวลานานกว่า 30 วินาที

อ้างอิง: https://devcenter.heroku.com/articles/request-timeout

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.