Flask-RESTful - 当前请求的资源类

7

问题

我的应用程序的所有路由都是通过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) 

但也许这件事情应该用其他方式完成?

2个回答

3

不要继承自 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)

编辑-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)

谢谢,这就是我在寻找的。 - jbet
我修改了一下你的 dispatch_request,不再使用 try/exceptsetattr,而是在调用 super().dispatch_request 之前使用 from flask import gg.processed_by = self。我认为这是更干净的解决方案,并且在请求没有异常时也可以正常工作。 - jbet

2

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

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

相反,我选择在基类中处理日志记录。这里有一个简单的例子,我只是使用了print函数,但您可以调用app.logging.info代替。

“Original Answer”翻译成中文为“最初的回答”

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的任何方法。
最初的回答:如果你想更深入地了解Flask Restful的源代码,你也可以通过猴子补丁(或者构建自己的软件包)修改任何调用get/post方法的函数。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接