Skip to main content

IPipelineBehavior

Trait IPipelineBehavior 

Source
pub trait IPipelineBehavior: Send + Sync {
    // Required method
    fn handle<'life0, 'async_trait>(
        &'life0 self,
        req: Box<dyn Any + Send>,
        next: Box<dyn FnOnce(Box<dyn Any + Send>) -> Pin<Box<dyn Future<Output = Result<Box<dyn Any + Send>, Error>> + Send>> + Send>,
    ) -> Pin<Box<dyn Future<Output = Result<Box<dyn Any + Send>, Error>> + Send + 'async_trait>>
       where 'life0: 'async_trait,
             Self: 'async_trait;
}
Expand description

Pipeline behavior that wraps around request handling.

Multiple behaviors form a chain. Each behavior can:

  • Inspect or modify the request before passing it on
  • Inspect or modify the response after it returns
  • Short-circuit and skip the rest of the chain (by not calling next)

Analogous to MediatR’s IPipelineBehavior<TRequest, TResponse>.

§Lifetime and dependencies

Behaviors are registered as singletons: one instance per host, shared by every request. The DI resolver is available only while the behavior is being constructed — use IServiceCollectionExt::add_pipeline_with and take your dependencies in the factory closure:

ⓘ
svc.add_pipeline_with(|resolver| {
    // `&dyn IServiceResolver` is object-safe: resolve type-erased.
    let clock: Arc<Clock> = resolver
        .get_any(std::any::type_name::<Clock>())
        .and_then(|service| service.downcast::<Arc<Clock>>().ok())
        .map(|service| Arc::clone(&*service))
        .expect("register Clock before the pipeline");
    Arc::new(TimingBehavior { clock }) as Arc<dyn IPipelineBehavior>
})

Inside IPipelineBehavior::handle there is no resolver and no typed request/response: the request and the eventual response are Box<dyn Any + Send>. Cross-cutting mechanics (timing, logging, cancellation) belong here; business rules that need the typed request belong in the handler or a use case it calls. IServiceCollectionExt::add_pipeline remains the zero-dependency convenience registration for Default behaviors.

§Response type contract

A behavior that short-circuits must return the same response type the handler would return (downcast to it happens after the chain, before the caller sees the value). Short-circuiting with a different type — for example Box::new(()) — fails with an explicit type-mismatch error instead of silently succeeding.

Required Methods§

Source

fn handle<'life0, 'async_trait>( &'life0 self, req: Box<dyn Any + Send>, next: Box<dyn FnOnce(Box<dyn Any + Send>) -> Pin<Box<dyn Future<Output = Result<Box<dyn Any + Send>, Error>> + Send>> + Send>, ) -> Pin<Box<dyn Future<Output = Result<Box<dyn Any + Send>, Error>> + Send + 'async_trait>>
where 'life0: 'async_trait, Self: 'async_trait,

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§