我正在使用Bottle Web Framework上的Web服务的RESTful API,并希望通过jQuery AJAX调用来访问这些资源。
使用REST客户端,资源接口按预期工作并正确处理GET,POST,...请求。但是当发送jQuery AJAX POST请求时,生成的OPTIONS预检请求被简单地拒绝为'405:Method not allowed'。
我尝试在Bottle服务器上启用CORS - 如下所述:http://bottlepy.org/docs/dev/recipes.html#using-the-hooks-plugin 但是从不为OPTIONS请求调用 after_request钩子。
以下是我服务器的摘录:
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]"
[...]
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);
}
});
服务器仅记录405错误:
192.168.169.3 - - [23/Jun/2013 17:10:53] "OPTIONS /cors HTTP/1.1" 405 741
$ .post确实有效,但无法发送PUT请求会破坏RESTful服务的目的。 那么如何才能处理OPTIONS预检请求?
答案 0 :(得分:28)
安装处理程序而不是挂钩。
我过去有两种互补方式:装饰者或瓶子插件。我会告诉你们两个,你可以决定它们中的一个(或两个)是否适合你的需要。在这两种情况下,一般的想法是:处理程序在将响应发送回客户端之前拦截响应,插入CORS头,然后继续返回响应。
当您只想在某些路由上运行处理程序时,此方法更为可取。只需装饰您希望它执行的每条路线。这是一个例子:
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)
如果您希望处理程序在所有或大多数路由上执行,则此方法更为可取。您只需define a Bottle plugin一次,Bottle会在每条路线上自动为您拨打电话;无需在每个上指定装饰器。 (请注意,您可以使用路由的skip
参数来避免基于每个路由的此处理程序。)以下是与上一个对应的示例:
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)
答案 1 :(得分:4)
这是@ ron.rothman的方法#2在全局安装CORS处理程序的一个小改进。他的方法要求您指定在您声明的每个路径上接受OPTIONS
方法。此解决方案为所有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'
```
答案 2 :(得分:1)
考虑让您的网络服务器,而不是Bottle,设置标题。
不确定这是否适用于您的情况,但我通过在Apache中为我的Bottle应用程序设置CORS标头来解决过去项目中的问题。它易于配置,使我的Python代码保持良好和干净,并且效率很高。
信息可用from many sources,但是如果您使用的是Apache,那么这就是我的配置的样子(或多或少):
<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>
答案 3 :(得分:1)
你也不应该实际使用它吗?
response.set_header('Access-Control-Allow-Origin', '*')
response.add_header('Access-Control-Allow-Methods', 'GET, POST, PUT, OPTIONS')