Personalize your Error pages in Turbogears 2

I was looking for a way to propagate exceptions from my tg2 app to the ErrorController to permit to propagate errors from controllers to the user. To generate errors and show them you usually have to redirect to /error/document and pass as parameters the error message and error code, but this isn’t really flexible and also modern languages have a really good feature to propagate errors: Exceptions.

So I was looking for a way to raise a webob.exc.HTTPForbidden, place a message inside it and let the ErrorController render the message. Usually you don’t want to tell to the user what went wrong with your 500 server side exception, but you might want to tell to the user why he can’t do what he is trying to do, let him know why it is Forbidden to him.

First thing you can easily do is check for resp.status_int inside your ErrorController.document and fetch only 403 error (the forbidden one). This permits to create a specific message for Forbidden errors, but doesn’t tell much to the user about why it is forbidden. webob.exc.HTTPForbidden permits to set a detail message and also generates an error page, but the turbogears stack when gets a status code different from 200 hooks the response and calls the ErrorController.document to generate a new response. This way your HTTPForbidden exception is lost forever.

Actually it isn’t really lost, as you can access the previous error page from request.environ.get(‘pylons.original_response’). If you  want a quick solution you can hook ErrorController if status_int is 403 and return the original_response instead of rendering ErrorController.document template.

But the original response isn’t really nice and you usually want to adapt it.

My solution has been to create a new ApplicationError(webob.exc.Forbidden) class defined as the following one:

from webob.exc import HTTPForbidden

try:
  from string import Template
except ImportError:
  from webob.util.stringtemplate import Template

class HTTPMyAppError(HTTPForbidden):
  body_template_obj = Template('''<div>${detail}</div>''')

  def __init__(self, msg):
    super(HTTPMyAppError, self).__init__(msg)

This by itself doesn’t change a lot as you will get the same ugly page with simply a div around your error. But now by using BeautifoulSoup you are able to get only your message from the original_response inside your ErrorController.document action.

if resp.status_int == 403: title = "Application Error" message = "We can't perform this, reason was:" details = str(BeautifulSoup(resp.body).find('div'))

Simply personalize your ErrorController.document template and display your details somewhere and you will be able to report errors to your users by simply doing something like raise HTTPMyAppError(‘You have already used this registration code’)

I know that this isn’t a really great solution as you have to parse your already generated error page to fetch only the error message and generate a new error page, if anyone has a better solution that permits to directly access the exception instance feel free to tell me!

4 thoughts on “Personalize your Error pages in Turbogears 2

  1. i've come up with a slightly different approach: using a custom HTTP header

    response.headers['X-message']

    which can then be retrieved from the response object in the error controller.

  2. That is also a very good solution, we ended in using the headers to report “login required” errors from controller methods that have be used as API and so should not redirect the user to the login page.

    We set a API = YES header and inside the ErrorController we check for that.

Leave a Reply

Your email address will not be published. Required fields are marked *