Rust Web 开发 I

后端框架 Axum

技术
技术 Rust Web 开发 后端框架

2026-04-04

什么是 Web 后端开发

Web 浏览器通过超文本传输协议(HTTP 或 HTTPS)与 Web 服务器进行通信。当用户加载一个网址、点击一个链接或提交一个表单时,浏览器通常会向目标服务器发送一个 HTTP 请求。

Web 服务器接收客户端发送的请求,在处理请求后返回一个 HTTP 响应。

Web 浏览器根据服务器返回的响应内容解析资源,并将其呈现给用户。

在浏览器端(或称客户端)运行的代码主要负责网页的结构、外观以及交互行为,例如 UI 元素设计、页面布局、导航逻辑和表单验证等。这部分职责称为 Web 前端开发。

相对而言,服务器端网站编程主要负责处理客户端请求、执行业务逻辑,并决定返回给浏览器的内容,例如数据库访问、身份认证和权限控制等。这部分职责称为 Web 后端开发。

Rust 社区围绕异步运行时 Tokio 建立了一个功能强大的 Web 开发生态,它由一系列库构成,包含 Tonic、Tower、Tracing、Axum 等。其中作为 Web 后端框架的是 Axum。

Axum 的核心概念

Axum 的核心概念包括路由、处理器、提取器、响应和中间件。

路由(routing)用于确定应用程序如何响应对特定端点的客户机请求,此请求包含一个 URI(或路径)和一个特定的 HTTP 请求方法(GETPOST 等)。在 Axum 中,路由负责设置路径和 HTTP 请求方法与服务之间的对应关系。

use axum::{Router, routing::get};

// 这里设置了一个路由
let app = Router::new()
    // 请求为 `GET /` 时调用 `root` 处理请求
    .route("/", get(root))
    // 请求为 `GET /foo` 时调用 `get_foo` 处理请求
    // `POST /foo` 时调用 `post_foo` 处理请求
    .route("/foo", get(get_foo).post(post_foo))
    // 请求为 `GET /foo/bar` 时调用 `foo_bar` 处理请求
    .route("/foo/bar", get(foo_bar));

// 以下是会被调用的处理器
async fn root() {}
async fn get_foo() {}
async fn post_foo() {}
async fn foo_bar() {}

处理器(handlers)负责处理 HTTP 请求,并执行核心的业务逻辑,然后返回对客户端的响应。在 Axum 中,处理器是这样的一个异步函数:它接受零个或多个提取器作为参数,并返回可以转换为响应的类型。

use axum::{body::Bytes, http::StatusCode};

/// 一个只返回空的 `200 OK` 响应的处理器
async fn unit_handler() {}

/// 返回一个内容为纯文本的 `200 OK` 响应的处理器
///
/// `String` 类型实现了 `IntoResponse`,因此可以作为处理器的返回类型
async fn string_handler() -> String {
    "Hello, World!".to_string()
}

/// 接收请求体内容并将之处理为 UTF-8 纯文本
/// 若处理成功则返回 `Result` 包装的纯文本
/// 若处理失败则返回 `400 Bad Request`
/// 
/// `String` 和 `StatusCode` 类型都实现了 `IntoResponse`
/// 因此 `Result<String, StatusCode>` 可以作为处理器的返回类型
async fn echo(body: Bytes) -> Result<String, StatusCode> {
    if let Ok(string) = String::from_utf8(body.to_vec()) {
        Ok(string)
    } else {
        Err(StatusCode::BAD_REQUEST)
    }
}

提取器(extractors)是一种用于从 HTTP 请求的不同部分(如 URL 路径、查询参数、请求头、请求体等)中提取数据的工具或机制。在 Axum 中,提取器是一种实现了 FromRequestFromRequestParts 的类型,它用于解析传入的请求并获取处理器所需的各个部分。

use axum::extract::{Path, Query, Json};
use std::collections::HashMap;

/// `Path` 提取器会从请求中提取出路径参数
/// 将之反序列化并传入处理器
async fn path(Path(user_id): Path<u32>) {}

/// `Query` 提取器会提取出查询参数
/// 将之反序列化并传入处理器
async fn query(Query(params): Query<HashMap<String, String>>) {}

/// `Json` 提取器会把请求体放入缓冲区
/// 并将其作为 JSON 反序列化为 `serde_json::Value`
/// 此提取器支持反序列化任何实现 `serde::Deserialize` 的类型
async fn json(Json(payload): Json<serde_json::Value>) {}

响应(responses)是指服务器对客户端请求的答复或反馈。在 Axum 中,响应是任何实现了 IntoResponse 的类型,只要是响应都可以从处理器中返回。

use axum::{
    body::Body,
    routing::get,
    response::Json,
    Router,
};
use serde_json::{Value, json};

/// `&'static str` 类型会转为这样的一个响应:
/// 它包含 `200 OK` 状态码和 `content-type: text/plain; charset=utf-8` 响应头
async fn plain_text() -> &'static str {
    "foo"
}

/// `Json` 类型会转换成这样一个响应:
/// 响应头是 `application/json`
///
/// 它支持任何实现 `serde::Serialize` 的类型
async fn json() -> Json<Value> {
    Json(json!({ "data": 42 }))
}

// 创建一个路由来使用以上处理器
let app = Router::new()
    .route("/plain_text", get(plain_text))
    .route("/json", get(json));

中间件(middleware)是一种用于处理请求和响应的组件或函数,可以对客户端请求和服务器响应进行拦截、修改、增强或终止。正如其名,它位于客户端请求与服务器最终处理逻辑之间,或者服务器生成的响应与客户端接收之间,起到一个“中介”的作用。在 Axum 中,中间件很多时候都依赖 tower 生态。


总结一下,响应就是服务端收到请求后,经过一系列工序,最终要返回给客户端的内容;路由就是一个请求分发的中枢,它是请求抵达服务端的第一站,决定何种请求应该由哪家处理;中间件是路由分发后先对请求进行预处理的模块(如果有的话),也可以在向客户端传回之前对响应进行再处理;处理器就是正式处理请求的地方,提取器会辅助处理器工作,把请求相关的参数传给处理器,然后由处理器返回响应。

参考资料