我正在使用Bottle Web Framework上的Web服务的RESTful API,并希望通过jQuery AJAX调用来访问资源。
使用REST客户端,资源接口可按预期工作并正确处理GET,POST,…请求。但是,当发送jQuery AJAX POST请求时,最终的OPTIONS预检请求被简单地拒绝为“ 405:不允许使用方法”。
这是我的服务器的摘录:
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飞行前请求?
安装一个处理程序而不是一个钩子。
我过去有两种互补的方法:装饰器或Bottle插件。我将向您展示两者,您可以决定它们中的一个(或两者)是否满足您的需求。在这两种情况下,通常的想法是:处理程序在将响应发送回客户端之前先对其进行拦截,然后插入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)
如果希望处理程序在所有或大多数路由上执行,则最好使用此方法。您只需定义一次Bottle插件,Bottle就会在每条路线上自动为您调用它;无需在每一个上指定一个装饰器。(请注意,您可以使用路由的skip参数来避免针对每个路由使用此处理程序。)以下示例与上述示例相对应:
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)