'actix-web: Add data to request in middleware

I am learning actix-web, I parsed jwt in middleware, I want to pass the data in jwt to the controller that parses to handle this request, but I don't know how to do it

my middleware:

use actix_web::{error::ErrorUnauthorized, Error};
use std::future::{ready, Ready};

use actix_web::dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform};
use futures_util::future::LocalBoxFuture;

pub struct JWTAuth;
impl<S, B> Transform<S, ServiceRequest> for JWTAuth
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type InitError = ();
    type Transform = JWTAuthHiMiddleware<S>;
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ready(Ok(JWTAuthHiMiddleware {
            service,
            verification_path: vec!["/api"],
            noverification_path: vec!["/api/auth"],
        }))
    }
}

pub struct JWTAuthHiMiddleware<S> {
    service: S,
    verification_path: Vec<&'static str>,
    noverification_path: Vec<&'static str>,
}

impl<S, B> JWTAuthHiMiddleware<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    fn is_need_verification(&self, path: &str) -> bool {
        self.verification_path
            .iter()
            .any(|&vp| path.starts_with(vp))
            && !self
                .noverification_path
                .iter()
                .any(|&vp| path.starts_with(vp))
    }
}

impl<S, B> Service<ServiceRequest> for JWTAuthHiMiddleware<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

    forward_ready!(service);

    fn call(&self, req: ServiceRequest) -> Self::Future {
        if self.is_need_verification(req.path()) {
            let authorization = req.headers().get("Authorization");
            if authorization.is_none() {
                return Box::pin(async { Err(ErrorUnauthorized("err")) });
            }

            let authorization = authorization.unwrap().to_str();
            if authorization.is_err() {
                return Box::pin(async { Err(ErrorUnauthorized("err")) });
            }

            let authorization = authorization.unwrap();
            let token = &authorization[7..]; // 'Bearer ' + token
            let token_data = crate::utils::jwt::Claims::decode(token);

            if let Err(err) = token_data {
                return Box::pin(async { Err(ErrorUnauthorized(err)) });
            }

            let token_data = token_data.unwrap();
            
            // I need to pass this user_id to the next Handle
            println!("user_id: {}", &token_data.claims.user_id);
        }

        let fut = self.service.call(req);

        Box::pin(async move {
            let res = fut.await?;
            Ok(res)
        })
    }
}

I want to get this user_id data in the controller

#[get("/user")]
pub async fn list(req: actix_web::HttpRequest, pool: web::Data<DbPool>) -> HttpResult {
    
    // This is the way I guess, the right way I don't know how to do it
    req.user_id;

    Ok(HttpResponse::Ok().json(req.user_id))
}


Solution 1:[1]

Refer to this example

https://github.com/actix/examples/tree/master/middleware/middleware-ext-mut

let token_data = token_data.unwrap();
req.extensions_mut().insert(token_data.claims);
pub async fn list(claims: Option<web::ReqData<Claims>>, pool: web::Data<DbPool>) -> HttpResult {
    if let Some(claims) = claims {
        log::info!("user_id: {:?}", claims.user_id);
    }

...

}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 januw a