-
Notifications
You must be signed in to change notification settings - Fork 1.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
Missing extensions are runtime errors rather than compile errors #1142
Comments
I have been thinking about this and have a proposal now. The reason its hard in the first place is that My proposal is to change #[tokio::main]
async fn main() {
let mut app = Router::new(AppState {});
// can also consider having `Router::with_state(...)` and
// then `Router::new()` just being `with_state(())`
// add a route for `GET /` that goes to `handler`
app.get("/", handler);
// add routes for both `GET /foo` and `POST /foo`
app.get("/foo", handler).post(handler);
// this wouldn't work
// the thing returned by `get` doesn't allow adding routes with new paths
// just new methods
// this we can still debate but its not strictly relevant to this issue
// app.get("/something", handler).post("/something-else", bar);
axum::Server::bind(...)
.serve(app.into_make_service())
.await
.unwrap();
}
// the specific state our app needs
#[derive(Clone)]
struct AppState {}
async fn handler() {} Now Next add a new pub trait Handler<S, T, B = Body>: Clone + Send + Sized + 'static {
type Future: Future<Output = Response> + Send + 'static;
fn call(self, state: S, req: Request<B>) -> Self::Future;
} The signature of impl<B> Router<B> {
pub fn get<H, S, T>(mut self, path: &str, handler: T) -> Self
where
H: Handler<S, T, B>
{
// ...
}
} Next the same state is present in impl<S, B> RequestParts<S, B> {
// only immutable access to the state
pub fn state(&self) -> &S {
&self.state
}
} This changes #[async_trait]
pub trait FromRequest<S, B>: Sized {
type Rejection: IntoResponse;
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection>;
} Next we add a pub struct State<S>(pub S);
#[async_trait]
impl<S, B> FromRequest<S, B> for State<S>
where
S: Clone
{
type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
Ok(Self(req.state().clone()))
}
} I think that should work.
|
Could also consider doing app.at("/foo").get(handler).post(handler); It is perhaps more symmetric 🤷 I haven't thought much about this and have to play around with it. Note this is exactly what tide's router does. |
This would be a lot of churn, removing |
Hm yeah that could work if I was a little worried that rust wouldn't be able to infer the state type if you do Thats way nicer and should result in less churn. Do you have an opinion about the changes to |
Attempting to extract
Extension<Foo>
without having.layer(Extension(foo))
will result in a runtime error, rather than a compile error. That has been a long standing problem since 0.1. We don't have a good solution and ergonomic solution to it yet but I think its something we should experiment with for 0.6.The text was updated successfully, but these errors were encountered: