Is it possible to put a grpc client in the Application State and then pull it into the handler?

70 Views Asked by At

Gateway microservice on Actix-Web. Is it possible to put a grpc client in the Application State and then pull it into the handler? To avoid creating it in every handler. If possible, how can I do it?

How do I specify the type?

[main.rs]

#[actix_rt::main]
async fn main() -> std::io::Result<()> {
...
    let channel = Channel::from_static("http://127.0.0.1:5051").connect().await.unwrap();
    let mut user_client = UserServiceClient::with_interceptor(channel, user_client_handler);

    let shared_data = web::Data::new(State {
        user_client: **user_client**,
    });
...
}

fn user_client_handler(mut req: Request<()>) -> Result<Request<()>, Status> {
    let token = AsciiMetadataValue::from_static("...");
    req.metadata_mut().insert("authorization", token);
    Ok(req)
}

pub struct State {
    pub user_client: **?**,
}

[user_handler.rs]

pub async fn get_users(path: web::Path<(i64,i64)>, state: web::Data<State>,) -> Result<HttpResponse, AppError> {
    let request = tonic::Request::new(...);
    let mut client = UserServiceClient::connect("http://127.0.0.1:5051").await?;
    let mut grpc_response = client.list_users(request).await?.into_inner();
    let json_response: UsersListResponse = grpc_response.into();
    Ok(HttpResponse::Ok().json(json_response))
}

Updated Error message:

error[E0308]: mismatched types
  --> src/main.rs:59:22
   |
59 |         user_client: user_client,
   |                      ^^^^^^^^^^^ expected fn pointer, found fn item
   |
   = note: expected struct `UserServiceClient<tonic::service::interceptor::InterceptedService<_, fn(tonic::Request<_>) -> Result<_, _>>>`
              found struct `UserServiceClient<tonic::service::interceptor::InterceptedService<_, fn(tonic::Request<_>) -> Result<_, _> {user_client_handler}>>`

UPDATE 2024.03.06

    //[main.rs]
    let channel = Channel::from_static("http://127.0.0.1:5051").connect().await.unwrap();
    let mut user_client = UserServiceClient::with_interceptor(channel, token_interceptor);
    let shared_data = web::Data::new(State {
        user_client: ???,
    });

    //[state.rs]
    pub struct State {
        pub user_client: UserServiceClient<Box<dyn Fn(Request<()>) -> Result<Request<()>,Status>>>,
        //pub user_client: UserServiceClient<InterceptedService<Channel, fn(Request<()>) -> Result<Request<()>, Status>>>,
    }

Error

error[E0308]: mismatched types
  --> src/main.rs:58:22
   |
58 |         user_client: user_client,
   |                      ^^^^^^^^^^^ expected `UserServiceClient<Box<dyn Fn(...) -> ...>>`, found `UserServiceClient<InterceptedService<..., ...>>`
   |
   = note: expected struct `UserServiceClient<Box<(dyn Fn(tonic::Request<()>) -> Result<tonic::Request<()>, tonic::Status> + 'static)>>`
              found struct `UserServiceClient<tonic::service::interceptor::InterceptedService<Channel, fn(tonic::Request<()>) -> Result<tonic::Request<()>, tonic::Status> {token_interceptor}>>`

1

There are 1 best solutions below

4
Oussama Gammoudi On

Change the closure to a seperate function

fn user_client_handler(mut req: Request<()>) -> Result<Request<()>, Status> {
    req.metadata_mut().insert("authorization", token);
    Ok(req)
}
let mut user_client = UserServiceClient::with_interceptor(channel,user_client_handler);

This is because in your definition you used function pointer

fn(Request<()>) -> Result<Request<()>, Status>

Notice the lower case "fn", if you want to use closure you will need to use

impl Fn(Request<()>) -> Result<Request<()>, Status>

Update:

pub struct State<T: Fn(Request<()>) -> Result<Request<()>, Status>> {
    pub user_client: UserServiceClient<T>,
}

pub struct State {
    pub user_client: UserServiceClient<Box<dyn Fn(Request<()>) -> Result<Request<()>,Status>>>,
}

You'll have to either use Generics T or Box dyn