我正试图在signin
、signup
和signout
解析器中使用具有Redis
的会话对象作为分布式系统中的存储,以设置和删除userid
的会话,但由于activx的Session
没有实现Send
,无法跨线程使用,因此存在问题。类型:Rc<RefCell<actix_session::SessionInner>>
问题
- 在
async-graphql
中处理此类问题的惯用方法是什么?我想做如下事情:
#[Object]
impl User {
async fn signin(&self, ctx: &Context<'_>) -> anyhow::Result<Vec<User>> {
let session = ctx.data_unchecked::<Session>();
session.insert("user_id", id);
session.get::<UserId>("user_id");
...
}
}
如果我尝试以上操作,我会得到:
`Rc<RefCell<actix_session::SessionInner>>` cannot be shared between threads safely
within `actix_session::Session`, the trait `Sync` is not implemented for `Rc<RefCell<actix_session::SessionInner>>`
- 此外,在异步graphql上下文中创建
session
的正确位置在哪里?我正在尝试,但会面临同样的问题:
#[post("/graphql")]
pub async fn index(
schema: web::Data<MyGraphQLSchema>,
req: HttpRequest,
gql_request: GraphQLRequest,
) -> GraphQLResponse {
let mut request = gql_request.into_inner();
let session = req.get_session();
request = request.data(session);
schema.execute(request).await.into()
}
我的应用程序:
let redis_key = Key::from(hmac_secret_from_env_var.expose_secret().as_bytes());
App::new()
.wrap(cors)
.wrap(
RedisSession::new(redis.get_url(), redis_key.master())
.cookie_http_only(true)
// allow the cookie only from the current domain
.cookie_same_site(cookie::SameSite::Lax),
)
.wrap(Logger::default())
我使用临时破解解决了这个问题。如果您检查会话定义,您会注意到它将RefCell包装如下,并且不实现send
pub struct Session(Rc<RefCell<SessionInner>>);
理想情况下,Session
应该实现sync,这样我们就可以在多线程上下文中使用它。
我目前使用的方法是用SendWrapper进行包装,这并不理想,因为当你确信包装的项目将以单线程方式使用时,这是不理想的。
use std::ops::Deref;
use actix_session::Session;
use actix_web::Error;
use send_wrapper::SendWrapper;
use uuid::Uuid;
#[derive(Clone, Debug)]
struct Shared<T>(pub Option<SendWrapper<T>>);
impl<T> Shared<T> {
pub fn new(v: T) -> Self {
Self(Some(SendWrapper::new(v)))
}
}
impl<T> Deref for Shared<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&*self.0.as_deref().clone().unwrap()
}
}
type SessionShared = Shared<actix_session::Session>;
pub struct TypedSession(SessionShared);
impl TypedSession {
const USER_ID_KEY: &'static str = "user_id";
pub fn new(session: Session) -> Self {
Self(Shared::new(session))
}
pub fn renew(&self) {
self.0.renew();
}
pub fn insert_user_uuid(&self, user_id: Uuid) -> Result<(), Error> {
self.0.insert(Self::USER_ID_KEY, user_id)
}
pub fn get_user_uuid(&self) -> Result<Option<Uuid>, Error> {
self.0.get::<Uuid>(Self::USER_ID_KEY)
}
pub fn clear(&self) {
self.0.clear()
}
}