内容简介
本专题将带你从零开始实现一个简单的、RESTFUL 风格的 Todo 服务。包括:JSON 响应及请求、PostgreSQL 的使用、自定义错误的处理、RESTFul 的定义、配置文件、日志的记录等。配置文件
本章我们将实现配置文件的加载。我们将对 axum 的监听地址和 PostgreSQL 相关的信息进行配置,并保存到`.env`文件中。错误处理
本章我们将自定义错误、自定义一个`Result`以及让它们作为 handler 的返回值,进行 HTTP 响应。自定义响应
我们的 Todo 服务是对外提供 API 的服务,它的响应格式总是`JSON`类型。为此,我们可以定义响应类型,以简化 handler 的编写。数据库、模型、状态共享及TodoList
现在是时候开始进行数据库操作,以便实现功能了。本章将实现`TodoList`的功能。日志及重构
本章我们将对之前的代码进行重构并且使用日志记录可能发生的错误。实现TodoItem
经过一番重构,目前我们的 Todo 服务已经基本完善了,现在只差最后一个部分:TodoItem。本章我们就来实现它。总结
经过一番不懈努力,我们终于完成了一个提供 RESTFul API 的 Todo 服务。虽然功能简单,但它涉及到了 Axum 开发的多个方面。
自定义响应
- 994255
- 2021-11-25 10:29:59
我们的 Todo 服务是对外提供 API 的服务,它的响应格式总是JSON
类型。为此,我们可以定义响应类型,以简化 handler 的编写。
本章代码在03/自定义响应分支。
本章代码在03/自定义响应分支。
自定义响应
声明response
模块,并定义相关的数据结构:
-
code
:响应状态码,如果没有错误,该值为0
-
msg
:提示信息,如果没有错误,该值为OK
-
data
:响应的数据。如果发生错误,该值为null
(Rust 里的None
)
code
:响应状态码,如果没有错误,该值为0
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
这里使用的是 crate::Response
为了加强印象,请再看一遍crate::Result
的定义:
type Result<T> = std::result::Result<T, error::AppError>;