I have error handlers in my app. I add these using connexion's add_error_handler which is calling flask's register_error_handler. I wish to restructure the error data that is returned by my endpoints in this handler. However, since I have so many unit tests reliant on the old structure, I wish to implement the new error structure for a subset of endpoints first. I believe I can do this as follows:
from flask import request
new_endpoints = ("/new_endpoint",)
def is_new_endpoint():
return request.path in new_endpoints
def my_error_handler(e):
if is_new_endpoint():
return FlaskApi.response(new_error_response(e))
else:
return FlaskApi.response(old_error_response(e))
Is there another approach to doing this? The problem I have is that I believe that the is_new_endpoint function might get messy.
I define my endpoints in a yaml file, and for each endpoint, I have an operationId which specifies the python function associated with the endpoint. Maybe I could decorate these functions to define them as new and have this information available in the error handler. Could this be a possible alternative approach? Could I use flask.g for this?
Not sure if this is what you are looking for. But as far as I understood the main idea was to avoid describing routes paths in the application config(new_endpoints = ...
).
Yes. Looks like you can use flask.g
+ custom decorator
. Here is an example:
from functools import wraps
from flask import Flask, request, g
app = Flask(__name__)
def new_endpoint_decorator():
def _new_endpoint(f):
@wraps(f)
def __new_endpoint(*args, **kwargs):
# register request path
if not hasattr(g, 'new_endpoints'):
g.new_endpoints = set()
g.new_endpoints.add(request.path)
return f(*args, **kwargs)
return __new_endpoint
return _new_endpoint
@app.route('/old_endpoint')
def old_endpoint():
raise ValueError('old_endpoint')
@app.route('/new_endpoint')
@new_endpoint_decorator() # endpoint will use a new error format
def new_endpoint():
raise ValueError('new_endpoint')
@app.errorhandler(ValueError)
def error_handler(error):
# checking flask.g on new_endpoints
if hasattr(g, 'new_endpoints') and request.path in g.new_endpoints:
return f'new error: {error}'
return f'legacy error: {error}'
if __name__ == '__main__':
app.run('localhost', debug=True)
Let's check:
curl http://localhost:5000/new_endpoint # new error: new_endpoint
curl http://localhost:5000/old_endpoint # legacy error: old_endpoint
It's probably best to replace @new_endpoint_decorator()
to @legacy_response()
because old routes usually don't change. So you can just add decorator to all legacy routes 1 time and forget it.