是否可以将 URL 路径参数提取到 Axum
route_service
处理程序中,该处理程序是使用下面我的 tower::service_fn
中看到的 main
错误处理方法实现的?从 Tower 文档中,我看到 service_fn
收到了请求,但除此之外,我无法弄清楚如何使用 Path
来提取我的 person_id
路径的 /person/:person_id
路径参数部分。
这是我用来实现迄今为止所拥有的内容的 Axum 文档页面:https://docs.rs/axum/latest/axum/error_handling/index.html#routing-to-fallible-services
我之所以实施此实施有几个原因。
?
运算符来从外部 API 获取实际响应,无论是成功还是错误。reqwest::Error
状态代码,并为我的 API 的错误响应返回该状态代码。async fn get_person(/*
Is it possible to extract the person_id here given my implemntation?
*/) -> Result<Json<serde_json::Value>, reqwest::Error> {
let request_url = format!("https://swapi.dev/api/people/{}", "1asdf".to_owned());
let response = reqwest::get(request_url).await?;
if response.status().is_success() {
let json = response.json::<serde_json::Value>().await?;
Ok(Json(json))
} else {
Err(response.error_for_status().unwrap_err())
}
}
#[tokio::main]
async fn main() {
dotenv().ok();
let client = reqwest::Client::new();
let cors = CorsLayer::new()
.allow_methods([Method::GET])
.allow_origin(Any);
let faillible_person_service = tower::service_fn(|req: Request| async {
let body = get_person().await?;
Ok::<_, reqwest::Error>(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: reqwest::Error) -> Response {
// Rather than have my API return a 200, return the status code from the Reqwest call
let status = err.status();
let status = status.unwrap_or(reqwest::StatusCode::INTERNAL_SERVER_ERROR);
let status_as_u16 = status.as_u16();
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!("Something went wrong: {}", err),
},
}));
return (axum_status, res_json).into_response();
}
提取路径参数等数据(又名提取器),通过
FromRequest
和FromRequestParts
特征来表达。路径参数只需要后者,因为主体不相关。
要在塔服务中使用它们,您可以拆分请求并手动调用提取器方法。像这样的东西:
use axum::extract::{FromRequestParts, Path, Request};
use axum::response::{IntoResponse, Response};
use axum::{Json, Router};
async fn get_person(person_id: String) -> Result<Json<serde_json::Value>, reqwest::Error> {
...
}
let faillible_person_service = tower::service_fn(|req: Request| async {
let (mut req_parts, _req_body) = req.into_parts();
let person_id = match Path::<String>::from_request_parts(&mut req_parts, &()).await {
Ok(Path(person_id)) => person_id,
Err(rejection) => return Ok(rejection.into_response()),
};
let body = get_person(person_id).await?;
...
});
match
是这样完成的,因为路径提取(以及大多数提取器)可能会失败,但 Axum 的设计方式是它的所有错误都会返回响应,所以这就是它的作用。您可以使用此模式使用更多 FromRequestParts
提取器。如果您需要主体(即 FromRequest
提取器),则必须最后完成并通过 Request::from_parts
给出重新组装的请求。
老实说,我真的不会推荐这个,而且你的理由听起来并不合理。如果您的目标只是简单地返回 reqwest 响应作为 Axum 响应,我在这里有一个答案,描述了您如何做到这一点:A proxy with axum 0.7 and reqwest 0.12 based on http 1?