我有以下简单的代码:
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
}
在现实世界的示例中,我的
state
将包含来自 sqlx
的数据库池,并且 info_middleware
会执行诸如确保 token
有效、提取 IP 地址、用户代理等操作,并将其提供给所有人处理程序。
对于路由,基本上:
"/"
是将用户带到 home_get
"/assets/favicon.ico"
提供资源文件夹中的图标文件,并且home_get
。1 和 2 效果很好。 3 没有。
例如,
"/submit"
失败,内部错误低于 500:
Missing request extension: Extension of type `fallbackdemo::MyConnectionInfo` was not found. Perhaps you forgot to add it? See `axum::Extension`.
我认为这是因为扩展中间件无法进行后备访问?
如何让它发挥作用?或者一些解决方法(除了必须手动指定每个后备路由之外)?
它不起作用的原因是因为你正在使用
route_layer
。它仅适用于特定路线,如下所述:https://docs.rs/axum/latest/axum/struct.Router.html#method.route_layer
使用
layer
来代替,它会正常工作:
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
}