I have written a Flask
application that provides an API. I am using the RESTplus
library.
I use a model to format the data. If a request is successful, the values are inserted into the model and the model is returned.
However, if the request is unsuccessful, the model is returned and all values are null
. My goal is to return a user-defined error message with multiple key-value pairs. The error message should have a different structure than as Model.
Here is a minimal example:
from flask import Flask
from flask_restplus import Resource, fields, Api
app = Flask(__name__)
api = Api()
api.init_app(app)
books = {'1': {"id": 1, "title": "Learning JavaScript Design Patterns", 'author': "Addy Osmani"},
'2': {"id": 2, "title": "Speaking JavaScript", "author": "Axel Rauschmayer"}}
book_model = api.model('Book', {
'id': fields.String(),
'title': fields.String(),
'author': fields.String(),
})
@api.route('/books/<id>')
class ApiBook(Resource):
@api.marshal_with(book_model)
def get(self, id):
try:
return books[id]
except KeyError as e:
return {'message': 'Id does not exist'}
if __name__ == '__main__':
app.run()
Successful output
curl -X GET "http://127.0.0.1:5000/books/1" -H "accept: application/json"
{
"id": "1",
"title": "Learning JavaScript Design Patterns",
"author": "Addy Osmani"
}
Erroneous output
curl -X GET "http://127.0.0.1:5000/books/3" -H "accept: application/json"
{
"id": null,
"title": null,
"author": null
}
Is it possible to have a user-defined error message next to a model? Is there an alternative?
Don't catch the exception in the get
method and then return an object; anything you return from the method will be marshalled using the model.
Instead, follow the error handling documentation and use flask.abort()
to set a 404
response with message:
# at the top of your module
from flask import abort
# in the resource class
@api.marshal_with(book_model)
def get(self, id):
try:
return books[id]
except KeyError as e:
raise abort(404, 'Id does not exist')
The second argument you give abort()
is automatically turned into a JSON object with message
key, so {"message": "Id does not exist"}
.
You could also create a @api.errorhandler
registration for the KeyError
exception and turn that into a 404 response:
@api.errorhandler(KeyError)
def handle_keyerror(error):
return {"message": f"Object with id {error} could not be found"}, 404
and then don't catch the exception in your get()
methods:
@api.marshal_with(book_model)
def get(self, id):
return books[id]
Note that when ERROR_404_HELP
is set to True
(the default) then the message is added to by RestPlus with an alternative route suggestion, tacked on to every 404 response:
curl -X GET "http://127.0.0.1:5000/books/3" -H "accept: application/json"
{
"message": "Object with id '3' could not be found. You have requested this URI [/books/3] but did you mean /books/<id> ?"
}
This may not be that helpful in your specific case so you may want to disable ERROR_404_HELP
.