1.. _guide.exceptions:
2
3Exception handling
4==================
5A good app is prepared even when something goes wrong: a service is down,
6the application didn't expect a given input type or many other errors that
7can happen in a web application. To react to these cases, we need a good
8exception handling mechanism and prepare the app to handle the unexpected
9scenarios.
10
11
12HTTP exceptions
13---------------
14WebOb provides a collection of exceptions that correspond to HTTP status codes.
15They all extend a base class, ``webob.exc.HTTPException``, also available in
16webapp2 as ``webapp2.HTTPException``.
17
18An ``HTTPException`` is also a WSGI application, meaning that an instance of it
19can be returned to be used as response. If an ``HTTPException`` is not handled,
20it will be used as a standard response, setting the header status code and
21a default error message in the body.
22
23
24Exceptions in handlers
25----------------------
26Handlers can catch exceptions implementing the method
27:meth:`webapp2.RequestHandler.handle_exception`. It is a good idea to define
28a base class that catches generic exceptions, and if needed override
29``handle_exception()`` in extended classes to set more specific responses.
30
31Here we will define a exception handling function in a base class, and the real
32app classes extend it::
33
34    import logging
35
36    import webapp2
37
38    class BaseHandler(webapp2.RequestHandler):
39        def handle_exception(self, exception, debug):
40            # Log the error.
41            logging.exception(exception)
42
43            # Set a custom message.
44            response.write('An error occurred.')
45
46            # If the exception is a HTTPException, use its error code.
47            # Otherwise use a generic 500 error code.
48            if isinstance(exception, webapp2.HTTPException):
49                response.set_status(exception.code)
50            else:
51                response.set_status(500)
52
53    class HomeHandler(BaseHandler):
54        def get(self):
55            self.response.write('This is the HomeHandler.')
56
57    class ProductListHandler(BaseHandler):
58        def get(self):
59            self.response.write('This is the ProductListHandler.')
60
61If something unexpected happens during the ``HomeHandler`` or
62``ProductListHandler`` lifetime, ``handle_exception()`` will catch it because
63they extend a class that implements exception handling.
64
65You can use exception handling to log errors and display custom messages
66instead of a generic error. You could also render a template with a friendly
67message, or return a JSON with an error code, depending on your app.
68
69
70Exceptions in the WSGI app
71--------------------------
72Uncaught exceptions can also be handled by the WSGI application. The WSGI app
73is a good place to handle '404 Not Found' or '500 Internal Server Error'
74errors, since it serves as a last attempt to handle all uncaught exceptions,
75including non-registered URI paths or unexpected application behavior.
76
77We catch exceptions in the WSGI app using error handlers registered in
78:attr:`webapp2.WSGIApplication.error_handlers`. This is a dictionary that
79maps HTTP status codes to callables that will handle the corresponding error
80code. If the exception is not an ``HTTPException``, the status code 500 is
81used.
82
83Here we set error handlers to handle "404 Not Found" and "500 Internal Server
84Error"::
85
86    import logging
87
88    import webapp2
89
90    def handle_404(request, response, exception):
91        logging.exception(exception)
92        response.write('Oops! I could swear this page was here!')
93        response.set_status(404)
94
95    def handle_500(request, response, exception):
96        logging.exception(exception)
97        response.write('A server error occurred!')
98        response.set_status(500)
99
100    app = webapp2.WSGIApplication([
101        webapp2.Route('/', handler='handlers.HomeHandler', name='home')
102    ])
103    app.error_handlers[404] = handle_404
104    app.error_handlers[500] = handle_500
105
106The error handler can be a simple function that accepts
107``(request, response, exception)`` as parameters, and is responsible for
108setting the response status code and, if needed, logging the exception.
109
110
111abort()
112-------
113The function :func:`webapp2.abort` is a shortcut to raise one of the HTTP
114exceptions provided by WebOb: it takes an HTTP status code (403, 404, 500 etc)
115and raises the corresponding exception.
116
117Use ``abort`` (or :func:`webapp2.RequestHandler.abort` inside handlers)
118to raise an ``HTTPException`` to be handled by an exception handler.
119For example, we could call ``abort(404)`` when a requested item is not found
120in the database, and have an exception handler ready to handle 404s.
121
122Besides the status code, some extra keyword arguments can be passed to
123``abort()``:
124
125detail
126  An explanation about the error.
127comment
128  An more detailed comment to be included in the response body.
129headers
130  Extra response headers to be set.
131body_template
132  A string to be used as template for the response body. The default template
133  has the following format, with variables replaced by arguments, if defined:
134
135.. code-block:: html
136
137   ${explanation}<br /><br />
138   ${detail}
139   ${html_comment}
140