Note
The module code listed below is also attached to this page and can be downloaded here: httpmethodrouter.py
The decorator included below enables TurboGears applications to automatically route requests to functions based on the HTTP method type (GET, POST, PUT, and DELETE).
Here is a quick synopsis of how you would use the code below in your controller class:
from turbogears import controllers
from httpmethodrouter import *
class MyController(controllers.Controller):
@http_method_router()
def edit(self, *p, **kwp):
pass
def edit_GET(self, *p, **kwp):
"""Handles all edit GET requests."""
def edit_POST(self, *p, **kwp):
"""Handles all edit POST requests."""
def edit_PUT(self, *p, **kwp):
"""Handles all edit PUT requests."""
def edit_DELETE(self, *p, **kwp):
"""Handles all edit DELETE requests."""
Please see the code below for more information about the http_method_router decorator.
# -*- coding: UTF-8 -*-
import cherrypy
from turbogears.decorator import decorator
__all__ = ['http_method_router']
def http_method_router(default=None, alt_mappings=None):
"""Routes requests based on the http request type (GET, POST, PUT, DELETE).
By default, the http_method_router will attempt to forward the request
to the corresponding ``<function_name>_<http_method>`` function.
For example, if a function named "edit" is decorated with the
``http_method_router``, then all GET requests will be routed to the
``edit_GET`` function, all POST requests to the ``edit_POST`` function, etc.
If a function cannot be matched to the request, then the router will
first check for a value in the default parameter. If this is a valid
value, then the corresponding function will be called. If no default value
is specified, then an attempt will be made to route the request to the
``<function_name>_GET`` function. If that function does not exist, then a
``cherrypy.NotFound`` exception will be raised.
If you do not want to use the default ``<function_name>_<http_method>``
behavior, then alternate mappings can be specified by passing in a
dict of ``<http_method>`` to ``function_name`` values in the
``alt_mappings`` parameter.
See the examples below for more information.
Parameters
----------
``default:``
The full name of the default function to call
if a match cannot be found. If left empty,
the ``<func_name>_GET`` function will be called.
``alt_mappings:``
A dictionary of alternate http method->function
name mappings. The keys for the dictionary
should be the relevant http method strings
(GET, POST, PUT, DELETE).
Example 1: Default Behavior
---------------------------
The example below works as follows:
1. The target method, ``edit``, is decorated with the ``http_method_router``
decorator.
2. When ``edit`` is called, it will route all GET requests to ``edit_GET``,
and all POST requests to ``edit_POST``.
3. If an unmapped method is used (PUT or DELETE in this case), then
``edit_GET`` will be called by default. To change the default behavior,
add the ``default`` parameter to the decorator::
@http_method_router(default='some_other_method_name')
Sample Code 1
~~~~~~~~~~~~~
::
@turbogears.expose()
@http_method_router()
def edit(self, *args, **kwargs):
pass
def edit_GET(self, *args, **kwargs):
pass
def edit_POST(self, *args, **kwargs):
pass
Example 2: Alternate mappings
-----------------------------
The example below works as follows:
1. The ``alt_mappings`` parameter is specified with the mappings for
the GET and POST requests.
2. All ``edit GET`` requests are routed to the ``show`` function, and
all ``edit POST`` requests are routed to the ``update`` function.
3. In this case, since no other mappings are declared, and no
default method was specified, all PUT and DELETE requests
will cause a ``cherrypy.NotFound`` exception to be raised.
Sample Code 2
~~~~~~~~~~~~~
::
@turbogears.expose()
@http_method_router(alt_mappings=dict(GET='show', POST='update'))
def edit(self, *args, **kwargs):
pass
def show(self, *args, **kwargs):
pass
def update(self, *args, **kwargs):
pass
"""
def entangle(fn):
def method_router(func, self, *args, **kwargs):
if callable(func):
name = func.__name__
method = cherrypy.request.method
target = name + "_" + method
alternate = None
if isinstance(alt_mappings, dict) and \
alt_mappings.has_key(method):
alternate = alt_mappings[method]
if(hasattr(self, alternate) and
callable(self.__getattribute__(alternate))):
return self.__getattribute__(alternate)(*args, **kwargs)
if hasattr(self, target) and \
callable(self.__getattribute__(target)):
return self.__getattribute__(target)(*args, **kwargs)
elif(default and
hasattr(self, default) and
callable(self.__getattribute__(default))):
return self.__getattribute__(default)(*args, **kwargs)
elif hasattr(self, name + "_GET"):
return self.__getattribute__(name+"_GET")(*args, **kwargs)
else:
raise cherrypy.NotFound()
else:
raise cherrypy.NotFound()
return decorator(method_router)(fn)
return entangle