22

我正在开发 Bottle Web 框架上的 Web 服务的 RESTful API,并希望通过 jQuery AJAX 调用访问资源。

使用 REST 客户端,资源接口按预期工作并正确处理 GET、POST、... 请求。但是在发送 jQuery AJAX POST 请求时,生成的 OPTIONS 预检请求被简单地拒绝为“405:不允许方法”。

我尝试在 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 预检请求呢?

4

4 回答 4

35

安装处理程序而不是挂钩。

我过去有两种互补的方法:装饰器或瓶子插件。我将向您展示两者,您可以决定其中一个(或两个)是否适合您的需求。在这两种情况下,一般的想法是:处理程序在将响应发送回客户端之前拦截响应,插入 CORS 标头,然后继续返回响应。

方法 1:安装 Per-route(装饰器)

当您只想在某些路由上运行处理程序时,此方法更可取。只需装饰您希望它执行的每条路线。这是一个例子:

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 Plugin)

如果您希望处理程序在所有或大部分路由上执行,则此方法更可取。您只需定义一次 Bottle 插件,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)
于 2013-06-23T16:38:01.423 回答
7

这是@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'

```

于 2017-01-12T05:43:59.287 回答
2

你也不应该真的使用这个吗?

response.set_header('Access-Control-Allow-Origin', '*')
response.add_header('Access-Control-Allow-Methods', 'GET, POST, PUT, OPTIONS')
于 2018-02-21T19:44:33.743 回答
1

考虑让你的网络服务器,而不是 Bottle,设置标题。

不确定这是否适用于您的情况,但我通过在 Apache 中为我的 Bottle 应用程序设置 CORS 标头解决了过去项目中的问题。它易于配置,让我的 Python 代码保持整洁,并且高效。

信息可从许多来源获得,但如果您使用的是 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>
于 2013-06-24T06:15:44.920 回答