blog.mahoroi.com

actix-webのFromRequestでasyncを使う

-

Rust 製のフレームワークである Actix に含まれる FromRequest。この trait を継承した impl のメソッド from_request 内で async 関数を使いたいときの備忘録です。

リクエストハンドラーが取りうる User struct を作り、FromRequestUser に継承させます。is_authorized が async 関数です。

src/models/user.rs
use actix_web::dev::Payload;
use actix_web::error::ErrorUnauthorized;
use actix_web::{Error, FromRequest, HttpRequest};
use serde::{Deserialize, Serialize};
use std::future::Future;
use std::pin::Pin;

#[derive(Deserialize, Serialize, Debug)]
pub struct User {
  pub uid: String,
}

impl FromRequest for User {
  type Error = Error;
  type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
  type Config = ();

  fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
    Box::pin(async move {
      // async function
      match is_authorized("token_value").await {
        Ok(_) => Ok(User {
          uid: "abcde".to_string(),
        }),
        Err(_) => Err(ErrorUnauthorized("Unauthorized")),
      }
    })
  }
}

async fn is_authorized(_: &str) -> Result<(), ()> {
  Ok(())
}
src/main.rs
#[get("/me")]
async fn me(user: models::user::User) -> HttpResponse {
  HttpResponse::Ok().json(user)
}
// => { "uid": "abcde" }

ちなみに Box::pin を使わなければ以下のようなエラーによりコンパイルできません。

src/models/user.rs
impl FromRequest for User {
  type Error = Error;
  type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
  type Config = ();

  fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
    // async fn is_authorized(_: &str) -> Result<(), ()>
    // `await` is only allowed inside `async` functions and blocks
    // only allowed inside `async` functions and blocksrustc(E0728)
    match is_authorized("token_value").await {
      Ok(_) => Ok(User {
        uid: "user_id".to_string(),
      }),
      Err(_) => Err(ErrorUnauthorized("Unauthorized")),
    }
  }
}

参考