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:

  1. The exception is logged (to fresco.core.FrescoApp.logger)

  2. If no process_exception or process_http_error_response handlers are installed, fresco re-raises the exception and the request is not further processed by fresco.

  3. 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 allow process_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.

  4. If no Response was generated by the process_exception handlers, a 500 Internal Server Error response is generated.

  5. 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 or process_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