我们的 Todo 服务是对外提供 API 的服务,它的响应格式总是JSON
类型。为此,我们可以定义响应类型,以简化 handler 的编写。
本章代码在03/自定义响应分支。
自定义响应
声明response
模块,并定义相关的数据结构:
// src/response.rs
#[derive(Serialize)]
pub struct Response<T: Serialize> {
pub code: i32,
pub msg: String,
pub data: Option<T>,
}
-
msg
:提示信息,如果没有错误,该值为OK
-
data
:响应的数据。如果发生错误,该值为null
(Rust 里的None
)
为了以 JSON 作为响应,这个结构体以及包括data
在内的所有字段必须是可序列化的,即实现了Serialize
trait。
为了简化操作,定义几个方法:
impl<T> Response<T>
where
T: Serialize,
{
pub fn new(code: i32, msg: String, data: Option<T>) -> Self {
Self { code, msg, data }
}
pub fn ok(data: T) -> Self {
Self::new(0, "OK".to_string(), Some(data))
}
pub fn err(code: i32, msg: String) -> Self {
Self::new(code, msg, None)
}
}
-
new()
:创建一个新的响应 -
ok()
:创建一个没有错误发生响应 -
err()
:创建一个发生错误的响应
重导出 Response
目前,Response
结构体的引用路径是crate::response::Response
,这个路径有两个问题:
-
略显冗长
为了便于其它模块引用这个结构体,我们在src/main.rs
中对其进行重导出:
pub use response::Response;
现在它的引用路径变成了:
crate::Response;
在 handler 中使用自定义响应
现在我们把自定义响应和上一章所述的错误处理中实现的自己的Result
(注意,别忘了被它隐藏的AppError
)在 handler 中使用:
pub async fn usage<'a>() -> Result<Json<Response<Vec<&'a str>>>> {
let data = r#"
GET /todo -- 获取所有待办列表
POST /todo -- 添加待办列表
GET /todo/:list_id -- 获取待办列表详情
DELETE /todo/:list_id -- 删除指定的待办列表,包括其所有待办事项
PUT /todo/:list_id -- 修改待办列表
GET /todo/:list_id/items -- 获取待办列表的所有待办事项
GET /todo/:list_id/items/:item_id -- 获取待办事项的详情
PUT /todo/:list_id/items/:item_id -- 修改待办事项(将其的状态修改为“已完成”)
DELETE /todo/:list_id/items/:item_id -- 删除待办事项
"#;
let data: Vec<&str> = data
.split('\n')
.into_iter()
.map(|line| line.trim())
.filter(|line| !line.is_empty())
.collect();
let data = Response::ok(data);
Ok(Json(data))
}
-
这里使用的是
crate::Result
-
这里使用的是
crate::Response
为了加强印象,请再看一遍crate::Result
的定义:
type Result<T> = std::result::Result<T, error::AppError>;
在自定义错误中使用自定义响应
之前我们的自定义错误的 IntoResponse
直接使用的是 String
的 into_response()
,现在我们将它改成使用我们自定义响应的 JSON 响应:
impl IntoResponse for AppError {
type Body = Full<Bytes>;
type BodyError = Infallible;
fn into_response(self) -> axum::http::Response<Self::Body> {
let code = (&self).code();
let msg = match self.message {
Some(msg) => msg,
None => "有错误发生".to_string(),
};
let res: Response<()> = Response::err(code, msg);
Json(res).into_response()
}
}