9.4 KiB
Pecan Hooks
Although it is easy to use WSGI middleware with Pecan, it can be hard (sometimes impossible) to have access to Pecan's internals from within middleware. Pecan Hooks are a way to interact with the framework, without having to write separate middleware.
Hooks allow you to execute code at key points throughout the life cycle of your request:
on_route
: called before Pecan attempts to route a request to a controllerbefore
: called after routing, but before controller code is runafter
: called after controller code has been runon_error
: called when a request generates an exception
Implementating a Pecan Hook
In the below example, a simple hook will gather some information
about the request and print it to stdout
.
Your hook implementation needs to import PecanHook
so it can be used
as a base class. From there, you'll need to override the on_route
, before
, after
, or on_error
methods.
from pecan.hooks import PecanHook
class SimpleHook(PecanHook):
def before(self, state):
print "\nabout to enter the controller..."
def after(self, state):
print "\nmethod: \t %s" % state.request.method
print "\nresponse: \t %s" % state.response.status
on_route
, before
, and after
are each passed a
shared state object which includes useful information, such as the
request and response objects, and which controller was selected by
Pecan's routing.
on_error
is passed
a shared state object and the original exception. If an
on_error
handler
returns a Response object, this response will be returned to the end
user and no furthur on_error
hooks will be executed.
Attaching Hooks
Hooks can be attached in a project-wide manner by specifying a list of hooks in your project's configuration file.
app = {
'root' : '...'
# ...
'hooks': lambda: [SimpleHook()]
}
Hooks can also be applied selectively to controllers and their
sub-controllers using the __hooks__
attribute on one or more controllers.
from pecan import expose
from pecan.hooks import HookController
from my_hooks import SimpleHook
class SimpleController(HookController):
__hooks__ = [SimpleHook()]
@expose('json')
def index(self):
print "DO SOMETHING!"
return dict()
Now that SimpleHook
is included, let's see what happens when
we run the app and browse the application from our web browser.
pecan serve config.py
serving on 0.0.0.0:8080 view at http://127.0.0.1:8080
about to enter the controller...
DO SOMETHING!
method: GET
response: 200 OK
Hooks That Come with Pecan
Pecan includes some hooks in its core. This section will describe their different uses, how to configure them, and examples of common scenarios.
RequestViewerHook
This hook is useful for debugging purposes. It has access to every
attribute the response
object has plus a few others that
are specific to the framework.
There are two main ways that this hook can provide information about a request:
- Terminal or logging output (via an file-like stream like
stdout
) - Custom header keys in the actual response.
By default, both outputs are enabled.
pecan_hooks
Configuring RequestViewerHook
There are a few ways to get this hook properly configured and running. However, it is useful to know that no actual configuration is needed to have it up and running.
By default it will output information about these items:
- path : Displays the url that was used to generate this response
- status : The response from the server (e.g. '200 OK')
- method : The method for the request (e.g. 'GET', 'POST', 'PUT or 'DELETE')
- controller : The actual controller method in Pecan responsible for the response
- params : A list of tuples for the params passed in at request time
- hooks : Any hooks that are used in the app will be listed here.
The default configuration will show those values in the terminal via
stdout
and it will also add them to the response headers
(in the form of X-Pecan-item_name
).
This is how the terminal output might look for a /favicon.ico request:
path - /favicon.ico
status - 404 Not Found
method - GET
controller - The resource could not be found.
params - []
hooks - ['RequestViewerHook']
In the above case, the file was not found, and the information was printed to stdout. Additionally, the following headers would be present in the HTTP response:
X-Pecan-path /favicon.ico
X-Pecan-status 404 Not Found
X-Pecan-method GET
X-Pecan-controller The resource could not be found.
X-Pecan-params []
X-Pecan-hooks ['RequestViewerHook']
The configuration dictionary is flexible (none of the keys are
required) and can hold two keys: items
and
blacklist
.
This is how the hook would look if configured directly (shortened for brevity):
...
'hooks': lambda: [
RequestViewerHook({'items':['path']})
]
Modifying Output Format
The items
list specify the information that the hook
will return. Sometimes you will need a specific piece of information or
a certain bunch of them according to the development need so the
defaults will need to be changed and a list of items specified.
Note
When specifying a list of items, this list overrides completely the defaults, so if a single item is listed, only that item will be returned by the hook.
The hook has access to every single attribute the request object has and not only to the default ones that are displayed, so you can fine tune the information displayed.
These is a list containing all the possible attributes the hook has access to (directly from webob):
====================== | ========================== |
accept |
|
accept_charset |
|
accept_encoding |
|
accept_language |
|
application_url |
|
as_string |
|
authorization |
|
blank |
|
body |
|
body_file |
|
body_file_raw |
|
body_file_seekable |
|
cache_control |
|
call_application |
|
charset |
|
content_length |
|
content_type |
|
cookies |
|
copy |
|
copy_body |
|
copy_get |
|
date |
|
decode_param_names |
|
environ |
|
from_file |
|
from_string |
|
get_response |
|
headers |
|
host |
|
host_url |
|
http_version |
|
if_match |
|
if_modified_since |
|
if_none_match |
|
if_range |
|
if_unmodified_since |
|
is_body_readable |
|
is_body_seekable |
|
is_xhr make_body_seekable |
|
And these are the specific ones from Pecan and the hook:
- controller
- hooks
- params (params is actually available from webob but it is parsed by the hook for redability)
Blacklisting Certain Paths
Sometimes it's annoying to get information about every
single request. To limit the ouptput, pass the list of URL paths for
which you do not want data as the blacklist
.
The matching is done at the start of the URL path, so be careful when using this feature. For example, if you pass a configuration like this one:
{ 'blacklist': ['/f'] }
It would not show any url that starts with f
,
effectively behaving like a globbing regular expression (but not quite
as powerful).
For any number of blocking you may need, just add as many items as wanted:
{ 'blacklist' : ['/favicon.ico', '/javascript', '/images'] }
Again, the blacklist
key can be used along with the
items
key or not (it is not required).