Post-processing
Post-processing middlewares are invoked after the request handler.
They are suitable for modifying the response and/or performing side-effects based on its contents.
E.g. logging the response's status code, injecting response headers, etc.
use pavex::{response::Response};
use pavex_tracing::{
RootSpan,
fields::{http_response_status_code, HTTP_RESPONSE_STATUS_CODE}
};
pub fn response_logger(response: Response, root_span: &RootSpan) -> Response
{
root_span.record(
HTTP_RESPONSE_STATUS_CODE,
http_response_status_code(&response),
);
response
}
Registration
You register a post-processing middleware against a blueprint via the post_process
method.
use pavex::blueprint::router::GET;
use pavex::blueprint::Blueprint;
use pavex::f;
pub fn blueprint() -> Blueprint {
let mut bp = Blueprint::new();
bp.post_process(f!(super::response_logger));
bp.route(GET, "/", f!(super::handler));
bp
}
You must provide an unambiguous path to the middleware, wrapped in the f!
macro.
The middleware will be invoked for all request handlers registered after it, as long as they were registered against the same Blueprint
or one of its nested children.
Check out the scoping section for more details.
Registration syntax
You can use free functions, static methods, non-static methods, and trait methods as middlewares. Check out the dependency injection cookbook for more details on the syntax for each case.
IntoResponse
Post-processing middlewares, like request handlers, must return a type that can be converted into a Response
via the
IntoResponse
trait.
If you want to return a custom type from your middleware, you must implement IntoResponse
for it.
Middlewares can fail
Post-processing middlewares can be fallible, i.e. they can return a Result
.
use pavex::response::Response;
use super::CompressionError;
pub fn compress(response: Response) -> Result<Response, CompressionError>
{
let compressed = {
// Try to compress the response
// [...]
}?;
Ok(compressed)
}
If they do, you must specify an error handler when registering them:
use pavex::blueprint::router::GET;
use pavex::blueprint::Blueprint;
use pavex::f;
pub fn blueprint() -> Blueprint {
let mut bp = Blueprint::new();
bp.post_process(f!(super::compress))
.error_handler(f!(super::compression_error_handler));
// [...]
bp
}
Check out the error handling guide for more details.
Dependency injection
Post-processing middlewares can take advantage of dependency injection.
You must specify the dependencies of your middleware as input parameters in its function signature.
Those inputs are going to be built and injected by the framework, according to the constructors you have registered.
Post-processing middlewares, like request handlers and pre-processing middlewares,
can mutate request-scoped types.
Ask for a &mut
reference to the type you want to mutate as an input parameter, the framework will take care of the rest.
Check out the dependency injection guide for more details
on how the process works.
Check out the request data guide for an overview of the data you can extract from the request
using Pavex's first-party extractors.