Bottle Py:为 jQuery AJAX 请求启用 CORS

2022-01-15 00:00:00 cors jquery ajax bottle

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

I'm working on a RESTful API of a web service on the Bottle Web Framework and want to access the resources with jQuery AJAX calls.

使用 REST 客户端,资源接口按预期工作并正确处理 GET、POST、... 请求.但是在发送 jQuery AJAX POST 请求时,生成的 OPTIONS 预检请求会被简单地拒绝为 '405: Method not allowed'.

Using a REST client, the resource interfaces work as intended and properly handle GET, POST, ... requests. But when sending a jQuery AJAX POST request, the resulting OPTIONS preflight request is simply denied as '405: Method not allowed'.

我尝试在 Bottle 服务器上启用 CORS - 如下所述:http://bottlepy.org/docs/dev/recipes.html#using-the-hooks-plugin但是 after_request 钩子 永远不会为 OPTIONS 请求调用.

I tried to enable CORS on the Bottle server - as described here: http://bottlepy.org/docs/dev/recipes.html#using-the-hooks-plugin But the after_request hook is never called for the OPTIONS request.

这是我的服务器的摘录:

Here is an excerpt of my server:

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 调用:

The jQuery AJAX call:

$.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 错误:

The server only logs a 405 error:

192.168.169.3 - - [23/Jun/2013 17:10:53] "OPTIONS /cors HTTP/1.1" 405 741

$.post 确实有效,但无法发送 PUT 请求会破坏 RESTful 服务的目的.那么如何允许处理 OPTIONS 预检请求呢?

$.post does work, but not being able to send PUT requests would defeat the purpose of a RESTful service. So how can I allow the OPTIONS preflight request to be handled?

推荐答案

安装处理程序而不是钩子.

Install a handler instead of a hook.

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

There are two complementary ways I've done this in the past: decorator, or Bottle plugin. I'll show you both and you can decide whether one (or both) of them suit your needs. In both cases, the general idea is: a handler intercepts the response before it's sent back to the client, inserts the CORS headers, and then proceeds to return the response.

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

This method is preferable when you only want to run the handler on some of your routes. Just decorate each route that you want it to execute on. Here's an example:

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 插件 一次,Bottle 会自动为您调用它每条路线;无需为每个指定装饰器.(请注意,您可以使用路由的 skip 参数在每个路由的基础上避免此处理程序.)下面是一个对应于上述示例的示例:

Method 2: Install Globally (Bottle Plugin)

This method is preferable if you want the handler to execute on all or most of your routes. You'll just define a Bottle plugin once, and Bottle will automatically call it for you on every route; no need to specify a decorator on each one. (Note that you can use a route's skip parameter to avoid this handler on a per-route basis.) Here's an example that corresponds to the one above:

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)

相关文章