Post-processing
Post-processing middlewares are invoked after the route handler.
They can modify the response and/or perform side-effects based on its contents.
E.g. logging the response's status code, injecting response headers, etc.
Defining post-processing middlewares
Use the #[post_process] attribute to define a new post-processing middleware:
use pavex::Response;
use pavex::post_process;
use pavex_tracing::{
RootSpan,
fields::{HTTP_RESPONSE_STATUS_CODE, http_response_status_code},
};
#[post_process]
pub fn response_logger(response: Response, root_span: &RootSpan) -> Response {
root_span.record(
HTTP_RESPONSE_STATUS_CODE,
http_response_status_code(&response),
);
response
}
Signature
Pavex accepts a wide range of function signatures for post-processing middlewares. There are two constraints:
Responsemust be one of their input parameters.- Their return type must be one of the following:
- A type that implements the
IntoResponsetrait, or Result<T, E>, whereTimplementsIntoResponse.
Other than that, you have a lot of freedom in how you define your post-processing middlewares:
- They can be free functions or methods.
- They can be synchronous or asynchronous.
- They can take additional input parameters, leaning on Pavex's dependency injection system.
Registration
Invoke Blueprint::post_process to register a post-processing middleware:
use crate::logger::RESPONSE_LOGGER;
use pavex::{Blueprint, blueprint::from};
pub fn blueprint() -> Blueprint {
let mut bp = Blueprint::new();
bp.post_process(RESPONSE_LOGGER); // (1)!
// [...]
}
RESPONSE_LOGGERis a strongly-typed constant generated by the#[post_process]attribute on theresponse_loggerfunction.
Check out the documentation on component ids for more details.
The middleware will be invoked for all routes registered after it. Check out the scoping section for more details.
Middlewares can fail
Post-processing middlewares can return a Result.
use crate::errors::CompressionError;
use pavex::Response;
use pavex::post_process;
#[post_process]
pub fn compress(response: Response) -> Result<Response, CompressionError> {
let compressed = {
// Try to compress the response
// [...]
}?;
Ok(compressed)
}
Check out the error handling guide for more details on how to handle the error case.
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.