I have the following simple code:
use std::{net::SocketAddr, path::Path};
use axum::{extract::{ConnectInfo, Request, State}, middleware::Next, response::{Html, IntoResponse, Response}, routing::{get, get_service}, Extension};
use tower_http::services::ServeDir;
#[derive(Clone, Debug)]
struct AppState {
something: String
}
#[tokio::main]
async fn main() {
let state = AppState {
something: "Hello world!".to_string()
};
let app = axum::Router::new()
.route("/", get(home_get))
.nest_service("/assets", get_service(ServeDir::new(Path::new("assets"))))
.fallback(get(home_get).with_state(state.clone()))
.route_layer(axum::middleware::from_fn_with_state(state.clone(),info_middleware))
.with_state(state);
let listener = tokio::net::TcpListener::bind(":::7070").await.unwrap();
axum::serve(listener, app.into_make_service_with_connect_info::<SocketAddr>()).await.unwrap();
}
async fn home_get(state: State<AppState>, connection_info: Extension<MyConnectionInfo>) -> Response {
Html(format!("{} You called from: {}",state.something,connection_info.ip)).into_response()
}
#[derive(Clone, Debug)]
pub struct MyConnectionInfo {
pub ip: String
}
pub async fn info_middleware(addr: ConnectInfo<SocketAddr>, mut request: Request, next: Next) -> Response {
request.extensions_mut().insert(MyConnectionInfo {ip: addr.to_string()});
next.run(request).await
}
In a real world example, my state
would contain the database pool from sqlx
and the info_middleware
would do things like ensure token
is valid, extract IP address, User agent etc. and make it available to all handlers.
For routing, basically:
"/"
is to take user tohome_get
"/assets/favicon.ico"
serves the favicon file in the assets folder, and- any other routes is to fallback to
home_get
.
1 and 2 work well. 3 does not.
For example, "/submit"
fails with below 500 Internal error:
Missing request extension: Extension of type `fallbackdemo::MyConnectionInfo` was not found. Perhaps you fot to add it? See `axum::Extension`.
I think this is because the Extension middleware isn't accessible to fallbacks?
How to make it work? Or some workaround (other than having to specify every single fallback route manually)?
I have the following simple code:
use std::{net::SocketAddr, path::Path};
use axum::{extract::{ConnectInfo, Request, State}, middleware::Next, response::{Html, IntoResponse, Response}, routing::{get, get_service}, Extension};
use tower_http::services::ServeDir;
#[derive(Clone, Debug)]
struct AppState {
something: String
}
#[tokio::main]
async fn main() {
let state = AppState {
something: "Hello world!".to_string()
};
let app = axum::Router::new()
.route("/", get(home_get))
.nest_service("/assets", get_service(ServeDir::new(Path::new("assets"))))
.fallback(get(home_get).with_state(state.clone()))
.route_layer(axum::middleware::from_fn_with_state(state.clone(),info_middleware))
.with_state(state);
let listener = tokio::net::TcpListener::bind(":::7070").await.unwrap();
axum::serve(listener, app.into_make_service_with_connect_info::<SocketAddr>()).await.unwrap();
}
async fn home_get(state: State<AppState>, connection_info: Extension<MyConnectionInfo>) -> Response {
Html(format!("{} You called from: {}",state.something,connection_info.ip)).into_response()
}
#[derive(Clone, Debug)]
pub struct MyConnectionInfo {
pub ip: String
}
pub async fn info_middleware(addr: ConnectInfo<SocketAddr>, mut request: Request, next: Next) -> Response {
request.extensions_mut().insert(MyConnectionInfo {ip: addr.to_string()});
next.run(request).await
}
In a real world example, my state
would contain the database pool from sqlx
and the info_middleware
would do things like ensure token
is valid, extract IP address, User agent etc. and make it available to all handlers.
For routing, basically:
"/"
is to take user tohome_get
"/assets/favicon.ico"
serves the favicon file in the assets folder, and- any other routes is to fallback to
home_get
.
1 and 2 work well. 3 does not.
For example, "/submit"
fails with below 500 Internal error:
Missing request extension: Extension of type `fallbackdemo::MyConnectionInfo` was not found. Perhaps you fot to add it? See `axum::Extension`.
I think this is because the Extension middleware isn't accessible to fallbacks?
How to make it work? Or some workaround (other than having to specify every single fallback route manually)?
Share Improve this question edited Jan 19 at 23:13 Ken White 126k15 gold badges236 silver badges464 bronze badges asked Jan 19 at 21:56 sudoExclamationExclamationsudoExclamationExclamation 8,81410 gold badges51 silver badges119 bronze badges1 Answer
Reset to default 1The reason it doesn't work is because you are using route_layer
. It only applies to a specific route, as explained here: https://docs.rs/axum/latest/axum/struct.Router.html#method.route_layer
use layer
instead and it will work just fine:
use std::{net::SocketAddr, path::Path};
use axum::{
extract::{ConnectInfo, Request, State},
middleware::Next,
response::{Html, IntoResponse, Response},
routing::{get, get_service},
Extension,
};
use tower_http::services::ServeDir;
#[derive(Clone, Debug)]
struct AppState {
something: String,
}
#[tokio::main]
async fn main() {
let state = AppState {
something: String::from("Hello World!"),
};
let app = axum::Router::new()
.route("/", get(home_get))
.nest_service("/assets", get_service(ServeDir::new(Path::new("assets"))))
.fallback(get(home_get).with_state(state.clone()))
.layer(axum::middleware::from_fn_with_state(
state.clone(),
info_middleware,
))
.with_state(state);
let listener = tokio::net::TcpListener::bind(":::7070").await.unwrap();
axum::serve(
listener,
app.into_make_service_with_connect_info::<SocketAddr>(),
)
.await
.unwrap();
}
async fn home_get(
state: State<AppState>,
connection_info: Extension<MyConnectionInfo>,
) -> impl IntoResponse {
Html(format!(
"{} You called from: {}",
state.something, connection_info.ip
))
}
#[derive(Clone, Debug)]
struct MyConnectionInfo {
pub ip: String,
}
pub async fn info_middleware(
addr: ConnectInfo<SocketAddr>,
mut request: Request,
next: Next,
) -> Response {
request.extensions_mut().insert(MyConnectionInfo {
ip: addr.to_string(),
});
next.run(request).await
}