|
| 1 | +Implementing API Exceptions |
| 2 | +=========================== |
| 3 | + |
| 4 | +It's very common to implement RESTful APIs on top of Flask. One of the |
| 5 | +first thing that developers run into is the realization that the builtin |
| 6 | +exceptions are not expressive enough for APIs and that the content type of |
| 7 | +``text/html`` they are emitting is not very useful for API consumers. |
| 8 | + |
| 9 | +The better solution than using ``abort`` to signal errors for invalid API |
| 10 | +usage is to implement your own exception type and install an error handler |
| 11 | +for it that produces the errors in the format the user is expecting. |
| 12 | + |
| 13 | +Simple Exception Class |
| 14 | +---------------------- |
| 15 | + |
| 16 | +The basic idea is to introduce a new exception that can take a proper |
| 17 | +human readable message, a status code for the error and some optional |
| 18 | +payload to give more context for the error. |
| 19 | + |
| 20 | +This is a simple example:: |
| 21 | + |
| 22 | + from flask import jsonfiy |
| 23 | + |
| 24 | + class InvalidUsage(Exception): |
| 25 | + status_code = 400 |
| 26 | + |
| 27 | + def __init__(self, message, status_code=None, payload=None): |
| 28 | + Exception.__init__(self) |
| 29 | + self.message = message |
| 30 | + if status_code is not None: |
| 31 | + self.status_code = status_code |
| 32 | + self.payload = payload |
| 33 | + |
| 34 | + def to_dict(self): |
| 35 | + rv = dict(self.payload or ()) |
| 36 | + rv['message'] = self.message |
| 37 | + return rv |
| 38 | + |
| 39 | +A view can now raise that exception with an error message. Additionally |
| 40 | +some extra payload can be provided as a dictionary through the `payload` |
| 41 | +parameter. |
| 42 | + |
| 43 | +Registering an Error Handler |
| 44 | +---------------------------- |
| 45 | + |
| 46 | +At that point views can raise that error, but it would immediately result |
| 47 | +in an internal server error. The reason for this is that there is no |
| 48 | +handler registered for this error class. That however is easy to add:: |
| 49 | + |
| 50 | + @app.errorhandler(InvalidAPIUsage) |
| 51 | + def handle_invalid_usage(error): |
| 52 | + response = jsonify(error.to_dict()) |
| 53 | + response.status_code = error.status_code |
| 54 | + return response |
| 55 | + |
| 56 | +Usage in Views |
| 57 | +-------------- |
| 58 | + |
| 59 | +Here is how a view can use that functionality:: |
| 60 | + |
| 61 | + @app.route('/foo') |
| 62 | + def get_foo(): |
| 63 | + raise InvalidUsage('This view is gone', status_code=410) |
0 commit comments