为了学习Rust, 我启动了一个叫rust-learning-example项目,主要是分享一些学习Rust开发的案例库,根据一些小案例由浅入深进行Rust开发学习。
Github地址:
https://github.com/zsf2025/rust-learning-example/tree/feat-axum
上一小节,我们学习了Axum「路由定义、请求解析、响应返回」基本用法(内容在feat-axum分支),今天我们就应用之前的基础搭建一个简单的静态文件服务器。(本章节代码在feat-axum_02)
📕静态文件服务器
目标 :学会用 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静态资源服务、自定义错误响应。
一、访问 GET / 返回 static/index.html(前端静态页面)
- 在根目录下创建static目录,在static目录下创建index.html文件,内容如下:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>默认加载</title>
</head>
<body>
<h1>你好~</h1>
<p>访问没有的路径都会到这里~~~</p>
</body>
</html>
- Axum在0.8版本后移除了内置的
serve-dir,推荐使用tower-http的fs特性实现静态文件服务。我们可以按需启用tower-http的一些常用特性:
| 功能 | 所需特性 |
|---|---|
| 静态文件服务 | fs |
| 跨域(CORS) | cors |
| 压缩 | compression |
| 日志 | logging |
| 限流 | rate-limit |
| 设置响应头 | set-header |
| API Key认证 | auht |
| 健康检查 | health-check |
| 反向代理 | proxy |
这里我主要用到fs,在终端输入以下命令加载依赖:
cargo add tower-http --features fs
- 修改我们上一章节的
main.rs,实现GET /返回步骤一创建的index.html文件
rust
// 引入tower_http的fs特性下静态文件服务
use tower_http::services::ServeDir;
// Rust操作文件路径的工具类
use std::path::PathBuf;
...
// 注意掉,这行代码,在这个后面添加我们的静态服务,注释掉主要是为了GET / 能访问到index.html
// .route("/", get(hello_world));
// fallback_service 是一个兜底服务,当所有路由都匹配失败时,会调用这个服务.
// GET / 如果没有路由会找static/index.html, GET /test 没有路径,会找static/test.html,找不到再找static/test/index.html
.fallback_service(ServeDir::new(PathBuf::from("static")));
...
👏运行cargo run, 浏览器访问http://127.0.0.1:3000, 可以看到成功加载了static/index.html页面。
二、访问 GET /assets/:file 读取 static/assets/ 下的文件(图片、CSS)
其实在上一步已经有一个静态服务器,但我需要使用nest_service将ServeDir服务嵌套到/assets路径下,实现GET /assets/:file 读取 static/assets/ 下的文件。
rust
...
// 嵌套静态文件服务到 /assets 路径下
.nest_service("/assets", ServeDir::new(PathBuf::from("static/assets")))
.fallback_service(ServeDir::new(PathBuf::from("static")));
...
启动项目,浏览器访问http://127.0.0.1:3000/assets/style.css, 可以看到成功加载了static/assets/style.css`文件。
404 页面:访问不存在的路由返回自定义 404 HTML
这里我们只要用fallback替换fallback_service即可。fallback是接收一个兜底的函数,当所有路由都匹配失败时,会调用这个函数。
rust
// 定义我们fallback函数
async fn fallback() -> impl IntoResponse {
println!("没有匹配到路由");
// 自定义404页面
(StatusCode::NOT_FOUND, "页面未找到")
}
...
// 自定义 fallback 处理函数
.fallback(fallback);
...
到这里我们会发现GET /无法访问index.html文件了,因为fallback覆盖了fallback_service注册器。这里涉及到一个Axum的路由匹配优先级问题。
- 精确匹配的路由 ( .route() 、 .nest() 等)具有最高优先级
- 服务路由 ( .nest_service() )次之
- fallback 处理器 ( .fallback_service() 、 .fallback() )优先级最低
所有这里我们需要修改一下, 把fallback放在fallback_service里面进行兜底
rust
...
// 自定义 fallback 处理函数
.fallback_service(
ServeDir::new("static")
.fallback(fallback.into_service())
);
...
OK, 现在我们可以访问http://127.0.0.1:3000,可以看到成功加载了static/index.html页面。访问不存在的路由会进行兜底,返回自定义的404页面。
👏总结:
- 静态文件服务器的实现主要是利用了Axum的路由嵌套和
tower-http的ServeDir服务。 - 我们可以通过
nest_service将ServeDir服务嵌套到/assets路径下,实现GET /assets/:file读取static/assets/下的文件。 - 当所有路由都匹配失败时,会调用
fallback函数,返回自定义的404页面。
🤔课后作业
- 处理static目录不存在场景
- 实现一个简单的
static下文件资源管理器,index.html返回的是static目录下的文件列表。是文件查看文件, 是目录可以继续查看目录下的文件。