Error Documents
+++++++++++++++

Introduction
============

When an application returns a status code other than ``200``, which is the code for "everything went fine", you might wish to display a message to the user explaining what happened with an error document. 

Pylons provides a facility for displaying customized error messages to users via the ``ErrorDocuments`` middleware. For those familiar with Apache's ``ErrorDocument`` directive, Pylons' ``ErrorDocuments`` provides similar functionality.

If an error document is not provided by an application, users will see the browser's default page for that error, or a blank screen, depending on their browser.

The Default Setup
=================

Newly created Pylons applications come configured with the ``ErrorDocuments`` middleware enabled. The middleware is setup to call Pylons ``error_mapper`` function; which in turn calls the application's ``ErrorController``. The ``ErrorController`` renders the actual error document shown to the user; it uses Pylons' ``error_document_template`` HTML by default.

Every part of the error documents system can be configured and customized to provide whatever error handling you like.

Disabling Error Documents Support
=================================

To disable error documents, just remove the following lines from your ``config/middleware.py`` file:

.. code-block:: Python

    # @@@ Display error documents for 401, 403, 404 status codes (if debug is False also intercepts 500)  @@@
     app = ErrorDocuments(app, global_conf, mapper=error_mapper, **app_conf)

Any status codes will no longer be intercepted. 

Changing the template
=====================

To use a different template for the error documents, modify your ``ErrorController`` (in ``controllers/error.py``). Change the ``document()`` action so that rather than using the ``pylons.middleware.error_document_template`` HTML string as a template, it uses your own template:

.. code-block:: Python

    def document(self):
        my_template="""
        <html>
        <head><title>Error %(code)s</title></head>
        <body>
        <h1>Error %(code)s</h1>
        <p>%(message)s</p>
        </body>
        </html>
        """ % {'code':request.GET('code', ''), 'message':request.GET('message', '')}
        return Response(my_template)

The other actions in the default ``ErrorController`` are most likely unnecessary when using a customized template. They serve images and style sheets from the Pylons package, for use by Pylons' ``error_document_template``, without the need for those files to be located in your application's ``public`` directory.


Changing the error mapper
=========================

By default the status codes 404, 500, 401 and 403 are intercepted and redirected to the error controller. This is done by the error mapper specified in the error documents middleware. Have a look at your ``config/middleware.py`` file:

.. code-block:: Python

    # @@@ Display error documents for 401, 403, 404 status codes (if debug is False also intercepts 500)  @@@
    app = ErrorDocuments(app, global_conf, mapper=error_mapper, **kw)
    
By default the ``pylons.middleware.error_mapper`` is used. It looks like this:

.. code-block:: Python

    from urllib import urlencode
    from paste.deploy.converters import asbool
    
    def error_mapper(code, message, environ, global_conf=None, **kw):
        if global_conf is None:
            global_conf = {}
        codes = [401, 403, 404]
        if not asbool(global_conf.get('debug')):
            codes.append(500)
        if code in codes:
            url = '%s/error/document/?%s' % (get_prefix(environ),
                                             urlencode({'message':message, 'code':code}))
            return url
            
Note how in debug mode the mapper does not intercept server error 500 codes as they are used for the error report produced by the ``PylonsEvalException`` middleware.

You can create your own error mapper based on this one to intercept different status codes by changing the line ``if code in [401, 403, 404, 500]``. You can even change the URL so that a static file is served from your ``public`` directory rather than by the error controller. Just specify your error_mapper as the ``mapper`` parameter to middleware.

Testing Your Error Document
===========================

You can test your error document implementation by aborting a request with ``abort()``. The method takes two parameters: the first is an integer representing the error code, the second is the response message to send as part of the status HTTP header.

Add a controller to your project named ``error_doc_test``::
    
    paster controller error_doc_test
    
Modify your controller's ``index()`` action to look like this:

.. code-block:: Python

    def index(self):
        abort(500, 'Internal Server Error')
        
Then start your server as described in the `Getting Started Guide <getting_started.html>`_ and visit http://localhost:5000/error_doc_test/

You should see the error 500 page with an ``Internal Server Error`` message.
