Próbuję wysłać żądanie cross-origin za pomocą jquery, ale jest ono odrzucane wraz z komunikatem
XMLHttpRequest nie może załadować http: // ... Żądany zasób nie zawiera nagłówka „Access-Control-Allow-Origin”. Dlatego Origin ... nie ma dostępu.
Używam flask, heroku i jquery
kod klienta wygląda następująco:
$(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);
}
});
});
});
po stronie heroku używam kolby i tak jest
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()
curl
poleceniami. Teraz napisałem krótką stronę html i próbuję wysłać żądanie metodą JS fetch () do mojego API, które w oparciu o Heroku i mam błąd CORS. Po zastosowaniu twojego kodu mój terminal zaczął odpowiadać kodem HTML (usługa HTTP / 1.1 503 niedostępna) zamiast JSON. Co może być tutaj błędem? Dziękuję Ci!!OK, nie sądzę, aby oficjalny fragment wspomniany przez galuszkak był używany wszędzie, powinniśmy obawiać się, że podczas obsługi może zostać uruchomiony jakiś błąd, taki jak
hello_world
function. Niezależnie od tego, czy odpowiedź jest poprawna, czy niepoprawna,Access-Control-Allow-Origin
powinniśmy zwrócić uwagę na nagłówek. Tak więc sprawa jest bardzo prosta, tak jak poniżej:@blueprint.after_request # blueprint can also be app~~ def after_request(response): header = response.headers header['Access-Control-Allow-Origin'] = '*' return response
To wszystko ~~
źródło
Właśnie stanąłem przed tym samym problemem i doszedłem do wniosku, że inne odpowiedzi są nieco bardziej skomplikowane niż powinny, więc oto moje podejście dla tych, którzy nie chcą polegać na większej liczbie bibliotek lub dekoratorów:
Żądanie CORS w rzeczywistości składa się z dwóch żądań HTTP. Żądanie inspekcji wstępnej, a następnie rzeczywiste żądanie, które jest wysyłane tylko wtedy, gdy inspekcja wstępna zakończy się pomyślnie.
Żądanie inspekcji wstępnej
Przed faktycznym
POST
żądaniem międzydomenowym przeglądarka wyśleOPTIONS
żądanie. Ta odpowiedź nie powinna zwracać żadnej treści, a jedynie pewne uspokajające nagłówki informujące przeglądarkę, że wykonanie tego żądania między domenami jest w porządku i nie jest to część ataku typu cross-site scripting.Napisałem funkcję w Pythonie, aby zbudować tę odpowiedź przy użyciu
make_response
funkcji zflask
modułu.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
Ta odpowiedź jest odpowiedzią wieloznaczną, która działa dla wszystkich żądań. Jeśli chcesz uzyskać dodatkowe zabezpieczenia zapewniane przez CORS, musisz podać białą listę źródeł, nagłówków i metod.
Ta odpowiedź przekona Twoją przeglądarkę (Chrome) do kontynuowania i wykonania właściwego żądania.
Rzeczywiste żądanie
Podczas obsługi rzeczywistego żądania należy dodać jeden nagłówek CORS - w przeciwnym razie przeglądarka nie zwróci odpowiedzi na wywołujący kod JavaScript. Zamiast tego żądanie zakończy się niepowodzeniem po stronie klienta. Przykład z jsonify
response = jsonify({"order_id": 123, "status": "shipped"} response.headers.add("Access-Control-Allow-Origin", "*") return response
Napisałem również funkcję do tego.
def _corsify_actual_response(response): response.headers.add("Access-Control-Allow-Origin", "*") return response
umożliwiając zwrot jednej linijki.
Kod końcowy
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
źródło
Jeśli chcesz włączyć CORS dla wszystkich tras, następnie wystarczy zainstalować flask_cors rozszerzenie (
pip3 install -U flask_cors
) i owinąćapp
tak:CORS(app)
.To wystarczy, aby to zrobić (przetestowałem to z
POST
prośbą o przesłanie obrazu i zadziałało):from flask import Flask from flask_cors import CORS app = Flask(__name__) CORS(app) # This will enable CORS for all routes
Ważna uwaga: jeśli w Twojej trasie jest błąd, powiedzmy, że próbujesz wydrukować zmienną, która nie istnieje, otrzymasz komunikat związany z błędem CORS, który w rzeczywistości nie ma nic wspólnego z CORS.
źródło
Wypróbuj następujące dekoratory:
@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()
Ten dekorator zostałby utworzony w następujący sposób:
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
Możesz również sprawdzić ten pakiet Flask-CORS
źródło
Moje rozwiązanie to opakowanie wokół 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(): ...
źródło
Ulepszenie rozwiązania opisanego tutaj: https://stackoverflow.com/a/52875875/10299604
With
after_request
we can obsłużyć nagłówki odpowiedzi CORS, unikając dodawania dodatkowego kodu do naszych punktów końcowych:### 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
źródło
Wszystkie powyższe odpowiedzi działają poprawnie, ale prawdopodobnie nadal będziesz otrzymywać błąd CORS, jeśli aplikacja zgłosi błąd, którego nie obsługujesz, na przykład błąd klucza, jeśli na przykład nie wykonujesz poprawnie sprawdzania poprawności danych wejściowych. Można dodać procedurę obsługi błędów, aby przechwytywać wszystkie wystąpienia wyjątków i dodawać nagłówki odpowiedzi CORS w odpowiedzi serwera
Więc zdefiniuj procedurę obsługi błędów - 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
następnie używając odpowiedzi Billala :
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
źródło
Jeśli nie możesz znaleźć swojego problemu, a kod powinien działać, może to oznaczać, że Twoje żądanie zbliża się do maksymalnego czasu, w którym heroku pozwala na zgłoszenie żądania. Heroku anuluje żądania, jeśli trwa to dłużej niż 30 sekund.
Źródła: https://devcenter.heroku.com/articles/request-timeout
źródło
Rozwiązałem ten sam problem w Pythonie używając flask i tej biblioteki. flask_cors
Źródła: https://flask-cors.readthedocs.io/en/latest/
źródło