【问题标题】:flask-restful - resource class for current requestflask-restful - 当前请求的资源类
【发布时间】:2019-11-01 00:39:14
【问题描述】:

问题

我的应用程序的所有路由都是通过 flask-restful 资源定义的。 如何找到正在处理当前请求的资源对象/类?

为什么我想要这个

我想记录处理请求时引发的所有异常。我连接到flask.got_request_exception,如http://flask.pocoo.org/docs/1.0/api/#signals 中所述,这样的东西效果很好:

from flask import got_request_exception, request

def log_exception(sender, exception, **extra):
    logger.info("URL: {}, Exception: {}".format(request.url, type(exception).__name__))

got_request_exception.connect(log_exception, app)

唯一的问题是我想记录一些请求数据,但不是所有数据 - 例如我想隐藏密码。我认为将日志记录数据逻辑与请求处理逻辑结合在一起是个好主意,如下所示:

from flask import request
import flask_restful

class SomeResource(flask_restful.Resource):
    def get(self):
        # ... GET processing
    def log_data(self):
        # log all body params
        return request.get_json()

class Login(flask_restful.Resource):
   def post(self):
       # ... authentication
   def log_data(self):
       # log selected body params
       return {'login': request.get_json()['login'], 'password': 'HIDDEN!'}

然后在我的log_exception 中使用它:

from flask import got_request_exception, request

def log_exception(sender, exception, **extra):
    resource_class = # THIS IS THE THING I'M MISSING
    logger.info("URL: {}, Exception: {}, Data: {}".format(request.url, type(exception).__name__), 
                resource_class.log_data())

got_request_exception.connect(log_exception, app) 

但也许这应该以其他方式完成?

【问题讨论】:

    标签: python flask flask-restful


    【解决方案1】:

    您不想从 flask_restful.Resource 继承,而是从自定义资源中继承所有内容

    class MyResource(flask_restful.Resource):
        def dispatch_request(self, *args, **kwargs):
            try:
                return super(MyResource,self).dispatch_request(*args, **kwargs)
            except Exception as ex:
                setattr(ex, "_raised_by", self)
                raise ex
    

    然后你可以使用异常处理程序

    def log_exception(sender, exception, **extra):
        _raised_by = getattr(exception, "_raised_by", None)
        if _raised_by:
            print(_raised_by)
        property("URL: {}, Exception: {}".format(request.url, type(exception).__name__))
    

    这是我尝试过的完整代码

    from flask import request, Flask
    import flask_restful
    
    app = Flask(__name__)
    
    api = flask_restful.Api(app)
    
    
    class MyResource(flask_restful.Resource):
        def dispatch_request(self, *args, **kwargs):
            try:
                return super(MyResource,self).dispatch_request(*args, **kwargs)
            except Exception as ex:
                setattr(ex, "_raised_by", self)
                raise ex
    
    # MyResource = flask_restful.Resource
    
    class SomeResource(MyResource):
        def get(self):
            raise Exception("Not implemented")
    
        def log_data(self):
            # log all body params
            return request.get_json()
    
    
    class Login(MyResource):
        def post(self):
            raise Exception("Not implemented")
    
        def log_data(self):
            # log selected body params
            return {'login': request.get_json()['login'], 'password': 'HIDDEN!'}
    
    
    from flask import got_request_exception, request
    
    api.add_resource(Login, '/login')
    api.add_resource(SomeResource, '/some')
    
    
    def log_exception(sender, exception, **extra):
        _raised_by = getattr(exception, "_raised_by", None)
        if _raised_by:
            print(_raised_by)
        property("URL: {}, Exception: {}".format(request.url, type(exception).__name__))
    
    
    got_request_exception.connect(log_exception, app)
    
    if __name__ == '__main__':
        app.run(debug=True)
    

    Edit-1:8 月 5 日

    正如@jbet 评论的那样,如果想要获得处理类,一个更简洁的选择是使用MyResource,如下所示

    from flask import g
    
    class MyResource(flask_restful.Resource):
        def dispatch_request(self, *args, **kwargs):
            g.processed_by = self
            return super(MyResource,self).dispatch_request(*args, **kwargs)
    

    【讨论】:

    • 谢谢,这就是我要找的。​​span>
    • 我修改了一些你的dispatch_request,而不是try/except和setattr,我只是在调用super().dispatch_request之前做from flask import g,g.processed_by = self。我认为这是更清洁的解决方案,并且在请求无一例外地结束时也可以使用。
    【解决方案2】:

    一旦信号调用了日志记录方法,我就找不到访问对象的好方法。

    如果您愿意处理所有可能的情况,您可以创建自己的自定义异常,在您的类上调用 log_data 方法。

    相反,我选择在基类中处理自己的日志记录。给你一个简单的例子,我只是使用了打印函数,但你可以调用 app.logging.info 来代替。

    from flask import Flask, request
    import flask_restful
    
    app = Flask(__name__)
    api = flask_restful.Api(app)
    
    class MyCustomResource(flask_restful.Resource):
        def get(self):
            try:
                self.my_get()
            except Exception as exception:
                # Will catch all errors in your subclass my_get method
                print("exception caught")
                print(request.url)
                print(type(exception).__name__)
                print(self.log_data())
    
                # Re-raise if you want (or not)
                raise exception
    
        def my_get(self):
            # Python equivalent of virtual method
            raise NotImplementedError()
    
        def log_data(self):
            # Python equivalent of virtual method
            raise NotImplementedError()
    
    
    class SomeResource(MyCustomResource):
        def my_get(self):
            # Example unknown error occurs here
            raise Exception("error!")
            return "some data"
    
        def log_data(self):
            # Called by parent
            return "some logging data for the object"
    
    api.add_resource(SomeResource, "/")
    

    如果您想更深入地研究flask restful 源代码,您还可以使用猴子补丁(或构建自己的包)调用get/post 的任何方法。

    【讨论】:

      猜你喜欢
      • 2014-04-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-25
      • 2021-04-22
      • 2013-12-08
      • 2017-08-01
      • 1970-01-01
      相关资源
      最近更新 更多