路由

983741
2021/11/13 07:55:05

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 请求的路由。

链式操作

定义相关的 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 提供了嵌套路由功能。比如有以下路由:

通过嵌套路由可以使其更清晰:

定义 handlers:

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)
}

定义子路由:

let news_router = Router::new()
        .route("/", get(news_index))
        .route("/detail/:id", get(news_detail))
        .route("/comments/:id", get(news_comments));

将子路由嵌到全局路由:

.nest("/news", news_router);

本章讲解了 axum 路由功能。代码可以在代码仓库中找到。

思考题:

如何在 axum 实现跳转(即重定向)功能?

提示:

  1. 使用 (StatusCode, HeaderMap, ()) 响应
  2. 对应的状态码可查阅这里
  3. 对应的 Header 可查阅这里
async fn redirect() -> (StatusCode, HeaderMap, ()) {
    let mut headers = HeaderMap::new();
    headers.insert(
        axum::http::header::LOCATION,
        "https://axum.rs".parse().unwrap(),
    );
    (StatusCode::FOUND, headers, ())
}