2023-12-19

使用 Axum Session 构建 Rust 应用

Team Loco

要使用 Axum session 构建 Rust 应用,第一步是选择你的服务器。 在本例中,我们将使用 loco :)

首先,创建一个新项目并选择 SaaS app 模板:

$ cargo install loco
$ loco new
 ❯ App name? · myapp
? ❯ What would you like to build?
  lightweight-service (minimal, only controllers and views)
  Rest API (with DB and user auth)
 SaaS app (with DB and user auth)

创建仅内存会话存储

首先,将 Axum session crate 添加到 Cargo.toml:

axum_session = {version = "0.10.1", default-features = false}

然后,将 Axum session layer 添加到你的路由器。打开 app.rs 并添加以下钩子:

pub struct App;
#[async_trait]
impl Hooks for App {
    fn app_name() -> &'static str {
        env!("CARGO_CRATE_NAME")
    }

    // Other hooks...
    // 其他钩子...
    async fn after_routes(router: AxumRouter, _ctx: &AppContext) -> Result<AxumRouter> {
        let session_config =
            axum_session::SessionConfig::default().with_table_name("sessions_table");

        let session_store =
            axum_session::SessionStore::<axum_session::SessionNullPool>::new(None, session_config)
                .await
                .unwrap();

        let router = router.layer(axum_session::SessionLayer::new(session_store));
        Ok(router)
    }
    // Other hooks...
    // 其他钩子...
}

现在,你可以创建使用 Axum session 的控制器。使用 cargo loco generate controller 命令:

 cargo loco generate controller mysession --api
    Finished dev [unoptimized + debuginfo] target(s) in 0.36s
     Running `target/debug/axum-session-cli generate controller mysession`
added: "src/controllers/mysession.rs"
injected: "src/controllers/mod.rs"
injected: "src/app.rs"
added: "tests/requests/mysession.rs"
injected: "tests/requests/mod.rs"

打开控制器生成器创建的 src/controllers/mysession.rs 文件,并将内容替换为以下代码:

#![allow(clippy::unused_async)]
use axum_session::{Session, SessionNullPool};
use loco_rs::prelude::*;

pub async fn get_session(session: Session<SessionNullPool>) -> Result<()> {
    println!("{:#?}", session);
    format::empty()
}

pub fn routes() -> Routes {
    Routes::new().prefix("mysession").add("/", get(get_session))
}

现在,你可以调用 http://127.0.0.1:5150/mysession 端点来查看会话。

创建带数据库加密的会话

要添加会话数据库加密,请在 Cargo.toml 中包含 Axum session crate 和带有 SQLx 的 PostgreSQL:

axum_session = {version = "0.10.1"}
sqlx = { version = "0.7.2", features = [
  "macros",
  "postgres",
  "_unstable-all-types",
  "tls-rustls",
  "runtime-tokio",
] }

创建一个名为 session.rs 的文件,内容如下: connect_to_database 函数接收 Database 配置并返回 Axum session 所期望的 PgPool 实例。

use sqlx::postgres::PgPool;
use loco_rs::{
    config::Database,
    errors::Error,
    Result,
};

async fn connect_to_database(config: &Database) -> Result<PgPool> {
    PgPool::connect(&config.uri)
        .await
        .map_err(|e| Error::Any(e.into()))
}

将 Axum session layer 添加到 app.rs 中的路由器:

use session; // This is the session.rs file
              // 这是 session.rs 文件
pub struct App;
#[async_trait]
impl Hooks for App {
    fn app_name() -> &'static str {
        env!("CARGO_CRATE_NAME")
    }

    // Other hooks...
    // 其他钩子...
    async fn after_routes(router: AxumRouter, ctx: &AppContext) -> Result<AxumRouter> {
        let conn = session.connect_to_database(&ctx.config.database).await?;
        let session_config = axum_session::SessionConfig::default()
            .with_table_name("sessions_table")
            .with_key(axum_session::Key::generate())
            .with_database_key(axum_session::Key::generate())
            .with_security_mode(axum_session::SecurityMode::PerSession);

        let session_store = axum_session::SessionStore::<axum_session::SessionPgPool>::new(
            Some(conn.clone().into()),
            session_config,
        )
        .await
        .unwrap();

        let router = router.layer(axum_session::SessionLayer::new(session_store));
        Ok(router)
    }
    // Other hooks...
    // 其他钩子...
}

像之前一样使用 cargo loco generate controller 创建控制器

 cargo loco generate controller mysession --api
    Finished dev [unoptimized + debuginfo] target(s) in 0.36s
     Running `target/debug/axum-session-cli generate controller mysession`
added: "src/controllers/mysession.rs"
injected: "src/controllers/mod.rs"
injected: "src/app.rs"
added: "tests/requests/mysession.rs"
injected: "tests/requests/mod.rs"

并将 src/controllers/mysession.rs 的内容替换为提供的代码。

#![allow(clippy::unused_async)]
use axum_session::{Session, SessionPgPool};
use loco_rs::prelude::*;

pub async fn get_session(session: Session<SessionPgPool>) -> Result<()> {
    println!("{:#?}", session);
    format::empty()
}

pub fn routes() -> Routes {
    Routes::new().prefix("mysession").add("/", get(get_session))
}

现在,调用 http://127.0.0.1:5150/mysession 端点将显示会话。