Application Hooks¶
The FrescoApp
object implements various hooks called
at key points of the request cycle,
into which you may insert your own code.
process_request¶
This hook is called at the start of request processing, before any routing has taken place.
app = FrescoApp()
@app.process_request
def ensure_ssl(request):
if not request.is_secure():
return Response.redirect(request.make_url(scheme='https')
Functions are called with one argument, request
,
and may return a
Response
object, in which case
this replaces the usual response
and the request cycle is short circuited as far as the process_response
hook.
Hook functions are run in the order they were registered. If multiple hook functions return a response value, then the last function to be registered wins.
process_request_once¶
This is a special case of process_request
. Any function decorated with
process_request_once
will only be called once,
for the first request handled by that worker. Note that if you have multiple
workers then the function will be called multiple times.
process_view¶
This hook is called after routing but before the view function is called.
Functions are called with the arguments
request
, view
, view_args
, view_kwargs
.
Note that the view
argument is the view function as passed to
@app.route
or to the constructor of Route
.
Because the Route
object may be configured to
wrap or filter the view
function’s return value,
you should not rely on being able to
call view
directly to generate the same
Response
object as it would in normal operation.
If required you can access the fully wrapped view
calling context.route_traversal.route.getdecoratedview()
.
If a Response
object is returned
the original view is not called, and the request cycle is short circuited as
far as the process_response
hook:
app = FrescoApp()
@app.route('/fish', GET)
def view_fish():
return Response([b'fish'])
@app.process_view
def replace_fish(request, view, view_args, view_kwargs):
if view is view_fish:
return Response([b'chips'])
Otherwise the return value will be used to replace the normal view function:
@app.route('/pie', GET, filters=[piecrust])
def pie():
return 'apple'
def piecrust(filling):
return Response([b'A tasty {} pie'.format(filling)]
@app.process_view
def replace_pie(request, view, view_args, view_kwargs):
def better_pie(*args, **kwargs):
return view(*args, **kwargs) + ' and blackberry'
return better_pie
If multiple hook functions return a value
the last function to be registered wins.
process_view
hooks are run in the order they were registered.
process_response¶
This is called before the response is output to the browser.
app = FrescoApp()
@app.process_response
def enable_cors(request, response):
return response.add_header('Access-Control-Allow-Origin', '*')
Functions are called with arguments request
, response
and may return a
Response
object, in which case
that value is returned, replacing the original response.
If multiple hook functions return a value
the last function to be registered wins.
process_exception¶
The process_exception
hook is called if a view function raises an exception
during execution.
app = FrescoApp()
@app.process_exception
def log_error(request, exc_info):
logger.error('Error in %s %s'.format(request.method, request.url),
exc_info=exc_info)
You may also associate functions with particular exceptions, eg:
@app.process_exception(DatabaseError)
def log_db_error(request, exc_info):
...
Functions are called with the arguments
request
, exc_info
and may return a
Response
object, in which case
that value is output to the browser.
process_exception
hooks are run in the order they were
registered.
If multiple hook functions return a value,
the first function to be registered wins.
process_http_error_response¶
This hook is called whenever an HTTP error response
(ie an HTTP status code in the range 400-599) is generated.
Functions are called with the arguments request
, response
.
app = FrescoApp()
@app.process_http_error_response
def show_error_page(request, response):
if response.status_code == 404:
return Response(content=render('404.html'), status='404 Not Found')
You may also associate functions with particular status codes, eg:
@app.process_http_error_response(418)
def error_418(request, response):
return Response(content=render('teapot.html'),
status="418 I'm a teapot")
A process_http_error_response hook function may return a
Response
object, in which case
that value is output to the browser.
If your application raises an exception and you have
a suitable process_http_error_response
handler in place
then Fresco will log the exception and generate a default 500 response,
which is then passed to your process_http_error_response
handler.
process_teardown¶
This hook is called at the very end of the request processing cycle
after the response has been sent to the browser. Functions are called with
a single argument, request
:
@app.process_teardown
def close_db_conn(request):
...
process_teardown
hook functions should not return any values.
Error handling¶
If a view raises an exception other than a
ResponseException
:
The exception is logged (to
fresco.core.FrescoApp.logger
)If no
process_exception
orprocess_http_error_response
handlers are installed, fresco re-raises the exception and the request is not further processed by fresco.Otherwise, all
process_exception
handlers are called in order.Any handler may return a
Response
object. If multiple handlers return responses then the last registered handler wins.If a handler returns instead an
exc_info
tuple, the exception be reraised and fresco will not continue further processing of the request. (This is intended to allowprocess_exception
handlers to reraise the original exception)If an exception is raised inside a handler that exception is logged and processing continues with the next handler.
If no Response was generated by the
process_exception
handlers, a500 Internal Server Error
response is generated.If the final response generated has an error status code (eg 400-599) then this response is passed to any
process_http_error_response
handlers installed.
Exception handling in WSGI Middleware¶
Some WSGI middleware may rely catching exceptions to provide functionality. For example:
Authorization WSGI middleware may expect the WSGI to raise “Unauthorized” exceptions, which are then converted by the middleware a suitable response (401 error, redirect to login page etc). repoze.who is an example of middleware that does this.
Debugging middleware may expect to catch exceptions in order to show a full traceback and debugging tools. An example of this is the werkzeug.debug.DebuggedApplication middleware.
In these cases you can either:
Avoid installing any
process_exception
orprocess_http_error_response
handlers, causing fresco to disable its internal error handling.Install a
process_exception
handler similar to the following which will cause exceptions to be reraised:
@app.process_exception
def reraise_exceptions(request, exc_info):
return exc_info