Actix-web noob: using extractors and app state in the same handler?

I'm trying to build something based on examples in the actix-web repo.

This code works fine

use actix_web::{
    http,
    middleware::Logger,
    server, App, HttpRequest, Json, Result,
};

#[derive(Clone)]
struct State {
    val: u32,
}

fn data(input: Json<input::Input>, /* req: &HttpRequest<State> */) -> Result<String> {
    let mut v: u32 = 0;
    // let v = req.state().val;
    Ok(format!("got data {}", v))
}

fn main() {
    server::new(|| {
        App::with_state(State {
            val: 10
        })
        .middleware(Logger::default())
        .resource("/data", |r| r.method(http::Method::POST).with(data))
    })
    .bind("127.0.0.1:8088")
    .unwrap()
    .run();
}

mod input;

but if I uncomment the state args and usage in fn data:

fn data(input: Json<input::Input>, req: &HttpRequest<State>) -> Result<String> {
    let mut v: u32 = 0;
    let v = req.state().val;
    Ok(format!("got data {}", v))
}

I get a gnarly error on the .with() in the routing that I can't budge.

the trait bound for<'r> fn(actix_web::json::Json<input::Input>, &'r actix_web::httprequest::HttpRequest<State>) -> std::result::Result<std::string::String, actix_web::error::Error> {data}: actix_web::with::WithFactory<_, State, _> is not satisfied

the trait actix_web::with::WithFactory<_, State, _> is not implemented for for<'r> fn(actix_web::json::Json<input::Input>, &'r actix_web::httprequest::HttpRequest<State>) -> std::result::Result<std::string::String, actix_web::error::Error> {data}

Clues very welcome. The examples are somewhat inconsistent with eachother and there seem to so so many different ways of doing things, but none are coming together.

Try
fn data((input, req): (Json<input::Input>, HttpRequest<State>)) ->Result<String>

Alas, no. That gives me the same error rearranged as a tuple.

I should have mentioned more explicitly some of the other things I had tried to budge the error; I had seen some examples in that shape and tried it too.

I had also seen the example using JsonBody on the request (so I could take just the one HttpRequest parameter and use it for both). Taking that directly from the documentation page and just adjusting the json deserialised type:

fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
    req.json().from_err()
        .and_then(|val: input::Input| {
            println!("model: {:?}", val);
            Ok(HttpResponse::Ok().json(val))  // <- send response
        })
        .responder()
}

I first had to dig around trying to find yet another trait where the .responder() method came from, and then I get exactly the same kind of error from with_async() for a function that is not yet even trying to use the state mechanism:

the trait bound for<'r> fn(&'r actix_web::httprequest::HttpRequest) -> std::boxed::Box<(dyn futures::future::Future<Item = actix_web::httpresponse::HttpResponse, Error = actix_web::error::Error> + 'static)> {index}: actix_web::with::WithAsyncFactory<_, State, _, _, _> is not satisfied

the trait actix_web::with::WithAsyncFactory<_, State, _, _, _> is not implemented for for<'r> fn(&'r actix_web::httprequest::HttpRequest) -> std::boxed::Box<(dyn futures::future::Future<Item = actix_web::httpresponse::HttpResponse, Error = actix_web::error::Error> + 'static)> {index}

I wanted to keep the original post succinct and just focus on the simplest instance, but this is (the beginning of) what I meant by feeling that there were lots of parts that don't quite fit together.

Try moving HttpRequest instead of borrowing it. This builds fine for me with the current stable rust.

[dependencies]
actix-web = "0.7.19"
serde = {version = "1.0.89", features = ["derive"]}
futures = "0.1.25"
failure = "0.1.5"
use actix_web::{
    http, middleware::Logger, server, App, AsyncResponder, HttpMessage, HttpRequest, HttpResponse,
    Json, Result,
};
use failure::Error;
use futures::Future;

mod input {
    use serde::{Deserialize, Serialize};
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct Input {
        pub in_val: u32,
    }
}

#[derive(Clone)]
struct State {
    val: u32,
}

fn data((input, req): (Json<input::Input>, HttpRequest<State>)) -> Result<String> {
    let v = req.state().val + input.in_val;
    Ok(format!("got data {:?}", v))
}

fn index(req: HttpRequest<State>) -> Box<Future<Item = HttpResponse, Error = Error>> {
    req.json()
        .from_err()
        .and_then(|val: input::Input| {
            println!("model: {:?}", val);
            Ok(HttpResponse::Ok().json(val)) // <- send response
        })
        .responder()
}

fn main() {
    server::new(|| {
        App::with_state(State { val: 10 })
            .middleware(Logger::default())
            .resource("/data", |r| r.method(http::Method::POST).with(data))
            .resource("/", |r| r.method(http::Method::POST).with_async(index))
    })
    .bind("127.0.0.1:8088")
    .unwrap()
    .run();
}

Yes, this is where I landed based on discussion in the actix gitter.

I'm not 100% clear yet, but the examples certainly use a borrowed &HttpRequest, including the one I posted above. It seems there are two registration styles, Request::f and Request::with. The examples don't always show which registration they're intended to be used with, and this is apparently the mismatch.

A clarification can be found in the documentation of extractors on actix' homepage.

  • Route::f is for registering fiunctions that only take a &HttpRequest.
  • Route::with is for registering functions that use Extractors. BTW there is an Extractor State so data could be written as
    use actix_web::State as AppState;
    
    fn data(input: Json<input::Input>, state: AppState<State>) -> impl Responder
    
    Not tested but this should work

Edit: It seems that the State extractor can not be found in the docs of actix_web's latest version. Here is the documentation for version 0.7.19.

Well, sort of. It does say several times that if you want to use extractors, you have to register using with(), which is fine. I was using with().

However, all the examples on that page that refer to HttpRequest do so with a reference.

On careful reading, now that I know what I'm looking for, I spot the clue: at the very end of the page, under "Other", I see that there is the State extractor you mentioned (with no example and a 404 link), and also a HttpRequest extractor type - so it's a separate thing entirely.

Yes, therefore I searched for the docs:

Finally, I have to say I realy like to work with actix and actix-web but it is always a pain to find proper examples and documentation. Well, luckily we have a great, active community :wink:.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.