I am using Flask-RESTful for creating API endpoints, and I specify the URL this way:
api.add_resource(ListVersionsByStaff, '/shots/versions/<int:project_id>/<int:staff_id>')
api.add_resource(ListSeasons, '/seasons/<int:project_id>')
While Flask-RESTful will return an error response if the given argument is not int
, it will return a HTML response.
How can I return a custom JSON error response, for example:
except InvalidParameter as err:
abort(err.status_code, **err.to_dict())
Checking for the value this way also does not work, the parameter is always type String
class SpecificProject(Resource):
def get(self, project_id):
print("DEBUG: project_id is [", project_id, "]", file=sys.stderr)
print("DEBUG: Type is [", type(project_id), "]", file=sys.stderr)
if isinstance(project_id, int):
pass
else:
message = "'{}' is not a valid project_id. Hint: this is a number representing primary key.".format(project_id)
errors = {}
errors['resource'] = "Project"
errors['field'] = "project_id"
errors['code'] = "invalid"
errors['stack_trace'] = ""
abort(400, message=message, errors=errors)
output:
DEBUG: project_id is [ 1 ]
DEBUG: Type is [ <class 'str'> ]
My solution is to extend the Flask-RESTful Api
class and implement my custom error handler. The official documentation explains a little bit about extending , but did not go into sufficient details.
All errors in my application is in this structure
class InvalidParameter(Exception):
status_code = 400
def __init__(self, message, status_code=None, resource=None,
field=None, code=None, stack_trace=None):
Exception.__init__(self)
self.message = message
if status_code is not None:
self.status_code = status_code
self.resource = resource
self.field = field
self.code = code
self.stack_trace = stack_trace
def to_dict(self):
rv = {}
errors = {}
rv['message'] = self.message
errors['resource'] = self.resource
errors['field'] = self.field
errors['code'] = self.code
errors['stack_trace'] = self.stack_trace
rv['errors'] = errors
return rv
This is what Flask-RESTful return by default
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>404 Not Found</title>
<h1>Not Found</h1>
<p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>
The output should be JSON, in this format
{
"message": "The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. You have requested this URI [/projects/1asd] but did you mean /projects/<int:project_id> or /projects or /project/new ?",
"errors": {
"resource": null,
"field": null,
"code": "invalid_url",
"stack_trace": null
}
}
Extend the Api
class and overwrite the handle_error
method for 404
errors
class CustomApi(Api):
def handle_error(self, e):
code = getattr(e, 'code', 404)
if code == 404:
response_dict = Api.handle_error(self, e).__dict__
resp_message = response_dict['response'][0]
error_message = re.sub(r'.*"(.*)".*', r'\1', resp_message.decode('utf-8'), flags=re.DOTALL)
err = InvalidParameter(error_message, stack_trace=None,
resource=None, code="invalid_url", field=None)
return self.make_response(err.to_dict(), 404) #{'message': "something", 'error': {'sub1': 'val1'}}, 404)
return super(Api, self).handle_error(e)
The handle_error
dictionary is in this format
{'headers': Headers([('Content-Type', 'application/json'), ('Content-Length', '263')]), '_status_code': 404, '_status': '404 NOT FOUND', 'direct_passthrough': False, '_on_close': [], 'response': [b'{\n "message": "The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. You have requested this URI [/projects/1asd] but did you mean /projects/<int:project_id> or /projects or /project/new ?"\n}\n']}
I want to re-use the 'response':'message'
that was generated, but not in the default format. message
was not not properly JSON formatted, so I strip out everything except for the content of message
, using this regex
error_message = re.sub(r'.*"(.*)".*', r'\1', resp_message.decode('utf-8'), flags=re.DOTALL)
Note that re.DOTALL
is needed to strip out the \\n
that was added by Flask-RESTful.
The code that actually builds the JSON response is self.make_response(err.to_dict(), 404)
For all other non-404 errors (eg 400, 500, 503), the error is just passed along to the original Flask-RESTful Api class.
Note that when you create your Flask application, you need to use your custom Api class, and catch all 404 errors:
app = Flask(__name__)
api = CustomApi(app, catch_all_404s=True)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.