axum中的各种响应
本章主要讨论 axum 的响应。axum 已经实现了多种响应,比如纯文本、HTML、JSON 及 自定义响应头(response header)。除了这些 axum 内置的响应之外,我们还将讨论如何将自己定义的结构体,作为响应返回给客户端。在axum中获取请求数据
在日常开发中,我们需要与用户进行交互,从各种渠道获取用户输入,包括但不限于:表单、URL 参数、URL Path 以及 JSON 等。axum 为我们提供了这些获取用户输入的支持。axum的状态共享
**状态共享**是指,在整个应用或不同路由之间,共享一份数据。axum 提供了方便的状态共享机制,但可能也会踩坑。本章将带你学习如何在 axum web 应用中共享状态。路由
axum 提供了常用的 HTTP 请求方式对应的路由,比如 `get`, `post`, `put`, `delete` 等。除此之外,axum 还提供了“嵌套路由”。路由,通常和 `handler(处理函数)` 结合在一起。中间件
中间件是一类提供系统软件和应用软件之间连接、便于软件各部件之间的沟通的软件,应用软件可以借助中间件在不同的技术架构之间共享信息与资源。本章将讨论如何在axum中使用中间件,以及如何自定义中间件。axum处理静态文件
和其它 Web 框架一样,axum 也会对所有请求进行处理。对于 CSS、JS 及图片等静态文件,并不需要 axum 的 handler 进行处理,而是只需要简单的把它们的内容进行返回即可。axum 提供了处理静态文件的中间件。axum处理cookie
Cookie 是通过 HTTP Header 进行传递的。由某个响应头进行设置,然后其它请求头就可以获取到了。本章将通过模拟用户中心来用 axum 操作 HTTP Header 演示 Cookie 的读写操作。axum 操作 redis
通过 redis-rs 这个 crate,可以很方便的操作 redis。它提供了同步和异步两种连接,由于我们要集成到 axum 中,所以这里使用异步连接。本章将展示如何获取 redis 异步连接、如何将字符串保存到 redis、如何获取到保存在 redis 里的字符串以及如何通过 redis 保存和读取自定义结构体。axum 操作 Postgres 数据库
PostgreSQL 是一款天然支持异步操作的高性能开源关系型数据库。本章将讨论如何在 axum 中使用 PostgreSQL。包括:数据的增加、修改、删除、查找以及开始事务保证业务的原子性。axum 实现 Session
由于 HTTP 是无状态的,所以我们可以通过Cookie来维护状态。但 cookie 是直接保存到客户端,所以对于敏感数据,不能直接保存到 cookie。我们可以把敏感数据保存到服务端,然后把对应的 ID 保存到 cookie,这就是 Session。本章我们将使用 Cookie 和 Redis 实现一个简单的 Session。axum 集成 JWT
Json web token(JWT)是为了网络应用环境间传递声明而执行的一种基于 JSON 的开发标准(RFC 7519),该 token 被设计为紧凑且安全的,特别适用于分布式站点的单点登陆(SSO)场景。JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该 token 也可直接被用于认证,也可被加密。axum 中使用模板引擎
利用模板引擎,我们不需要再把 HTML 代码写在 Rust 代码中了,而是将其独立保存为`*.html`文件。既方便维护,也有利用开发。axum 上传文件
文件上传是 Web 开发中常见的功能,本章将演示如何在 axum 实现文件上传。axum 集成hcaptcha验证码进行人机验证
在机器人采集、恶意攻击的今天,人机验证筑起了一道保护网。从之前的图片验证码,到 Google 提供的 reCaptcha,人机验证经历了一段漫长的演进过程。配置文件:让axum app可配置
将数据库连接信息、redis 连接信息以及 Web 应用监听地址等信息通过配置文件进行单独管理是一个比较好的开发实践。这样就无须在更改配置的时候重新编译整个项目,同时也可以针对不同环境使用不同的配置文件。axum错误处理
本章主要讨论 axum 的错误处理
路由
axum 提供了常用的 HTTP 请求方式对应的路由,比如 get, post, put, delete 等。除此之外,axum 还提供了“嵌套路由”。路由,通常和 handler(处理函数) 结合在一起。
handler 是什么
通常理解,handler 是指接收用户的请求,并将处理结果作为响应返回给用用户的函数。
它的返回值是什么
正如《axum 中的各种响应》所说,在 axum 中,所有实现了 IntoResponse 的数据类型都可以作为 handler 的返回值。
常用的请求方式
| 请求方式 | 说明 |
|---|---|
GET | 该请求应该只被用于获取数据 |
POST | 用于将实体提交到指定的资源,通常导致在服务器上的状态变化或副作用 |
PUT | 用请求有效载荷替换目标资源的所有当前表示 |
DELETE | 删除指定的资源 |
上表节选自《HTTP 请求方法》,更多请求方法,请参照原文。
在 axum 中,axum::routing 重新导出了 HTTP 请求方式的对应函数。也就是说,使用axum::routing::get() 来定义一个 GET 请求的路由。
链式操作
axum 的路由支持链式操作。假设以下场景:在编辑某用户信息时,首先要使用表单显示用户当前的信息(GET),修改完成之后,还要将这个表单数据发送到服务器(POST)。在这个过程中,路由的 URL 是一样的。
由于还未学习到模板的相关内容,本例我们用直接书写 HTML 来作演示。你也可以看一下《axum 中使用模板引擎》
定义相关的 struct:
/// 通过表单提交数据
#[derive(Deserialize)]
pub struct EditUser {
pub id: i32,
pub username: String,
pub email: String,
}
/// 对应数据库的模型
pub struct UserModel {
pub id: i32,
pub username: String,
pub email: String,
}
定义 handler:
/// 显示要修改的用户
async fn edit_user(Path(id): Path<i32>) -> Html<String> {
let model = UserModel {
id,
username: "AXUM.RS".to_string(),
email: "[email protected]".to_string(),
};
let html = format!(
r#"
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
<meta charset="utf-8" />
<meta name="author" content="axum.rs ([email protected])" />
<title>
修改用户-AXUM中文网
</title>
</head>
<body>
<form method="post" action="/edit_user/{}">
<input type="hidden" name="id" value="{}">
<div>
<label>用户名</label>
<input type="text" name="username" value="{}">
</div>
<div>
<label>Email</label>
<input type="email" name="email" value="{}">
</div>
<div>
<button type="submit">提交</button>
</div>
</form>
</body>
</html>
"#,
model.id, model.id, model.username, model.email
);
Html(html)
}
/// 对用户进行修改
async fn edit_user_action(Form(frm): Form<EditUser>) -> Html<String> {
let html = format!(
r#"
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
<meta charset="utf-8" />
<meta name="author" content="axum.rs ([email protected])" />
<title>
修改用户-AXUM中文网
</title>
</head>
<body>
<h1>修改成功!</h1>
<p>修改后的用户资料:</p>
<div>ID: {} </div>
<div>用户名: {} </div>
<div>Email: {} </div>
</body>
</html>"#,
frm.id, frm.username, frm.email
);
Html(html)
}
定义路由:
route("/edit_user/:id", get(edit_user).post(edit_user_action))
为了方便进路由进行分组,axum 提供了嵌套路由功能。比如有以下路由:
async fn news_index() -> &'static str {
"new index"
}
async fn news_detail(Path(id): Path<i32>) -> String {
format!("new detail {}", id)
}
async fn news_comments(Path(id): Path<i32>) -> String {
format!("new comments {}", id)
}
定义子路由:
将子路由嵌到全局路由:
本章讲解了 axum 路由功能。代码可以在代码仓库中找到。
思考题:
如何在 axum 实现跳转(即重定向)功能?
提示:
参考答案:
async fn redirect() -> (StatusCode, HeaderMap, ()) {
let mut headers = HeaderMap::new();
headers.insert(
axum::http::header::LOCATION,
"https://axum.rs".parse().unwrap(),
);
(StatusCode::FOUND, headers, ())
}
