271 lines
10 KiB
ReStructuredText
271 lines
10 KiB
ReStructuredText
.. _hooks:
|
|
|
|
Hooks
|
|
=====
|
|
Pecan Hooks are a nice way to interact with the framework itself without having to
|
|
write WSGI middleware.
|
|
|
|
There is nothing wrong with WSGI Middleware, and actually, it is really easy to
|
|
use middleware with Pecan, but it can be hard (sometimes impossible) to have
|
|
access to Pecan's internals from within middleware. Hooks make this easier.
|
|
|
|
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 controller
|
|
|
|
* ``before``: called after routing, but before controller code is run
|
|
|
|
* ``after``: called after controller code has been run
|
|
|
|
* ``on_error``: called when a request generates an exception
|
|
|
|
Implementation
|
|
--------------
|
|
In the below example, we will write a simple hook that 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 about the request, such as the request and response object, and which controller
|
|
was chosen by Pecan's routing.
|
|
|
|
``on_error`` is passed a shared state object **and** the original exception.
|
|
|
|
Attaching Hooks
|
|
--------------
|
|
Hooks can be attached in a project-wide manner by specifying a list of hooks
|
|
in your project's ``app.py`` file::
|
|
|
|
from application.root import RootController
|
|
from my_hooks import SimpleHook
|
|
|
|
app = make_app(
|
|
RootController(),
|
|
hooks = [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 my_hooks import SimpleHook
|
|
|
|
class SimpleController(object):
|
|
|
|
__hooks__ = [SimpleHook()]
|
|
|
|
@expose('json')
|
|
def index(self):
|
|
print "DO SOMETHING!"
|
|
return dict()
|
|
|
|
Now that our ``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
|
|
|
|
|
|
Included Pecan Hooks
|
|
--------------------
|
|
Pecan includes some hooks in its core and are very simple to start using them
|
|
away. This section will describe their different uses, how to configure them
|
|
and examples of common scenarios.
|
|
|
|
.. _requestviewerhook:
|
|
|
|
RequestViewerHook
|
|
-----------------
|
|
This hooks is very 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 on if they are not configured.
|
|
|
|
For the actual object reference, please see :ref:`pecan_hooks`.
|
|
|
|
Automatically Enabled
|
|
---------------------
|
|
This hook can be automatically added to the application itself if a certain key
|
|
exists in the configuration used for the app. This key is::
|
|
|
|
requestviewer
|
|
|
|
It does not need to contain anything (could be an empty dictionary), and this
|
|
is enough to force Pecan to load this hook when the WSGI application is
|
|
created.
|
|
|
|
Configuration
|
|
-------------
|
|
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.
|
|
|
|
No 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 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 properly
|
|
gathered and returned via `stdout`.
|
|
|
|
And this is how those same values would be seen in the response headers::
|
|
|
|
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 hook can be configured via a dictionary (or Config object from Pecan) when
|
|
adding it to the application or via the `requestviewer` key in the actual
|
|
configuration being passed to the application.
|
|
|
|
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 when using `make_app`
|
|
(shortened for brevity)::
|
|
|
|
...
|
|
hooks = [
|
|
RequestViewerHook({'items':['path']})
|
|
]
|
|
|
|
And the same configuration could be set in the config file like::
|
|
|
|
requestviewer = {'items:['path']}
|
|
|
|
Specifying items
|
|
----------------
|
|
Items are the actual information objects that the hook will use to return
|
|
information. 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.
|
|
|
|
Remember, 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 make_tempfile
|
|
accept_charset max_forwards
|
|
accept_encoding method
|
|
accept_language params
|
|
application_url path
|
|
as_string path_info
|
|
authorization path_info_peek
|
|
blank path_info_pop
|
|
body path_qs
|
|
body_file path_url
|
|
body_file_raw postvars
|
|
body_file_seekable pragma
|
|
cache_control query_string
|
|
call_application queryvars
|
|
charset range
|
|
content_length referer
|
|
content_type referrer
|
|
cookies relative_url
|
|
copy remote_addr
|
|
copy_body remote_user
|
|
copy_get remove_conditional_headers
|
|
date request_body_tempfile_limit
|
|
decode_param_names scheme
|
|
environ script_name
|
|
from_file server_name
|
|
from_string server_port
|
|
get_response str_GET
|
|
headers str_POST
|
|
host str_cookies
|
|
host_url str_params
|
|
http_version str_postvars
|
|
if_match str_queryvars
|
|
if_modified_since unicode_errors
|
|
if_none_match upath_info
|
|
if_range url
|
|
if_unmodified_since urlargs
|
|
is_body_readable urlvars
|
|
is_body_seekable uscript_name
|
|
is_xhr user_agent
|
|
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
|
|
------------
|
|
Sometimes it's annoying to get information about *every* single request. For this
|
|
purpose, there is a matching list of url paths that you can pass into the hook
|
|
so that paths that do not match are returned.
|
|
|
|
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).
|