rust-learning-example 是一个Rust开发练习案例库,一个分支为一篇案例分享内容和实战代码。它适合Rust入门练习,后序会陆续更新练习分支。
Github地址: https://github.com/zsf2025/rust-learning-example/tree/feat-axum
下面我们来看看第一个练习案例:《Axum入门实战练习》
一、入门级:夯实基础(重点练核心 API)
适合刚接触 Axum,先熟悉「路由定义、请求解析、响应返回」的基础用法。
1. 多功能 Hello World(覆盖基础请求处理)
目标:不只是简单返回字符串,而是结合路径参数、查询参数、JSON 请求/响应。
- 核心功能:
- 固定路由:
GET /返回 "Hello Axum!" - 路径参数:
GET /hello/:name返回 "Hello, {name}!"(解析路径参数) - 查询参数:
GET /greet?name=Alice&age=20返回 "Greet Alice (20)!"(解析Query) - JSON 请求:
POST /echo接收 JSON 数据(如{ "msg": "hi" }),原样返回(用Json提取器)
- 固定路由:
- 依赖:
axum + tokio + serde(derive/_json) - 练习点:
Router路由组合、Path/Query/Json提取器、IntoResponse响应 trait。
2. 静态文件服务器
目标 :学会用 Axum 提供静态资源(如 HTML、CSS、图片),熟悉 ServeDir 服务。
- 核心功能:
- 访问
GET /返回static/index.html(前端静态页面) - 访问
GET /assets/:file读取static/assets/下的文件(图片、CSS) - 404 页面:访问不存在的路由返回自定义 404 HTML
- 访问
- 依赖:
axum + tokio + tower-http(ServeDir) - 练习点:
Router::nest路由嵌套、ServeDir静态资源服务、自定义错误响应。
这里假设你已经入门基本语法,并且安装了rust和cargo, 现在出发学习案例:
1. 多功能 Hello World(覆盖基础请求处理)
初始化项目 + 依赖添加说明
这里我们需要添加axumweb框架、tokio异步运行时依赖(它相当于异步版本的std::thread)
cargo new rust-learning-example
cd rust-learning-example
# 添加 axum(默认包含核心功能)
cargo add axum
# 添加 tokio(需启用多线程运行时、宏、网络功能)
cargo add tokio --features rt-multi-thread,macros,net
# 可选:添加 serde(用于 JSON 序列化/反序列化,Web 开发必备)
cargo add serde --features derive
最终Cargo.tomal依赖会自动生成如下(无需手动修改):
[dependencies]
axum = "0.8.8"
serde = { version = "1.0.228", features = ["derive"] }
tokio = { version = "1.49.0", features = ["rt-multi-thread", "macros", "net"] }
🚀 准备就绪,现在开始我们的第一Axum程序。
1.1 启动Axum Web服务, 练习【固定路由】GET /
rust
// 以下为 main.rs 文件内容
use axum::{routing::get, Router, serve };
use tokio::net::TcpListener;
// 返回 &'static str(静态字符串,Axum 自动转为 200 OK 响应)
async fn hello_world() -> &'static str {
"Hello, Axum! 🚀"
}
#[tokio::main]
// 给 main 加返回值:Result<(), 错误类型>
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 创建路由器(Router):管理路由规则
let app = Router::new()
// 绑定路由:GET 方法 + 路径 "/" + 处理函数 hello_world
.route("/", get(hello_world));
// 绑定端口(返回 Result,需用 ? 处理错误)
let listener = TcpListener::bind("127.0.0.1:3000").await?;
println!("服务器启动成功!访问:http://127.0.0.1:3000");
// 启动服务器
serve(listener, app.into_make_service()).await?;
// 返回
Ok(())
}
👋 用浏览器访问http://127.0.0.1:3000, 我可以看到页面输出Hello, Axum! 🚀
1.2 练习【解析路径参数】 GET /hello/:name(基于之前的代码...为省略部分)
rust
// 依赖引入Path,用来解析路径
use axum::{routing::get, Router, serve, extract::{ Path } };
....
// 实现一个hello_name函数
async fn hello_name(Path(name): Path<String>) -> String {
format!("Hello, {}! 👋", name)
}
...
// let = Router::new()后面添加
.route("/hello/{name}", get(hello_name))
...
👋在浏览器输入http://127.0.0.1:3000/hello/zhang心独酌,可以看到页面输出Hello, zhang心独酌! 👋
1.3 练习【查询参数】GET /greet?name=Alice&age=20
rust
// 依赖引入Query
use axum::{routing::get, Router, serve, extract::{ Path, Query } };
...
// 实现greet函数
// Query是一个Map,所有在rust中我们使用HashMap来接收
async fn greet(params: Query<std::collections::HashMap<String, String>>) -> String {
// 处理两个参数都有,通过元组匹配
if let (Some(name), Some(sex)) = (params.get("name"), params.get("sex")) {
format!("Greetings, {} the {}! 🌟", name, sex)
}
// 处理只有name
else if let Some(name) = params.get("name") {
format!("Greetings, {}! 🌟", name)
}
// 处理只有sex
else if let Some(sex) = params.get("sex") {
format!("Greetings, {}! 🌟", sex)
}
// 处理没有参数
else {
"Greetings, stranger! 🌟".to_string()
}
}
...
// let = Router::new()后面添加
.route("/greet", get(greet))
...
👋在浏览器输入http://127.0.0.1:3000/greet?name=zhang心独酌&sex=男,可以看到页面输出Greetings, zhang心独酌 the 男! 🌟。其他每种分支都可以测试以下。
1.4 练习【JSON请求】POST /echo 接收 JSON 数据(如 { "msg": "hi" }),原样返回(用 Json 提取器)
rust
/**
* routing添加post,用来post请求
* extract添加Json, 可以理解解析请求体json/响应json包装器
* http::StatusCode 就是请求的http状态码,OK代表200,其他可以查阅文档
* response::IntoResponse 一个trait, 自动转换成HTTP响应并发送给客户端
*/
use axum::{routing::{ get, post }, Router, serve, extract::{ Path, Query, Json }, http::StatusCode, response::IntoResponse };
....
/**
* Deserialize 反序列化,从请求把JSON解析为结果体
* Serialize 序列化
*/
use serde::{Deserialize, Serialize};
....
// 实现一个结构体
#[derive(Deserialize, Serialize)]
struct Payload {
msg: String,
}
....
// 实现路由处理函数
async fn echo_json(Json(payload): Json<Payload>) -> impl IntoResponse {
(StatusCode::OK, Json(Payload {
msg: format!("Echo: {}", payload.msg),
}))
}
...
.route("/echo", post(echo_json))
...
👋我们可以用postman测试一下,header设置Content-type:application/json,Body填写JSON{"msg": "zhang心独酌"},结果返回:
json
{
"msg": "Echo: zhang心独酌"
}
👋👋到这我们已经完成了实战1【基础请求处理】练习,从中我们学会了怎么解析路径参数、怎么读取query参数、body的json参数。避免篇幅太长,实战2【静态文件服务器】我们放在feat-axum-入门级-实战2中进行练习。
🤔留给自己的课后作业
-
以下数据类型解析:
- form-data
- x-wwww-form-urlencoded
- Binary
- 等其他一些类型
-
混合解析练习:body+query+路径解析