如何在使用tower::service_fn的Axum route_service处理器中提取路径参数?
可以提取路径参数,解决方案如下
你可以通过Axum存储在Request扩展中的RouteParts来提取路径参数,无需改动原有的错误处理逻辑,具体实现方式如下:
修改后的完整代码
use axum::{ extract::Json, http::{Request, Response, StatusCode, Method}, routing::{RouteParts, Router}, error_handling::HandleError, }; use reqwest::{Error as ReqwestError, ErrorKind, Client}; use serde_json::Value; use tower_http::cors::{Any, CorsLayer}; use dotenv::dotenv; async fn get_person(person_id: String) -> Result<Json<Value>, ReqwestError> { let request_url = format!("https://swapi.dev/api/people/{}", person_id); let response = reqwest::get(request_url).await?; if response.status().is_success() { let json = response.json::<Value>().await?; Ok(Json(json)) } else { Err(response.error_for_status().unwrap_err()) } } #[tokio::main] async fn main() { dotenv().ok(); let client = Client::new(); let cors = CorsLayer::new() .allow_methods([Method::GET]) .allow_origin(Any); let faillible_person_service = tower::service_fn(|req: Request| async { // 提取路由中的person_id参数 let route_parts = req.extensions().get::<RouteParts>().ok_or_else(|| { ReqwestError::new(ErrorKind::Other, "无法获取路由信息") })?; let person_id = route_parts.params().get("person_id").ok_or_else(|| { ReqwestError::new(ErrorKind::Other, "缺少person_id路径参数") })?.to_owned(); let body = get_person(person_id).await?; Ok::<_, ReqwestError>(body.into_response()) }); let app = Router::new() .route("/", get(get_weather)) .route_service( "/person/:person_id", HandleError::new(faillible_person_service, handle_reqwest_error), ) .layer(cors) .with_state(client); let listener = tokio::net::TcpListener::bind("127.0.0.1:1337") .await .unwrap(); axum::serve(listener, app).await.unwrap(); } async fn handle_reqwest_error(err: ReqwestError) -> Response { let status = err.status().unwrap_or(reqwest::StatusCode::INTERNAL_SERVER_ERROR); let axum_status = StatusCode::from_u16(status.as_u16()) .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR); let res_json = Json(serde_json::json!({ "error": { "message": format!("请求出错: {}", err), }, })); (axum_status, res_json).into_response() } // 保留你原有的get_weather函数实现 async fn get_weather() -> Json<Value> { Json(serde_json::json!({"message": "weather endpoint"})) }
关键说明
- RouteParts的作用:Axum会将路由匹配结果(包括路径参数)存储在
Request的extensions中,类型为RouteParts,可以直接取出使用。 - 错误包装:如果提取参数失败,将错误包装成
reqwest::Error,这样就能继续使用?运算符,保持原有错误处理流程的一致性,最终在handle_reqwest_error中统一处理。 - 参数获取:通过
route_parts.params().get("person_id")直接按参数名获取值,无需使用Path提取器,适配service_fn的使用场景。
内容的提问来源于stack exchange,提问作者deepsun
相关产品推荐
相关产品推荐

