在axum中使用Mutex时如何解决“future returned by is not `Send`”?

问题描述 投票:0回答:1

我想使用 PKCE 在 axum 中实现授权代码流程。因此,我必须将生成的 PKCE 代码验证程序移交给回调路由,以便将代码交换为令牌,以便我可以继续使用会话等方式登录我的用户。

现在我主要使用锤子,只是为了描述为什么解决方案看起来像这样:

#[derive(Clone)]
struct AppState {
    db: PgPool,
    oauth_client: BasicClient,
    verifiers: Arc<Mutex<HashMap<String, String>>>,
}

#[debug_handler]
async fn callback(
    State(state): State<AppState>,
    Query(auth_request): Query<AuthRequest>,
) -> Result<impl IntoResponse, impl IntoResponse> {
    let auth_request = auth_request;

    let verifiers = state.verifiers.lock().unwrap();
    let pkce_verifier = verifiers.get(&auth_request.state).unwrap().into();
    let pkce_verifier = PkceCodeVerifier::new(pkce_verifier);

    let _token_result = match state
        .oauth_client
        .exchange_code(AuthorizationCode::new(auth_request.code))
        .set_pkce_verifier(pkce_verifier)
        .request_async(async_http_client)
        .await
    {
        Ok(res) => res,
        Err(e) => {
            error!("could not exchange code: {e}");
            return Err((StatusCode::INTERNAL_SERVER_ERROR, e.to_string()));
        }
    };

    Ok(Redirect::temporary("/"))
}

错误如下:

  --> src/main.rs:134:10
    |
125 |     let verifiers = state.verifiers.lock().unwrap();
    |         --------- has type `std::sync::MutexGuard<'_, std::collections::HashMap<std::string::String, std::string::String>>` which is not `Send`
134 |         .await
    |          ^^^^^ await occurs here, with `verifiers` maybe used later

我知道互斥锁上的锁保持时间太长。我试图理解很多类似的问题,但我还无法理解这一点。

rust async-await mutex rust-axum
1个回答
3
投票

Rust 的

std::sync::Mutex
无法跨过
await
点,这就是您看到
type is not Send
编译错误的原因。您有两个选择:

  1. 在到达

    await
    点之前放下锁。您只需使用
    verifiers
    即可获得
    pkce_verifier
    ,因此您可以内联它:

    let pkce_verifier = state
        .verifiers
        .lock()
        .unwrap()
        .get(&auth_request.state)
        .unwrap()
        .into();
    

    这有点难看,所以你也可以手动调用锁上的

    drop()

    let verifiers = state.verifiers.lock().unwrap();
    let pkce_verifier = verifiers.get(&auth_request.state).unwrap().into();
    drop(verifiers);
    

    或者,您可以在内部块作用域内创建和使用锁,当块退出作用域时,它将自动删除锁:

    let pkce_verifier = {
        let verifiers = state.verifiers.lock().unwrap();
        verifiers.get(&auth_request.state).unwrap().into()
    };
    
  2. 您可以使用异步感知锁,例如

    futures::lock::Mutex
    。如果您认为将来需要跨
    await
    点使用互斥体,您可以切换到异步感知互斥体。否则,只要锁没有跨过
    await
    点,标准库互斥体就可以工作。


顺便说明一下,除非您确定该字段存在,否则在对象上调用

.unwrap()
是不好的做法。特别是在
HashMap
的情况下,您应该使用模式匹配或 Rust 提供的内置
Option
-to-
Result
函数,例如
ok_or()

© www.soinside.com 2019 - 2024. All rights reserved.