Search code examples
rusttraitsactix-web

Implement actix_web::Responder trait on a custom structure


I'm using actix-web v4.

I'm trying to implement a web service with a custom structure for handling errors:

pub struct ApiError {
    pub message: String,
    pub code: ErrorEnum,
    pub details: Vec<ErrorDetail>,
}

This is an example of function that returns this structure on a failure:

pub fn my_func_that_fails() -> Result<(), ApiError> {
    Err(ApiError::default())
}

I have this function to map ApiErrors to HttpResponses:

pub fn err_to_http(error: &ApiError) -> actix_web::HttpResponse {
    match error.code {
        ErrorEnum::NotFound => actix_web::HttpResponse::NotFound()
            .content_type("application/json; charset=utf-8")
            .json(error),
        //...
    }
}

This is how I use it in a handler:

pub async fn my_handler(req: actix_web::HttpRequest) -> impl actix_web::Responder {
    if let Err(e) = my_func_that_fails() {
        return err_to_http(&e);
    }
}

I would like to be able to use it this way:

pub async fn my_handler(req: actix_web::HttpRequest) -> impl actix_web::Responder {
    my_func_that_fails()?;
}

For this I need to implement the trait actix_web::Responder, but I can't find documentation online to do that. This is my attempt at doing it, but I don't know what to put in the Body type, nor if I'm doing it correctly:

impl actix_web::Responder for ApiError {
    type Body = ???;

    fn respond_to(self, req: &actix_web::HttpRequest) -> actix_web::HttpResponse<Self::Body> {
        err_to_http(&self.api_error).into_future()
    }
}

Solution

  • The following should work:

    impl actix_web::Responder for ApiError {
        type Body = actix_web::body::BoxBody;
    
        fn respond_to(self, req: &actix_web::HttpRequest) -> actix_web::HttpResponse<Self::Body> {
            err_to_http(&self.api_error)
        }
    }
    

    Although, this technically isn't accurate because implementing Responder for ApiError will not solve your problem. I recommend something like this instead:

    pub async fn my_handler(req: actix_web::HttpRequest) -> Result<actix_web::HttpResponse, ApiError> {
        my_func_that_fails()?;
    }
    

    and then have ApiError implement the ResponseError trait, like so:

    impl actix_web::error::ResponseError for ApiError {
        fn error_response(&self) -> actix_web::HttpResponse {
            match error.code {
                ErrorEnum::NotFound => actix_web::HttpResponse::NotFound()
                    .content_type("application/json; charset=utf-8")
                    .json(error),
                //...
            }
        }
    
        fn status_code(&self) -> actix_web::http::StatusCode {
            // get status code here
        }
    }