Bottle Py: Habilitación de CORS para solicitudes jQuery AJAX
(4)
Además, ¿no deberías estar usando esto?
response.set_header(''Access-Control-Allow-Origin'', ''*'')
response.add_header(''Access-Control-Allow-Methods'', ''GET, POST, PUT, OPTIONS'')
Estoy trabajando en una API REST de un servicio web en Bottle Web Framework y quiero acceder a los recursos con las llamadas jQuery AJAX.
Usando un cliente REST, las interfaces de recursos funcionan según lo previsto y manejan adecuadamente las solicitudes GET, POST, ... Pero al enviar una solicitud POST de AJAX de jQuery, la solicitud de verificación previa de OPCIONES resultante se deniega simplemente como ''405: Método no permitido''.
Intenté habilitar CORS en el servidor de Bottle, como se describe aquí: http://bottlepy.org/docs/dev/recipes.html#using-the-hooks-plugin Pero el enlace after_request nunca se llama para la solicitud OPTIONS.
Aquí está un extracto de mi servidor:
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]"
[...]
La llamada jQuery AJAX:
$.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);
}
});
El servidor solo registra un error 405:
192.168.169.3 - - [23/Jun/2013 17:10:53] "OPTIONS /cors HTTP/1.1" 405 741
$ .post funciona, pero no poder enviar solicitudes PUT anularía el propósito de un servicio RESTful. Entonces, ¿cómo puedo permitir que se maneje la solicitud de verificación previa de OPCIONES?
Aquí hay una pequeña mejora en el método # 2 de @ ron.rothman para instalar el controlador CORS a nivel mundial. Su método requiere que especifique que el método OPTIONS
se acepta en cada ruta que declara. Esta solución instala un controlador global para todas las solicitudes de 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''
`` `
Considere dejar que su servidor web, no Bottle, establezca los encabezados.
No estoy seguro de si esto se aplica a su situación, pero he resuelto el problema en proyectos anteriores al configurar los encabezados CORS para mi aplicación Bottle en Apache. Es fácil de configurar, mantiene mi código Python limpio y es eficiente.
La información está disponible de muchas fuentes , pero si está utilizando Apache, este es el aspecto de mi configuración (más o menos):
<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>
Instale un controlador en lugar de un gancho.
Hay dos formas complementarias en las que he hecho esto en el pasado: decorador o plugin Bottle. Les mostraré ambos y usted puede decidir si uno (o ambos) de ellos se ajusta a sus necesidades. En ambos casos, la idea general es: un controlador intercepta la respuesta antes de enviarla al cliente, inserta los encabezados CORS y luego continúa para devolver la respuesta.
Método 1: Instalar por ruta (Decorador)
Este método es preferible cuando solo desea ejecutar el controlador en algunas de sus rutas. Solo decora cada ruta en la que quieras que se ejecute. Aquí hay un ejemplo:
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étodo 2: Instalar globalmente (plugin de botella)
Este método es preferible si desea que el controlador se ejecute en todas o la mayoría de sus rutas. Solo definirá un plugin de Bottle una vez, y Bottle lo llamará automáticamente en cada ruta; No es necesario especificar un decorador en cada uno. (Tenga en cuenta que puede usar el parámetro de skip
una ruta para evitar este controlador en cada ruta). Aquí hay un ejemplo que corresponde al de arriba:
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)