#session-cookies #session #rocket #rocket-rs

rocket_session

Rocket.rs 插件,用于基于cookie的会话以存储任意数据

5个不稳定版本

0.3.0 2023年1月10日
0.2.2 2021年1月24日
0.2.1 2020年2月9日
0.2.0 2019年12月31日
0.1.1 2019年12月31日

#501HTTP服务器

Download history 18/week @ 2024-02-28 5/week @ 2024-03-06 5/week @ 2024-03-27 7/week @ 2024-04-03

82 每月下载量

MIT 许可证

17KB
226 代码行

Rocket.rs会话

使用此crate,将基于cookie的会话添加到Rocket应用程序中非常简单。

会话用于在相关请求之间共享数据,例如用户身份验证、购物车、用于重新填充的验证失败的表单值等。

配置

实现是通用的,支持任何类型的会话数据:自定义结构体、StringHashMap,或可能serde_json::Value。您可以根据需要选择。

会话生命周期、cookie名称和其他参数可以通过在公平处理程序上调用链式方法进行配置。当会话过期时,与其关联的数据将被丢弃。

示例:Session::fairing().with_lifetime(Duration::from_secs(15))

使用方法

要在路由中使用会话,首先确保在启动时通过调用rocket.attach(Session::fairing())将公平处理程序附加到程序中,然后将类似于session : Session的内容添加到路由(们)的参数列表中。其余的(会话初始化、过期、cookie管理)都由系统在幕后处理。

在会话上下文中运行的闭包中访问会话数据,使用session.tap()方法。此闭包在会话级别的互斥锁内运行,避免了来自不同请求的同时修改。尽量避免在闭包中执行长时间操作,因为它会阻止客户端对启用了会话的路由的任何其他请求。

对启用会话的路由的每个请求都会将会话的寿命扩展到配置的全时长(默认为1小时)。自动清理会删除过期的会话,以确保会话列表不会浪费内存。

示例

更多示例在 "examples" 文件夹中 - 使用 cargo run --example=NAME 运行。

基本示例

这个简单的示例使用 u64 作为会话变量;请注意,它可以是一个结构体、映射或其他任何东西,只需实现 Send + Sync + Default

#[macro_use]
extern crate rocket;

type Session<'a> = rocket_session::Session<'a, u64>;

#[launch]
fn rocket() -> _ {
    rocket::build()
        .attach(Session::fairing())
        .mount("/", routes![index])
}

#[get("/")]
fn index(session: Session) -> String {
    let count = session.tap(|n| {
        // Change the stored value (it is &mut)
        *n += 1;

        // Return something to the caller.
        // This can be any type, 'tap' is generic.
        *n
    });

    format!("{} visits", count)
}

通过特质扩展会话

.tap() 方法功能强大,但有时您可能希望有更方便的方法。

以下是一个使用自定义特性和 json_dotpath 包来基于 serde 序列化实现多态存储的示例。

请注意,如果您在每个请求中多次访问会话对象,这种方法可能会导致数据竞争,因为每个方法都包含它自己的 .tap()。可能更安全的是在单个共享闭包中手动调用 .dot_*() 方法。

use serde_json::Value;
use serde::de::DeserializeOwned;
use serde::Serialize;
use json_dotpath::DotPaths;

pub type Session<'a> = rocket_session::Session<'a, serde_json::Map<String, Value>>;

pub trait SessionAccess {
    fn get<T: DeserializeOwned>(&self, path: &str) -> Option<T>;

    fn take<T: DeserializeOwned>(&self, path: &str) -> Option<T>;

    fn replace<O: DeserializeOwned, N: Serialize>(&self, path: &str, new: N) -> Option<O>;

    fn set<T: Serialize>(&self, path: &str, value: T);

    fn remove(&self, path: &str) -> bool;
}

impl<'a> SessionAccess for Session<'a> {
    fn get<T: DeserializeOwned>(&self, path: &str) -> Option<T> {
        self.tap(|data| data.dot_get(path))
    }

    fn take<T: DeserializeOwned>(&self, path: &str) -> Option<T> {
        self.tap(|data| data.dot_take(path))
    }

    fn replace<O: DeserializeOwned, N: Serialize>(&self, path: &str, new: N) -> Option<O> {
        self.tap(|data| data.dot_replace(path, new))
    }

    fn set<T: Serialize>(&self, path: &str, value: T) {
        self.tap(|data| data.dot_set(path, value));
    }

    fn remove(&self, path: &str) -> bool {
        self.tap(|data| data.dot_remove(path))
    }
}

依赖关系

~15–47MB
~770K SLoC