How can I pass multiple values of the same type with App::data() in actix-web?

1.9k views Asked by At

The official example code:

/// use std::cell::Cell;
/// use actix_web::{web, App, HttpResponse, Responder};
///
/// struct MyData {
///     counter: Cell<usize>,
/// }
///
/// async fn index(data: web::Data<MyData>) -> impl Responder {
///     data.counter.set(data.counter.get() + 1);
///     HttpResponse::Ok()
/// }
///
/// let app = App::new()
///     .data(MyData{ counter: Cell::new(0) })
///     .service(
///         web::resource("/index.html").route(
///             web::get().to(index)));
/// 
pub fn data<U: 'static>(mut self, data: U) -> Self {
    self.data.push(Box::new(Data::new(data)));
    self
}

My question is how to pass multi variables ? which one I got from argument?

let app = App::new()
         .data(MyData{ counter: Cell::new(0) })       // <-- multi 
         .data(MyData{ counter: Cell::new(100) })     // <-- multi 
         .data(MyData{ counter: Cell::new(200) })     // <-- multi 
         .service(
             web::resource("/index.html").route(
                 web::get().to(index)));

// which MyData is this data assign to?
async fn index(data: web::Data<MyData>) -> impl Responder {
    data.counter.set(data.counter.get() + 1);
    HttpResponse::Ok()
}

Which MyData is this data assign to?

1

There are 1 answers

0
Jimmie Fulton On BEST ANSWER

I believe that web::data is distinguished by type. You would need to create a new type for each piece of data you'd like to get access to.

If you want to create multiple instances of the same type, you'll need to use the "New Type" pattern. See here for an example: https://doc.rust-lang.org/rust-by-example/generics/new_types.html

In essence, the "instance" of data, and it's name, are one in the same. In a language like java, you might take an approach where there is a map of names to instances, and you'd need to cast to the type of object you expect as the value. In this case, it's strictly keyed on the type itself.

Here is a working example that demonstrates how to have one definition of a Counter, with multiple instances of it. Note that there is a different set of counters per thread:

use std::cell::Cell;
use actix_web::{get, web, App, HttpResponse, HttpServer, Responder};

struct Counter {
    counter: Cell<usize>,
}

impl Counter {
    pub fn new(value: usize) -> Counter {
        Counter { counter: Cell::new(value) }
    }

    pub fn increment(&self) -> usize {
        self.counter.set(self.counter.get() + 1);
        self.counter.get()
    }
}

struct SuccessCounter(Counter);

struct FailureCounter(Counter);

#[get("/")]
async fn index(success_counter: web::Data<SuccessCounter>,
               failure_counter: web::Data<FailureCounter>) -> impl Responder {
    println!("Successes: {}", success_counter.0.increment());
    println!("Failures: {}", failure_counter.0.increment());
    HttpResponse::Ok()
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .data(SuccessCounter(Counter::new(0)))
            .data(FailureCounter(Counter::new(100)))
            .service(index)
    })
        .bind("127.0.0.1:8080")?
        .run()
        .await
}