.. _guide.exceptions: Exception handling ================== A good app is prepared even when something goes wrong: a service is down, the application didn't expect a given input type or many other errors that can happen in a web application. To react to these cases, we need a good exception handling mechanism and prepare the app to handle the unexpected scenarios. HTTP exceptions --------------- WebOb provides a collection of exceptions that correspond to HTTP status codes. They all extend a base class, ``webob.exc.HTTPException``, also available in webapp2 as ``webapp2.HTTPException``. An ``HTTPException`` is also a WSGI application, meaning that an instance of it can be returned to be used as response. If an ``HTTPException`` is not handled, it will be used as a standard response, setting the header status code and a default error message in the body. Exceptions in handlers ---------------------- Handlers can catch exceptions implementing the method :meth:`webapp2.RequestHandler.handle_exception`. It is a good idea to define a base class that catches generic exceptions, and if needed override ``handle_exception()`` in extended classes to set more specific responses. Here we will define a exception handling function in a base class, and the real app classes extend it:: import logging import webapp2 class BaseHandler(webapp2.RequestHandler): def handle_exception(self, exception, debug): # Log the error. logging.exception(exception) # Set a custom message. response.write('An error occurred.') # If the exception is a HTTPException, use its error code. # Otherwise use a generic 500 error code. if isinstance(exception, webapp2.HTTPException): response.set_status(exception.code) else: response.set_status(500) class HomeHandler(BaseHandler): def get(self): self.response.write('This is the HomeHandler.') class ProductListHandler(BaseHandler): def get(self): self.response.write('This is the ProductListHandler.') If something unexpected happens during the ``HomeHandler`` or ``ProductListHandler`` lifetime, ``handle_exception()`` will catch it because they extend a class that implements exception handling. You can use exception handling to log errors and display custom messages instead of a generic error. You could also render a template with a friendly message, or return a JSON with an error code, depending on your app. Exceptions in the WSGI app -------------------------- Uncaught exceptions can also be handled by the WSGI application. The WSGI app is a good place to handle '404 Not Found' or '500 Internal Server Error' errors, since it serves as a last attempt to handle all uncaught exceptions, including non-registered URI paths or unexpected application behavior. We catch exceptions in the WSGI app using error handlers registered in :attr:`webapp2.WSGIApplication.error_handlers`. This is a dictionary that maps HTTP status codes to callables that will handle the corresponding error code. If the exception is not an ``HTTPException``, the status code 500 is used. Here we set error handlers to handle "404 Not Found" and "500 Internal Server Error":: import logging import webapp2 def handle_404(request, response, exception): logging.exception(exception) response.write('Oops! I could swear this page was here!') response.set_status(404) def handle_500(request, response, exception): logging.exception(exception) response.write('A server error occurred!') response.set_status(500) app = webapp2.WSGIApplication([ webapp2.Route('/', handler='handlers.HomeHandler', name='home') ]) app.error_handlers[404] = handle_404 app.error_handlers[500] = handle_500 The error handler can be a simple function that accepts ``(request, response, exception)`` as parameters, and is responsible for setting the response status code and, if needed, logging the exception. abort() ------- The function :func:`webapp2.abort` is a shortcut to raise one of the HTTP exceptions provided by WebOb: it takes an HTTP status code (403, 404, 500 etc) and raises the corresponding exception. Use ``abort`` (or :func:`webapp2.RequestHandler.abort` inside handlers) to raise an ``HTTPException`` to be handled by an exception handler. For example, we could call ``abort(404)`` when a requested item is not found in the database, and have an exception handler ready to handle 404s. Besides the status code, some extra keyword arguments can be passed to ``abort()``: detail An explanation about the error. comment An more detailed comment to be included in the response body. headers Extra response headers to be set. body_template A string to be used as template for the response body. The default template has the following format, with variables replaced by arguments, if defined: .. code-block:: html ${explanation}<br /><br /> ${detail} ${html_comment}