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_exceptionorprocess_http_error_responsehandlers are installed, fresco re-raises the exception and the request is not further processed by fresco.Otherwise, all
process_exceptionhandlers are called in order.Any handler may return a
Responseobject. If multiple handlers return responses then the last registered handler wins.If a handler returns instead an
exc_infotuple, the exception be reraised and fresco will not continue further processing of the request. (This is intended to allowprocess_exceptionhandlers 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_exceptionhandlers, a500 Internal Server Errorresponse 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_responsehandlers 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_exceptionorprocess_http_error_responsehandlers, causing fresco to disable its internal error handling.Install a
process_exceptionhandler similar to the following which will cause exceptions to be reraised:
@app.process_exception
def reraise_exceptions(request, exc_info):
return exc_info