actix / actix-web

Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust.
https://actix.rs
Apache License 2.0
21.6k stars 1.67k forks source link

Use AppState within Middleware? #300

Closed gnunicorn closed 6 years ago

gnunicorn commented 6 years ago

I have a middleware that loads the user information for the Activist (read "user") being logged in from the database. For that I use the diesel-example-style AppState with the DB-Actor in it. Now with my Middleware:

use futures::Future;
use actix_web::{App, HttpRequest, HttpResponse, Result};
use actix_web::middleware::{Middleware, Started, Response};
use actix_web::middleware::identity::RequestIdentity;
use actix_web::middleware::session::RequestSession;
use model::activist::{Activist, ActivistInfoQuery};
use futures::future;
use actix::Syn;
use actix::prelude::{Addr};
use model::db::ConnDsl;

pub struct AppState {
    pub db: Addr<Syn, ConnDsl>
}
pub struct AuthMiddleware;
/// Middleware implementation, middlewares are generic over application state,
/// so you can access state with `HttpRequest::state()` method.
impl<S> Middleware<S> for AuthMiddleware {

    /// Method is called when request is ready. It may return
    /// future, which should resolve before next middleware get called.
    fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> {
        if let Some(uuid) = req.identity() {
            Ok(Started::Future(req.state()
                .db.send(ActivistInfoQuery{uuid: uuid.to_owned()})
                // .from_err()
                .and_then(|res| {
                   match res {
                       Ok(activist) => req.session().set("current_activist", activist)
                           .and_then(|_| Ok(Started::Done)),
                       Err(err) => err.into()
                   }
               })))
        } else {
            req.session().set("current_activist", None)?;
            Ok(Started::Done)
        }
    }
}

Aside from the issue that I don't actually want to store it on the session (but haven't yet looked into how to use the extensions, that will be added later), the problem is that rust doesn't know we are using AppState and it thus complains that db doesn't exist on state:

error[E0609]: no field `db` on type `&S`
  --> src/helpers/auth_middleware.rs:21:18
   |
21 |                 .db.send(ActivistInfoQuery{uuid: uuid.to_owned()})
   |                  ^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0609`.
error: Could not compile `taskivista`.

Is there any way I can use global AppState from within the middleware? What am I missing? (I couldn't find any example combining those two features)

fafhrd91 commented 6 years ago

your middleware needs to be specific to AppState

impl Middleware<AppState> for AuthMiddleware {}

gnunicorn commented 6 years ago

ah! Thanks heaps, @fafhrd91 ! Still learning this whole impl-part of things. That seem to do the trick. thanks!

GopherJ commented 5 years ago

@gnunicorn hello, I encountered the same problem and I want to use extensions, have you updated your solution to use extensions?