tower-rs / tower

async fn(Request) -> Result<Response, Error>
https://docs.rs/tower
MIT License
3.45k stars 272 forks source link

Feature Request: implement `layer` method on `ServiceExt` #766

Open tp971 opened 5 months ago

tp971 commented 5 months ago

I am currently working on a project where middleware is applied bottom-up, i.e. the middleware is appended after the services (which is also the case in axum for example, where middlewares are applied after the routes). I noticed that there is no way to add a layer to a service except calling Layer::layer or using the ServiceBuilder, both of which are top-down, i.e. the middlewares are added before the services. In my project, I'm currently using this helper trait:

pub trait ServiceHelper<Request>: Service<Request> {
    fn layer<L: Layer<Self>>(self, layer: L) -> L::Service
    where
        Self: Sized
    {
        layer.layer(self)
    }
}

impl<T: Service<Request> + ?Sized, Request> ServiceHelper<Request> for T {}

This allows you to apply middleware like this:

some_service.layer(layer_c).layer(layer_b).layer(layer_a)

I see no reason to not include this in tower::ServiceExt, so I'm filing this issue as a feature request.

GlenDC commented 5 months ago

I’m not against it, but I cannot think of a situation where I wanted it bottom up. I can see the appeal for it. As a community member I’m not against it, but neither in particular need or desire for it.

tobz commented 1 month ago

Yeah, it'd be good to hear some rationale on this.

The main thing I can think of is that it lets you avoid needing to reference ServiceBuilder at the callsite of where you're layering... but you're still either importing the helper trait or importing ServiceBuilder. The resulting layering of the middleware is identical either way.