Returning HttpResponse from Actix Middleware

I have a middleware implementation as below:

impl<S, B> Service<ServiceRequest> for InitialRunMiddleware<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

    forward_ready!(service);

    fn call(&self, req: ServiceRequest) -> Self::Future {
        let pool_data: &web::Data<sqlx::PgPool> = req.app_data().cloned().unwrap();
        let fut = self.service.call(req);

        Box::pin(async move {
            log::debug!("Executing InitialRunMiddleware...");

            let res = fut.await?;
            let pool = pool_data.get_ref();
            let required_setups = get_initial_run(pool)
                .await
                .map_err(|e| SqlxErrorWrapper(e))?;

            if required_setups.len() > 0 {
                Ok(req.into_response(
                    HttpResponse::ServiceUnavailable()
                        .body("")
                        .map_into_boxed_body(),
                ))
            } else {
                Ok(res)
            }
        })
    }
}

This uses get_initial_run, which connects to database and gets some information as below:

#[derive(Debug, Eq, PartialEq, Hash)]
pub enum RequiredSetup {
    NoUser,
}

pub async fn get_initial_run(pool: &sqlx::PgPool) -> Result<HashSet<RequiredSetup>, sqlx::Error> {
    log::debug!("Querying for initial run...");

    let user_exists = sqlx::query("SELECT * FROM users LIMIT 1;")
        .fetch_optional(pool)
        .await?
        .is_some();

    let mut required_setups = HashSet::new();

    if !user_exists {
        required_setups.insert(RequiredSetup::NoUser);
    }

    log::trace!("required setups: {required_setups:?}");

    Ok(required_setups)
}

The problem is, if-else branch in middleware implementation has incompatible type. It seems the middleware is expecting to return ServiceRequest.

How do I return HttpResponse from a custom middleware?

What is the exact error message?

For now, I want to return an empty string. When I resolve the issue, I will set a custom json body for HttpResponse.

No, I meant what does it say when you run cargo check? :sweat_smile: Could you paste the output here, please?

Of course, here it is:

    Checking actixdemo v0.1.0 (/home/erayerdin/Projects/Trash/actixdemo)
warning: unused import: `body::EitherBody`
 --> src/shared/middlewares/initial_run.rs:7:5
  |
7 |     body::EitherBody,
  |     ^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

error[E0308]: mismatched types
  --> src/shared/middlewares/initial_run.rs:31:15
   |
31 |         ready(Ok(InitialRunMiddleware { service }))
   |         ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Result<InitialRunMiddleware<S>, ()>`, found `Result<InitialRunMiddleware<S>, Error>`
   |         |
   |         arguments to this function are incorrect
   |
   = note: expected enum `Result<_, ()>`
              found enum `Result<_, anyhow::Error>`
help: the return type of this call is `Result<InitialRunMiddleware<S>, anyhow::Error>` due to the type of the argument passed
  --> src/shared/middlewares/initial_run.rs:31:9
   |
31 |         ready(Ok(InitialRunMiddleware { service }))
   |         ^^^^^^------------------------------------^
   |               |
   |               this argument influences the return type of `ready`
note: function defined here
  --> /home/erayerdin/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/future/ready.rs:68:8
   |
68 | pub fn ready<T>(t: T) -> Ready<T> {
   |        ^^^^^

error[E0277]: the trait bound `B: MessageBody` is not satisfied
   --> src/shared/middlewares/initial_run.rs:67:24
    |
67  |                 Ok(res.map_into_boxed_body())
    |                        ^^^^^^^^^^^^^^^^^^^ the trait `MessageBody` is not implemented for `B`
    |
note: required by a bound in `ServiceResponse::<B>::map_into_boxed_body`
   --> /home/erayerdin/.cargo/registry/src/github.com-1ecc6299db9ec823/actix-web-4.3.1/src/service.rs:493:12
    |
493 |         B: MessageBody + 'static,
    |            ^^^^^^^^^^^ required by this bound in `ServiceResponse::<B>::map_into_boxed_body`
help: consider further restricting this bound
    |
43  |     B: 'static + actix_web::body::MessageBody,
    |                ++++++++++++++++++++++++++++++

error[E0308]: `if` and `else` have incompatible types
  --> src/shared/middlewares/initial_run.rs:67:17
   |
64 | /             if required_setups.len() > 0 {
65 | |                 Ok(HttpResponse::ServiceUnavailable().body("").into_body())
   | |                 ----------------------------------------------------------- expected because of this
66 | |             } else {
67 | |                 Ok(res.map_into_boxed_body())
   | |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Result<BoxBody, Error>`, found `Result<ServiceResponse, Error>`
68 | |             }
   | |_____________- `if` and `else` have incompatible types
   |
   = note: expected enum `Result<BoxBody, _>`
              found enum `Result<ServiceResponse, _>`

This error message does not look like it was generated by the code from your snippet:

Sorry, I try every way possible, so code changes frequently. I believe this is it:

warning: unused import: `body::EitherBody`
 --> src/shared/middlewares/initial_run.rs:7:5
  |
7 |     body::EitherBody,
  |     ^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

error[E0308]: mismatched types
  --> src/shared/middlewares/initial_run.rs:31:15
   |
31 |         ready(Ok(InitialRunMiddleware { service }))
   |         ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Result<InitialRunMiddleware<S>, ()>`, found `Result<InitialRunMiddleware<S>, Error>`
   |         |
   |         arguments to this function are incorrect
   |
   = note: expected enum `Result<_, ()>`
              found enum `Result<_, anyhow::Error>`
help: the return type of this call is `Result<InitialRunMiddleware<S>, anyhow::Error>` due to the type of the argument passed
  --> src/shared/middlewares/initial_run.rs:31:9
   |
31 |         ready(Ok(InitialRunMiddleware { service }))
   |         ^^^^^^------------------------------------^
   |               |
   |               this argument influences the return type of `ready`
note: function defined here
  --> /home/erayerdin/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/future/ready.rs:68:8
   |
68 | pub fn ready<T>(t: T) -> Ready<T> {
   |        ^^^^^

error[E0308]: `if` and `else` have incompatible types
  --> src/shared/middlewares/initial_run.rs:71:17
   |
39 |    impl<S, B> Service<ServiceRequest> for InitialRunMiddleware<S>
   |            - this type parameter
...
64 | /              if required_setups.len() > 0 {
65 | |/                 Ok(req.into_response(
66 | ||                     HttpResponse::ServiceUnavailable()
67 | ||                         .body("")
68 | ||                         .map_into_boxed_body(),
69 | ||                 ))
   | ||__________________- expected because of this
70 | |              } else {
71 | |                  Ok(res)
   | |                  ^^^^^^^ expected `Result<ServiceResponse, Error>`, found `Result<ServiceResponse<B>, Error>`
72 | |              }
   | |______________- `if` and `else` have incompatible types
   |
   = note: expected enum `Result<ServiceResponse<BoxBody>, _>`
              found enum `Result<ServiceResponse<B>, _>`
1 Like

I think you can use EitherBody to get your middleware to run:

- S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,    
+ S: Service<ServiceRequest, Response = ServiceResponse<EitherBody<B>>, Error = Error>,    
            if required_setups.len() > 0 {
                Ok(req.into_response(
                    HttpResponse::ServiceUnavailable()
                        .body("")
                        .map_into_boxed_body(),
-                ))
+                ).map_into_left_body())
            } else {
-                Ok(res)
+                Ok(res.map_into_right_body())
            }

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.