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

Help migrating middleware to 1.0 #1044

Closed robertohuertasm closed 5 years ago

robertohuertasm commented 5 years ago

Hi, I've been trying to migrate my old middleware to the latest version of actix but I'm getting errors all the time. Maybe some of you can help me with this šŸ˜.

This is my previous middleware:

use actix_web::middleware::{Middleware as TraitMiddleware, Started};
use actix_web::{AsyncResponder, HttpMessage, HttpRequest, HttpResponse, Result};
use futures::future::Future;
use std::str::from_utf8;

use crate::config::CONFIG;

pub struct Middleware;

impl TraitMiddleware<()> for Middleware {
    fn start(&self, req: &HttpRequest) -> Result<Started> {
        let req = req.clone();

        if req.path() != "/my_handler" {
            return Ok(Started::Response(HttpResponse::Ok().finish()));
        }

        if let Some(g_hash) = req
            .headers()
            .get("x-hub-hash")
            .and_then(|x| x.to_str().ok())
            .and_then(|x| Some(x.replace("sha1=", "")))
        {
            let fut = req
                .body()
                .from_err()
                .and_then(move |body| {
                    let key = CONFIG.secret.as_bytes();
                    let hash = hmacsha1::hmac_sha1(key, &body);
                    let body_hash = base16::encode_lower(&hash);

                    if let Ok(body_string) =
                        from_utf8(&body[..]).map(std::borrow::ToOwned::to_owned)
                    {
                        req.extensions_mut().insert(body_string);
                    }

                    if body_hash == g_hash {
                        Ok(None)
                    } else {
                        Ok(Some(HttpResponse::BadRequest().finish()))
                    }
                })
                .responder();
            Ok(Started::Future(fut))
        } else {
            Ok(Started::Response(HttpResponse::BadRequest().finish()))
        }
    }
}

I've used both the extended and the short (wrap_fn) way of creating new middlewares but I'm stuck trying to return a future from the payload.

 .wrap_fn(|mut req, srv| {
    let fut = req
        .take_payload()
        .fold(BytesMut::new(), move |mut body, chunk| {
             body.extend_from_slice(&chunk);
             Ok::<_, PayloadError>(body)
         })
        .and_then(|bytes| {
              let mut res = Response::Ok();
              Ok(res.body(bytes))
        });
    // WHAT SHOULD I RETURN HERE?
    // this fails:
    //  ^^^^^^^ expected enum `actix_http::error::PayloadError`, 
    // found struct     `actix_http::error::Error`
    fut.map(|r| req.into_response(r))
})

What am I doing wrong and certainly missing? I'd really appreciate your help. Thank you!

jesskfullwood commented 5 years ago

Based solely on the error message, I'd say you need to map the error type to turn PayloadError into actix_web::Error. PayloadError implements ResponseError and actix_web::Error implemented From<T: ResponseError> so you should just be able to map the error type like:

fut.map_err(actix_web::Error::from)
robertohuertasm commented 5 years ago

Thanks @jesskfullwood! Your help was invaluable! Actually, just using from_err() made the trick.

.wrap_fn(|mut req, srv| {
    let fut = req
        .take_payload()
        .from_err() // don't forget this
        .fold(BytesMut::new(), move |mut body, chunk| {
             body.extend_from_slice(&chunk);
             Ok::<_, PayloadError>(body)
         })
        .and_then(|bytes| {
              let mut res = Response::Ok();
              Ok(res.body(bytes))
        });
    fut.map(|r| req.into_response(r))
})

I even managed to make it work with a full middleware with just a slight modification. The return should be Box::new(fut.map(|r| req.into_response(r.into_body()))) instead.

I really, really appreciate your help! Thanks again!