域名 AXUM.RS 将于2025年10月到期。我们无意再对其进行续费,我们希望你能够接续这个域名,让更多 AXUM 开发者继续受益。
  • 方案1️⃣AXUM.RS 域名 = 3000
  • 方案2️⃣方案1️⃣ + 本站所有专题原始 Markdown 文档 = 5000
  • 方案3️⃣方案2️⃣ + 本站原始数据库 = 5500
如果你有意接续这份 AXUM 情怀,请与我们取得联系。
说明:
  1. 如果有人购买 AXUM.RS 域名(方案1️⃣),或者该域名到期,本站将启用新的免费域名继续提供服务。
  2. 如果有人购买了 AXUM.RS 域名,且同时购买了内容和/或数据库(方案2️⃣/方案3️⃣),本站将关闭。届时我们或许会以另一种方式与你再相遇。

axum处理cookie

本章示例将实现以下路由:

路由说明
GET /用户中心首页。如果用户未登录,显示提示信息;如果用户已登录,显示欢迎信息
GET /login用户登录表单
POST /login用户登录处理。如果用户名和密码正确,设置 Cookie 并跳转到用户中心首页
GET /logout退出登录

为了突出重点,用户登录表单就不在这里展示了,你可以通过源代码查看。

为了突出重点,用户登录表单就不在这里展示了,你可以通过源代码查看。

用户登录

async fn user_login_action(Form(frm): Form<UserLoginForm>) -> (StatusCode, HeaderMap, ()) {
    let mut headers = HeaderMap::new();
    if !(&frm.username == "axum.rs" && &frm.password == "axum.rs") {
        headers.insert(
            axum::http::header::LOCATION,
            "/login?msg=用户名或密码错误".parse().unwrap(),
        ); // 跳转到登录页面
    } else {
        let cookie = format!("{}={}", COOKIE_NAME, frm.username);
        headers.insert(
            axum::http::header::SET_COOKIE,
            cookie.as_str().parse().unwrap(),
        ); // 设置Cookie
        headers.insert(axum::http::header::LOCATION, "/".parse().unwrap()); // 跳转到用户中心首页
    }
    (StatusCode::FOUND, headers, ())
}

首先,检查用户名和密码是否是axum.rs,如果不是,跳转到用户登录的表单页面,并附带提示信息。

如果用户名和密码正确,先设置 Cookie,注意,设置 Cookie 的 Header 名是 SET_COOKIE;然后跳转到用户中心首页。

用户中心首页

对于未登录的用户,用户中心首页将提示错误信息。相应的,对于已登录用户,则从 Cookie 取出登录用户的用户名,并显示欢迎信息。

async fn user_center(headers: HeaderMap) -> Result<Html<String>, &'static str> {
    let cookies = headers
        .get(axum::http::header::COOKIE)
        .and_then(|v| v.to_str().ok())
        .map(|v| v.to_string())
        .unwrap_or("".to_string()); // 从请求头获取所有COOKIE
    if cookies.is_empty() {
        return Err("NO COOKIE SETTED"); // 没有 Cookie
    }
    let mut logined_username: Option<String> = None;
    let cookies: Vec<&str> = cookies.split(';').collect(); // 多个cookie用;分割
    for cookie in cookies {
        let cookie_pair: Vec<&str> = cookie.split('=').collect(); // 每个cookie都是用=分割的键值对
        let cookie_name = cookie_pair[0].trim();
        let cookie_value = cookie_pair[1].trim();
        // 如果 cookie 的名称是我们希望的,并且值不为空
        if cookie_name == COOKIE_NAME && !cookie_value.is_empty() {
            logined_username = Some(String::from(cookie_value)); // 设置已登录用户的用户名
            break;
        }
    }
    if logined_username.is_none() {
        return Err("COOKIE IS EMPTY"); // 没有我们需要的cookie
    }
    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>
          <p>你好,<strong>{}</strong>!你已成功登录。[<a href="/logout">退出登录</a>]
          </body>
          </html>
        "#,
        logined_username.unwrap()
    );
    Ok(Html(html))
}

因为我们要通过 HTTP Header 获取 Cookie,所以该处理器的参数是一个包含所有 Header 的集合。

首先,获取 Cookie。如果 HTTP Header 中没有 Cookie,直接返回错误信息。和写入不同,读取 Cookie 时,使用的 Header 名是 COOKIE

遍历已拆分的 Cookie,如果名称是我们期望的,并且值不为空,说明用户已登录,这时候返回欢迎信息;相反,返回错误信息。

退出登录

退出登录也是设置 Cookie,只是把 Cookie 的值设置为空。

async fn user_logout() -> (StatusCode, HeaderMap, ()) {
    let cookie = format!("{}=", COOKIE_NAME);
    let mut headers = HeaderMap::new();
    headers.insert(
        axum::http::header::SET_COOKIE,
        cookie.as_str().parse().unwrap(),
    ); // 清空Cookie
    headers.insert(axum::http::header::LOCATION, "/login".parse().unwrap()); // 跳转到登录页面
    (StatusCode::FOUND, headers, ())
}

本章通过一个模拟的用户中心演示了如何读写 Cookie。完整代码可以在我们的代码库中找到。

Cookie 中间件

思考题

  1. 在登录的时候,如果用户输入的用户名或密码错误,会跳转到登录表单页面,并附带一个提示信息。我们并没有获取这个提示信息并进行处理。请你动动手,获取并处理这个提示信息。

  2. 回想一下在讲解 axum 请求时,我们说到 axum 提供了TypedHeader来简化操作,试试看能不能用它来简化 Cookie 的读取。

在登录的时候,如果用户输入的用户名或密码错误,会跳转到登录表单页面,并附带一个提示信息。我们并没有获取这个提示信息并进行处理。请你动动手,获取并处理这个提示信息。

回想一下在讲解 axum 请求时,我们说到 axum 提供了TypedHeader来简化操作,试试看能不能用它来简化 Cookie 的读取。

要查看完整内容,请先登录