THE QUESTION
All of my app's routes are defined via flask-restful Resources. How can I find the resource object/class that is processing current request?
WHY I WANT THIS
I wanted to log all exceptions raised while processing requests. I connect to flask.got_request_exception
, as described in http://flask.pocoo.org/docs/1.0/api/#signals and something like this works well:
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)
The only problem is that I want to log some of the request data, but not all the data - e.g. I'd like to hide passwords. I think it would be a good idea to have logging-data-logic together with request processing logic, like this:
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!'}
and than use it in my 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)
But maybe this should be done other way?
Instead of inheriting from flask_restful.Resource
your want to inherit all from a custom 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
and then you can use the exception handler
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__))
Here is complete code I tried
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: 5th Aug
As commented by @jbet, in case one wants to get the processing class always a cleaner option would be to use the MyResource
as below
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)