【问题标题】:Bottle Py: Enabling CORS for jQuery AJAX requestsBottle Py:为 jQuery AJAX 请求启用 CORS
【发布时间】:2013-06-20 04:11:15
【问题描述】:

我正在开发 Bottle Web 框架上的 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 但是 after_request 钩子永远不会为 OPTIONS 请求调用。

这是我的服务器的摘录:

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 预检请求呢?

【问题讨论】:

    标签: jquery ajax cors bottle


    【解决方案1】:

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

    response.set_header('Access-Control-Allow-Origin', '*')
    response.add_header('Access-Control-Allow-Methods', 'GET, POST, PUT, OPTIONS')
    

    【讨论】:

    • 这对我来说适用于 Python 3.6 和瓶 0.12.16。尝试写入标头时,接受的答案抛出错误
    【解决方案2】:

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

    ```

    【讨论】:

    • 此方法适用于装在瓶子里的 cheroot cherrypy 服务器,谢谢
    • 您能添加一条路线吗?我不知道怎么用。
    【解决方案3】:

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

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

    方法一:安装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 插件)

    如果您希望处理程序在所有或大部分路由上执行,则此方法更可取。您只需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)
    

    【讨论】:

    • 嗨,罗恩,非常感谢您的回答。我已经尝试了您的两种方法,但不幸的是,它们都不适合我。问题仍然存在,即 OPTIONS 请求被错误 405 彻底拒绝。此外,方法 2 与通过挂钩调用 enable_cors 之间是否存在显着差异?对我来说,它们看起来几乎一样(尽管我不是蟒蛇也不是瓶子专家)。似乎可行的是这个 cors_plugin (github.com/myGengo/bottle-cors)。似乎在 RequestPreflightPlugin 类中添加了一个专用的@options 路由。这似乎可以解决问题。知道为什么您的代码不起作用吗?
    • 糟糕,抱歉,我忘记在 route 调用中包含 method 参数,以告诉 Bottle 接受 OPTIONS 请求。 (否则默认方法就是GET。)我已经更新了代码sn-ps;看起来它现在正在工作;希望对你也有用。
    • 好的,现在可以了!但是使用您的解决方案,我需要为我想要允许预检的每个请求(即所有在我的 REST API 中)的方法 OPTIONS 创建一个新路由?它不能被现有路由捎带,因为定义的方法期望请求.json 被正确填充或不正确(即错误)。
    • 酷!嗯,您最初的基于钩子的想法不会具有相同的属性吗? (即,与期望 request.json 的方法发生冲突。)无论如何,我认为这是可以解决的;如果你需要帮助,我可以修改我的代码,没问题。
    • 另外:如果服务器收到的第一个请求是预检的,则第一个评论中链接的 cors_plugin 似乎不起作用。未预检的请求似乎触发了 OPTIONS 方法的注册,然后才处理 OPTIONS 请求。很尴尬。
    【解决方案4】:

    考虑让您的网络服务器,而不是 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>
    

    【讨论】:

    • 我使用异步 GEVENT pywsgi 和一个 AWS 负载均衡器。我真的没有看到使用 AWS 中可用的负载均衡器选项运行 apache 的意义。
    • 谢谢。 OP 没有提到 AWS,所以不确定这与这个问题有什么关系。
    猜你喜欢
    • 2015-12-15
    • 2013-12-24
    • 2015-02-27
    • 2013-12-06
    • 2014-09-13
    • 1970-01-01
    • 2019-04-07
    相关资源
    最近更新 更多