【问题标题】:Route requests based on the Accept header in Flask根据 Flask 中的 Accept 标头路由请求
【发布时间】:2015-05-01 17:33:54
【问题描述】:

我想根据 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")

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

我不想将视图合并为一个,因为这会使代码变得非常复杂,其中有很多并且它们驻留在不同的蓝图中。

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

【问题讨论】:

  • Flask-Restful 做了一些类似的;它具有根据Accept 标头格式化视图结果的可插拔支持。默认情况下它只支持 JSON,但可以扩展。见Content Negotiation
  • 它的方法是不同的;它以pluggable views 为基础进行最终路由; Flask 已经提供了基于方法的调度,我相信同样的技术可以用于以类似的方式进行基于接受的调度。
  • @MartijnPieters 谢谢,这很有趣。我相信 Flask 使用 Werkzeug 来做基于 HTTP 方法的路由。可插拔视图可以帮助更好地组织代码,但这并不理想,因为最终我只注册了一个路由。
  • 是的,Werkzeug 负责路由,并且该机制不支持基于标头的路由。它只支持方法,扩展它并不容易。
  • 如果有人感兴趣,我写了一个decorator,它允许在 Flask 中进行这种路由。

标签: python flask http-headers werkzeug


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

    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)
    

    【讨论】:

    • 感谢您的信息,看起来有新的库/可能性可用。
    • 参见 github.com/di/flask-accept/issues/11 - 无法首先让它工作,必须在 macports 上升级到 python3.8,确保所有端口选择都正常。重启并升级 liclipse python 环境。
    【解决方案3】:

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

    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'}
    

    【讨论】:

      猜你喜欢
      • 2012-12-09
      • 2020-03-06
      • 1970-01-01
      • 2015-04-17
      • 2010-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多