内容简介
本专题将带你从零开始实现一个简单的、RESTFUL 风格的 Todo 服务。包括:JSON 响应及请求、PostgreSQL 的使用、自定义错误的处理、RESTFul 的定义、配置文件、日志的记录等。配置文件
本章我们将实现配置文件的加载。我们将对 axum 的监听地址和 PostgreSQL 相关的信息进行配置,并保存到`.env`文件中。错误处理
本章我们将自定义错误、自定义一个`Result`以及让它们作为 handler 的返回值,进行 HTTP 响应。自定义响应
我们的 Todo 服务是对外提供 API 的服务,它的响应格式总是`JSON`类型。为此,我们可以定义响应类型,以简化 handler 的编写。数据库、模型、状态共享及TodoList
现在是时候开始进行数据库操作,以便实现功能了。本章将实现`TodoList`的功能。日志及重构
本章我们将对之前的代码进行重构并且使用日志记录可能发生的错误。实现TodoItem
经过一番重构,目前我们的 Todo 服务已经基本完善了,现在只差最后一个部分:TodoItem。本章我们就来实现它。总结
经过一番不懈努力,我们终于完成了一个提供 RESTFul API 的 Todo 服务。虽然功能简单,但它涉及到了 Axum 开发的多个方面。
实现TodoItem
经过一番重构,目前我们的 Todo 服务已经基本完善了,现在只差最后一个部分:TodoItem。本章我们就来实现它。
模型定义
首先,我们在src/model.rs
定义相关模型:
/// 待办事项模型
#[derive(PostgresMapper, Serialize)]
#[pg_mapper(table = "todo_item")]
pub struct TodoItem {
pub id: i32,
pub title: String,
pub checked: bool,
pub list_id: i32,
}
/// 待办事项新ID模型
#[derive(PostgresMapper, Serialize)]
#[pg_mapper(table = "todo_item")]
pub struct TodoItemID {
pub id: i32,
}
接着,我们在src/form.rs
定义相关表单:
#[derive(Deserialize)]
pub struct CreateTodoItem {
pub title: String,
pub list_id: i32,
}
数据库操作
然后,我们实现其数据库操作。创建src/db/todo_item.rs
文件,并输入以下代码:(别忘了在src/db/mod.rs
中声明todo_item
模块)
pub async fn all(client: &Client, list_id: i32) -> Result<Vec<TodoItem>> {
let result: Vec<TodoItem> = super::query(
client,
"SELECT id,title,checked,list_id FROM todo_item WHERE list_id=$1 ORDER BY id ASC",
&[&list_id],
)
.await?;
Ok(result)
}
pub async fn find(client: &Client, list_id: i32, item_id: i32) -> Result<TodoItem> {
let result: TodoItem = super::query_one(
client,
"SELECT id,title,checked,list_id FROM todo_item WHERE id=$1 AND list_id=$2",
&[&item_id, &list_id],
)
.await?;
Ok(result)
}
pub async fn check(client: &Client, list_id: i32, item_id: i32) -> Result<bool> {
let result = super::execute(
client,
"UPDATE todo_item SET checked=true WHERE id=$1 AND list_id=$2 AND checked=false",
&[&item_id, &list_id],
)
.await?;
Ok(result > 0)
}
pub async fn delete(client: &Client, list_id: i32, item_id: i32) -> Result<bool> {
let result = super::execute(
client,
"DELETE FROM todo_item WHERE id=$1 AND list_id=$2",
&[&item_id, &list_id],
)
.await?;
Ok(result > 0)
}
pub async fn create(client: &Client, frm: form::CreateTodoItem) -> Result<TodoItemID> {
let result = query_one(
client,
"INSERT INTO todo_item (title, checked, list_id) VALUES ($1,$2,$3) RETURNING id",
&[&frm.title, &false, &frm.list_id],
)
.await?;
Ok(result)
}
从代码可以看出,都是对父模块相关方法的调用,这里不再赘述。
handler 的定义
同样的,我们创建src/handler/todo_item.rs
,并输入以下内容:
至此,TodoItem 已经开发完成,也意味着整个 Todo 服务开发完成。