-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add middleware::from_fn
for creating middleware from async fns
#656
Conversation
pub struct Next<ReqBody> { | ||
inner: BoxCloneService<Request<ReqBody>, Response, Infallible>, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also considered calling this Rest
but went with Next
since its more common across other frameworks such as tide.
Also worth noting that for simple middleware tower's
|
Has implications for backpressure and stuff
where | ||
F: FnMut(Request<ReqBody>, Next<ReqBody>) -> Fut, | ||
Fut: Future<Output = Out>, | ||
Out: IntoResponse, | ||
S: Service<Request<ReqBody>, Response = Response<ResBody>, Error = Infallible> | ||
+ Clone | ||
+ Send | ||
+ 'static, | ||
S::Future: Send + 'static, | ||
ResBody: HttpBody<Data = Bytes> + Send + 'static, | ||
ResBody::Error: Into<BoxError>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's quite a long list of constraints ^^
I wonder what cursed middleware one could write that actually makes use of the extra capabilities FnMut
gives in addition to "plain" Fn
😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hehe yeah 😜 I'm not sure FnMut
is that useful in practice actually but since we a &mut self
I figure why not. Tower also uses FnMut
.
Probably a naive question, but how would one pass some context variables into the middleware function? I've tried wrapping the middleware itself into a "factory function", but failed to do so because of the pub fn auth(some_context_variable: Context) -> impl Fn(Request<_>, Next<_>) -> impl IntoResponse {
|req: Request<_>, next: Next: <_>| async move {
// do something based on `some_context_variable`
Ok(next.run(req).await)
}
} |
@johannescpk You can do this: use axum::{http::Request, response::IntoResponse, Router};
use axum_extra::middleware::{self, Next};
use std::net::SocketAddr;
#[derive(Clone)]
struct Context {}
#[tokio::main]
async fn main() {
let ctx = Context {};
let app = Router::new().layer(middleware::from_fn(move |req, next| {
foo(ctx.clone(), req, next)
}));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
async fn foo<B>(ctx: Context, req: Request<B>, next: Next<B>) -> impl IntoResponse {
next.run(req).await
} |
I feel axum is in a pretty good place in terms of ease of use. However one area that is still a big hurdle for some people is
tower::Service
which quickly comes up up when writing custom middleware. Suddenly you cannot use high levelasync
/await
syntax anymore and have to worry about pinning,poll_ready
, and most likely lots of generics.This is something I've been thinking about for awhile but hadn't found a good solution for. But today I discovered poem's
middleware_fn
which I think is perfect for this!So this PR introduces
axum_extra::middleware::from_fn
that works like this:You have pretty much the same level of control as with
tower::Service
but with a higher level API.