summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Petrello <lists@ryanpetrello.com>2012-03-29 10:07:08 -0700
committerRyan Petrello <lists@ryanpetrello.com>2012-03-29 10:07:08 -0700
commit0a3f41ccddd3c506127bcc1cf241196f5cbea6fe (patch)
tree2544e88d458b66e06e82f515ca28246a7615be8c
parenta7d4684ac5dc704b3fe8320618e71d462ee1209e (diff)
parent00360001b19999cb491f10049ce6e0d63e3b59cf (diff)
downloadpecan-0a3f41ccddd3c506127bcc1cf241196f5cbea6fe.tar.gz
Merge pull request #120 from coderpete/next
Custom Error Docs
-rw-r--r--docs/source/errors.rst113
-rw-r--r--pecan/middleware/errordocument.py11
-rw-r--r--pecan/scaffolds/base/config.py_tmpl2
3 files changed, 122 insertions, 4 deletions
diff --git a/docs/source/errors.rst b/docs/source/errors.rst
index 01f2a87..afb4a9b 100644
--- a/docs/source/errors.rst
+++ b/docs/source/errors.rst
@@ -2,4 +2,115 @@
Custom Error Documents
======================
-TODO
+In this article we will configure a Pecan application to display a custom
+Error page whenever the server returns ``404 Page not Found`` status.
+
+This article assumes that you have already created a test application as
+described in :ref:`quick_start`
+
+.. note::
+ While this example focuses on the ``HTTP 404 status`` message, the same
+ technique may be applied to define custom actions for any of the ``HTTP``
+ status response codes in the 400 and 500 range. You are well advised to use
+ this power judiciously.
+
+.. _overview:
+
+Overview
+--------
+
+Pecan makes it simple to customize error documents in two simple steps:
+
+ * :ref:`configure` of the HTTP status messages you want to handle
+ in your application's config.py
+ * :ref:`controllers` to handle the status messages you have configured
+
+.. _configure:
+
+Configure Routing
+-----------------
+Let's configure our application *test_project* to route the ``HTTP 404``
+(page not found) messages to our custom built controller.
+
+First, we tweak test_project/config.py::
+
+ # Pecan Application Configurations
+ app = {
+ 'root' : 'test_project.controllers.root.RootController',
+ 'modules' : ['test_project'],
+ 'static_root' : '%(confdir)s/public',
+ 'template_path' : '%(confdir)s/test_project/templates',
+ 'reload' : True,
+ 'debug' : True,
+
+ ## modify the 'errors' element to direct HTTP status codes to your
+ ## own controller.
+ 'errors' : {
+ #404 : '/error/404',
+ 404 : '/notfound',
+ '__force_dict__' : True
+ }
+ }
+
+Instead of the default error page, Pecan will now route 404 messages to our
+very own controller named *notfound*.
+
+Let us now implement the *notfound* Controller
+
+.. _controllers:
+
+Write Custom Controllers
+------------------------
+The easiest way to implement our custom *notfound* error controller is to
+add it to ``test_project.root.RootController`` class
+(typically in test_project/controllers/root.py)::
+
+ from pecan import expose
+ from webob.exc import status_map
+
+
+ class RootController(object):
+
+ @expose(generic=True, template='index.html')
+ def index(self):
+ return dict()
+
+ @index.when(method='POST')
+ def index_post(self, q):
+ redirect('http://pecan.readthedocs.org/en/latest/search.html?q=%s' % q)
+
+
+ ## custom handling of '404 Page Not Found' messages
+ @expose('error.html')
+ def notfound(self):
+ return dict(status=404, message="test_project does not have this page")
+
+
+ @expose('error.html')
+ def error(self, status):
+ try:
+ status = int(status)
+ except ValueError:
+ status = 0
+ message = getattr(status_map.get(status), 'explanation', '')
+ return dict(status=status, message=message)
+
+
+And that's it!
+
+Notice that the only bit of code we added to our RootController is::
+
+ ## custom handling of '404 Page Not Found' messages
+ @expose('error.html')
+ def notfound(self):
+ return dict(status=404, message="test_project does not have this page")
+
+We simply ``@expose`` the ``notfound`` controller with the ``error.html``
+template (which was conveniently generated for us and placed under
+test_project/templates/ when we created ``test_project``). As with any common
+controller *@expose*'d through a template, we return a dictionary of variables
+for interpolation by the template renderer.
+
+Now we can modify the error template, or write a brand new one to make the 404
+error status page or ``test_project`` as pretty or fancy as we want.
+
diff --git a/pecan/middleware/errordocument.py b/pecan/middleware/errordocument.py
index 8a7aeab..22443cc 100644
--- a/pecan/middleware/errordocument.py
+++ b/pecan/middleware/errordocument.py
@@ -36,7 +36,11 @@ class StatusPersist(object):
class ErrorDocumentMiddleware(object):
-
+ '''
+ Intersects HTTP response status code, looks it up in the error map defined
+ in the Pecan app config.py, and routes to the controller assigned to that
+ status.
+ '''
def __init__(self, app, error_map):
self.app = app
self.error_map = error_map
@@ -44,6 +48,10 @@ class ErrorDocumentMiddleware(object):
def __call__(self, environ, start_response):
def replacement_start_response(status, headers, exc_info=None):
+ '''
+ Overrides the default response if the status is defined in the
+ Pecan app error map configuration.
+ '''
try:
status_code = int(status.split(' ')[0])
except (ValueError, TypeError): # pragma: nocover
@@ -60,7 +68,6 @@ class ErrorDocumentMiddleware(object):
self.error_map[status_code]
)
raise ForwardRequestException(factory=factory)
-
return start_response(status, headers, exc_info)
app_iter = self.app(environ, replacement_start_response)
diff --git a/pecan/scaffolds/base/config.py_tmpl b/pecan/scaffolds/base/config.py_tmpl
index b820e59..06507d9 100644
--- a/pecan/scaffolds/base/config.py_tmpl
+++ b/pecan/scaffolds/base/config.py_tmpl
@@ -12,7 +12,7 @@ app = {
'template_path': '%(confdir)s/${package}/templates',
'debug': True,
'errors': {
- '404': '/error/404',
+ 404: '/error/404',
'__force_dict__': True
}
}