tower-web
tower-web copied to clipboard
What is the recommended way of conditionally adding middleware?
I'd like to write this code:
let builder = ServiceBuilder::new()
.resource(Index::new(config.root.clone()))
.resource(Assets::new(config.root))
.resource(SandboxFixme)
.resource(Meta::default())
.resource(Gist::new(config.gh_token));
if config.cors_enabled {
let cors = CorsBuilder::new()
.allow_origins(AllowedOrigins::Any { allow_null: true })
.allow_headers(vec![header::CONTENT_TYPE])
.allow_methods(vec![Method::GET, Method::POST])
.allow_credentials(false)
.max_age(Duration::from_secs(ONE_HOUR_IN_SECONDS as u64))
.prefer_wildcard(true)
.build();
builder = builder.middleware(cors);
}
But it results in
error[E0308]: mismatched types
--> src/tower_web_server.rs:373:19
|
373 | builder = builder.middleware(cors);
| ^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `tower_web::middleware::Identity`, found struct `tower_web::middleware::Chain`
|
= note: expected type `tower_web::ServiceBuilder<_, _, tower_web::middleware::Identity>`
found type `tower_web::ServiceBuilder<_, _, tower_web::middleware::Chain<tower_web::middleware::Identity, tower_web::middleware::cors::CorsMiddleware>>`
I see a few avenues:
-
Make each middleware have an internal on/off switch. For CORS, this is "easy" as the restrictive settings should be as good as off. Every middleware would have to remember to add this, all would probably be named differently, it's not as "blatantly correct" as never inserting it, etc.
-
Implement
MiddlewareforBox<Middleware>and add some type erasure. -
Implement
MiddlewareforEither<L, R>and then doEither<Cors, Identity>
I think the best route forward is going to be something like 1. (passing in a type that conditionally enables or disables the middleware).
The problem is that the code you wrote feels natural and the fact that it does not work is frustrating.
passing in a type that conditionally enables or disables the middleware
Heh, this makes me think I should add MaybeMiddleware(M) so that all middleware can use the same implementation. Essentially a newtype on 3...
passing in a type that conditionally enables or disables the middleware
Actually, why do you think this is the best path? Doesn't doing this force the decision to be a runtime switch instead of potentially optimizing to a compile-time switch?
What would the compile time switch look like?
What would the compile time switch look like?
I guess I was thinking something like
#[cfg(feature = "cors")]
fn enable_cors() -> impl Middleware { ... } // cors
#[cfg(not(feature = "cors"))]
fn enable_cors() -> impl Middleware { ... } // identity
Which I suppose is still possible...
That can work.
I have the impression that it will be common to want to enable / disable middleware based on a config file or whatever, so there should be some good API for that.