根据Flask中的Accept标头路由请求

时间:2015-03-01 08:40:15

标签: python flask http-headers werkzeug

我想基于Accept HTTP标头路由到不同的Flask视图,例如:

@api.route('/test', accept='text/html')
def test_html():
    return "<html><body>Test</body></html>"

@api.route('/test', accept='text/json')
def test_json():
    return jsonify(test="Test")

我没有找到Flask使用的Werkzeug Rule constructor中的相关选项。它是一个缺失的功能还是可以以不同的方式实现相同的效果,例如通过在路由之前拦截和修改URL路径?

我不想将视图合并为一个,因为它会使代码复杂化,其中有很多并且它们位于不同的蓝图中。

我知道已经问过similar question,但是没有人使用Flask回答它。可以在不同的Web框架中进行,例如在Pyramid中使用predicates - 示例代码可以在this answer中找到。

3 个答案:

答案 0 :(得分:4)

我写了decorator这样做(在这里复制后代)。它只是一个可以进一步改进的粗略想法(例如,当没有匹配给定MIME类型的处理程序时,返回406 Not Acceptable响应而不是使用默认处理程序)。评论中有更多解释。

import functools
from flask import Flask, request, jsonify

app = Flask(__name__)

def accept(func_or_mimetype=None):
    """Decorator which allows to use multiple MIME type handlers for a single
    endpoint.
    """

    # Default MIME type.
    mimetype = 'text/html'

    class Accept(object):
        def __init__(self, func):
            self.default_mimetype = mimetype
            self.accept_handlers = {mimetype: func}
            functools.update_wrapper(self, func)

        def __call__(self, *args, **kwargs):
            default = self.default_mimetype
            mimetypes = request.accept_mimetypes
            best = mimetypes.best_match(self.accept_handlers.keys(), default)
            # In case of Accept: */*, choose default handler.
            if best != default and mimetypes[best] == mimetypes[default]:
                best = default
            return self.accept_handlers[best](*args, **kwargs)

        def accept(self, mimetype):
            """Register a MIME type handler."""

            def decorator(func):
                self.accept_handlers[mimetype] = func
                return func
            return decorator

    # If decorator is called without argument list, return Accept instance.
    if callable(func_or_mimetype):
        return Accept(func_or_mimetype)

    # Otherwise set new MIME type (if provided) and let Accept act as a
    # decorator.
    if func_or_mimetype is not None:
        mimetype = func_or_mimetype
    return Accept

@app.route('/')
@accept     # Or: @accept('text/html')
def index():
    return '<strong>foobar</strong>'

@index.accept('application/json')
def index_json():
    return jsonify(foobar=True)

@index.accept('text/plain')
def index_text():
    return 'foobar\n', 200, {'Content-Type': 'text/plain'}

答案 1 :(得分:4)

我知道这是一个老问题,但我最终在这里寻找类似的东西,所以我希望它可以帮助其他人。

flask_accept具有通过不同路由处理不同Accept类型的功能。

from flask import Flask, jsonify
from flask_accept import accept
app = Flask(__name__)

@app.route('/')
@accept('text/html')
def hello_world():
    return 'Hello World!'

@hello_world.support('application/json')
def hello_world_json():
    return jsonify(result="Hello World!")

if __name__ == '__main__':
    app.run()

如果您只想拒绝请求,具体取决于它们是否是特定数据类型,您还可以使用Flask-Negotiate

from flask import Flask
from flask_negotiate import consumes, produces

app = Flask(__name__)

@app.route('/consumes_json_only')
@consumes('application/json')
def consumes_json_only():
    return 'consumes json only'
  

当尝试访问没有有效Accept标头的端点时:

$ curl localhost:5000 -I
HTTP 415 (Unsupported Media Type)

答案 2 :(得分:0)

您可以使用request根据Accept标头返回不同的响应类型。例子。

if request.accept_mimetypes['application/json']:
           return jsonify(<object>), '200 OK'