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

#1

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.

0 Likes

#2

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

0 Likes

#3

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.

0 Likes

#4

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();
}
0 Likes

#5

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.

0 Likes

#6

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.

0 Likes

#7

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.

0 Likes

#8

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