Rust新手练习案例库- rust-learning-example

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中进行练习。

🤔留给自己的课后作业

  1. 以下数据类型解析:

    • form-data
    • x-wwww-form-urlencoded
    • Binary
    • 等其他一些类型
  2. 混合解析练习:body+query+路径解析

相关推荐
扶苏-su2 小时前
Java--转换流-InputStreamReader 和 OutputStreamWriter
java·开发语言
码事漫谈2 小时前
一文读懂“本体论”这个时髦词
后端
IguoChan2 小时前
D2L(2) — softmax回归
后端
无限进步_2 小时前
【C语言&数据结构】二叉树遍历:从前序构建到中序输出
c语言·开发语言·数据结构·c++·算法·github·visual studio
码事漫谈2 小时前
C++线程编程模型演进:从Pthread到jthread的技术革命
后端
花北城3 小时前
【C#】MES消耗类数量逻辑处理(物料消耗、打包装箱、生产订单派工等)
开发语言·c#
半夏知半秋3 小时前
kcp学习-skynet中的kcp绑定
开发语言·笔记·后端·学习
扶苏-su3 小时前
Java--标准输入输出流
java·开发语言
szm02253 小时前
Spring
java·后端·spring