Bottle Py: activer les CORS pour les requêtes AJAX jQuery
je travaille sur une API RESTful D'un service web sur le cadre Web de la bouteille et je veux accéder aux ressources avec les appels AJAX jQuery.
en utilisant un client REST, les interfaces de ressources fonctionnent comme prévu et gèrent correctement GET, POST,... demande. Mais lors de l'envoi d'une requête jQuery AJAX POST, les OPTIONS résultantes preflight request sont simplement refusées comme '405: Method not allowed'.
j'ai essayé d'activer CORS sur le serveur de bouteilles - comme décrit ici: http://bottlepy.org/docs/dev/recipes.html#using-the-hooks-plugin Mais le hook after_request n'est jamais appelé pour la requête D'OPTIONS.
Voici un extrait de mon serveur:
from bottle import Bottle, run, request, response
import simplejson as json
app = Bottle()
@app.hook('after_request')
def enable_cors():
print "after_request hook"
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token'
@app.post('/cors')
def lvambience():
response.headers['Content-Type'] = 'application/json'
return "[1]"
[...]
L'AJAX de jQuery appel:
$.ajax({
type: "POST",
url: "http://192.168.169.9:8080/cors",
data: JSON.stringify( data ),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(data){
alert(data);
},
failure: function(err) {
alert(err);
}
});
le serveur enregistre seulement une erreur 405:
192.168.169.3 - - [23/Jun/2013 17:10:53] "OPTIONS /cors HTTP/1.1" 405 741
$.post fonctionne, mais ne pas être en mesure d'envoyer des demandes PUT serait défaire le but d'un service reposant. Alors, comment puis-je permettre que les OPTIONS de demande de vol avant le vol soient gérées?
4 réponses
installer un handler au lieu d'un crochet.
il y a deux façons complémentaires que j'ai fait ceci dans le passé: décorateur, ou plugin de bouteille. Je vais vous montrer les deux et vous pouvez décider si l'un (ou les deux) d'entre eux conviennent à vos besoins. Dans les deux cas, l'idée générale est la suivante: un gestionnaire intercepte la réponse avant qu'elle ne soit renvoyée au client, insère les en-têtes CORS, puis procède au retour de la réponse.
Méthode 1: installation par parcours (décorateur))
cette méthode est préférable lorsque vous voulez seulement exécuter le handler sur certaines de vos routes. Juste décorer chaque itinéraire que vous souhaitez exécuter. Voici un exemple:
import bottle
from bottle import response
# the decorator
def enable_cors(fn):
def _enable_cors(*args, **kwargs):
# set CORS headers
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token'
if bottle.request.method != 'OPTIONS':
# actual request; reply with the actual response
return fn(*args, **kwargs)
return _enable_cors
app = bottle.app()
@app.route('/cors', method=['OPTIONS', 'GET'])
@enable_cors
def lvambience():
response.headers['Content-type'] = 'application/json'
return '[1]'
app.run(port=8001)
Méthode 2: Installation À L'Échelle Mondiale (Bouteille Plugin)
cette méthode est préférable si vous voulez que le gestionnaire exécute sur la totalité ou la plupart de vos routes. Vous allez juste définir un plugin de bouteille une fois, et la bouteille sera automatiquement appel pour vous sur chaque itinéraire; pas besoin de spécifier un décorateur sur chacun d'eux. (Notez que vous pouvez utiliser le paramètre skip
d'une route pour éviter ce handler sur une base par route.) Voici un exemple qui correspond à celui ci-dessus:
import bottle
from bottle import response
class EnableCors(object):
name = 'enable_cors'
api = 2
def apply(self, fn, context):
def _enable_cors(*args, **kwargs):
# set CORS headers
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token'
if bottle.request.method != 'OPTIONS':
# actual request; reply with the actual response
return fn(*args, **kwargs)
return _enable_cors
app = bottle.app()
@app.route('/cors', method=['OPTIONS', 'GET'])
def lvambience():
response.headers['Content-type'] = 'application/json'
return '[1]'
app.install(EnableCors())
app.run(port=8001)
voici une petite amélioration sur @ron.la méthode #2 de rothman pour installer le gestionnaire CORS globalement. Sa méthode exige que vous spécifiez que la méthode OPTIONS
est acceptée sur chaque route que vous déclarez. Cette solution installe un gestionnaire global pour toutes les requêtes OPTIONS
.
@bottle.route('/<:re:.*>', method='OPTIONS')
def enable_cors_generic_route():
"""
This route takes priority over all others. So any request with an OPTIONS
method will be handled by this function.
See: https://github.com/bottlepy/bottle/issues/402
NOTE: This means we won't 404 any invalid path that is an OPTIONS request.
"""
add_cors_headers()
@bottle.hook('after_request')
def enable_cors_after_request_hook():
"""
This executes after every route. We use it to attach CORS headers when
applicable.
"""
add_cors_headers()
def add_cors_headers():
if SOME_CONDITION: # You don't have to gate this
bottle.response.headers['Access-Control-Allow-Origin'] = '*'
bottle.response.headers['Access-Control-Allow-Methods'] = \
'GET, POST, PUT, OPTIONS'
bottle.response.headers['Access-Control-Allow-Headers'] = \
'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token'
""
envisagez de laisser votre serveur web, pas la bouteille, régler les en-têtes.
Je ne sais pas si cela s'applique à votre situation, mais j'ai résolu le problème dans des projets antérieurs en définissant des en-têtes CORS pour mon application de bouteilles dans Apache. Il est facile à configurer, maintient mon code Python propre et efficace.
informations disponibles à partir de nombreuses sources , mais si vous utilisez Apache, voici à quoi ressemble ma configuration (plus ou moins):
<Location "/cors">
Header set Access-Control-Allow-Headers "Origin, Content-Type"
Header set Access-Control-Allow-Methods "POST, GET, OPTIONS"
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Request-Headers "Origin, Content-Type"
</Location>
ne devriez-vous pas l'utiliser?
response.set_header('Access-Control-Allow-Origin', '*')
response.add_header('Access-Control-Allow-Methods', 'GET, POST, PUT, OPTIONS')