🥠Sessions as a `tower` and `axum` middleware.
This crate provides sessions, key-value pairs associated with a site
visitor, as a tower
middleware.
It offers:
- Pluggable Storage Backends: Arbitrary storage backends are implemented
with the
SessionStore
trait, fully decoupling sessions from their storage. - An
axum
Extractor forSession
: Applications built withaxum
can useSession
as an extractor directly in their handlers. This makes using sessions as easy as includingSession
in your handler. - Common Backends Out-of-the-Box:
RedisStore
and SQLx (SqliteStore
,PostgresStore
,MySqlStore
) stores are available via their respective feature flags. - Simple Key-Value Interface: Sessions offer a key-value interface that
supports native Rust types. So long as these types are
Serialize
and can be converted to JSON, it's straightforward to insert, get, and remove any value. - Strongly-Typed Sessions: Strong typing guarantees are easy to layer on top of this foundational key-value interface.
To use the crate in your project, add the following to your Cargo.toml
file:
[dependencies]
tower-sessions = "0.1.0"
use std::net::SocketAddr;
use axum::{
error_handling::HandleErrorLayer, response::IntoResponse, routing::get, BoxError, Router,
};
use http::StatusCode;
use serde::{Deserialize, Serialize};
use time::Duration;
use tower::ServiceBuilder;
use tower_sessions::{MemoryStore, Session, SessionManagerLayer};
const COUNTER_KEY: &str = "counter";
#[derive(Default, Deserialize, Serialize)]
struct Counter(usize);
#[tokio::main]
async fn main() {
let session_store = MemoryStore::default();
let session_service = ServiceBuilder::new()
.layer(HandleErrorLayer::new(|_: BoxError| async {
StatusCode::BAD_REQUEST
}))
.layer(
SessionManagerLayer::new(session_store)
.with_secure(false)
.with_max_age(Duration::seconds(10)),
);
let app = Router::new()
.route("/", get(handler))
.layer(session_service);
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
async fn handler(session: Session) -> impl IntoResponse {
let counter: Counter = session
.get(COUNTER_KEY)
.expect("Could not deserialize.")
.unwrap_or_default();
session
.insert(COUNTER_KEY, counter.0 + 1)
.expect("Could not serialize.");
format!("Current count: {}", counter.0)
}
You can find this example as well as other example projects in the example directory.
See the crate documentation for more usage information.
This crate uses #![forbid(unsafe_code)]
to ensure everything is implemented in 100% safe Rust.
We appreciate all kinds of contributions, thank you!