Rocket: How to handle error responses properly

Created on 28 Dec 2016  路  5Comments  路  Source: SergioBenitez/Rocket

After playing around with rocket, I've found myself writing code like the following (this is just an example to show what I mean):

#[get("/")]
fn get() -> Result<status::Custom<JSON<Structure>>, Error> {
    if 1 == 1 {
        let structure = get_a_structure();
        Ok(status::Custom(Status::Ok, JSON(structure)))
    } else if 1 == 2 {
        Err(Error::NotFound)
    } else if 1 == 3 {
        Err(Error::Unauthorized)
    }
}

Where depending on different scenarios, I'm either returning a Ok or Err where the error could be a number of enum variants.

Without any error handlers set up in rocket, both errors just responds with a 500, but I'm imagining that one could set a handler up like:

#[error(_)]
fn not_found(e: Error) -> JSON<Error> {
    // return appropriate json depending on what e is
}

That is, mounting an error handler that will catch all errors that are not handled by say a #[error(404)] handler, which can then act upon the Error that were returned by the route and give a proper response.

It doesn't seem like this is possible yet. I also don't know if there is an even better way to approach this problem, so my question is:

  • Am I doing errors wrong in my routes?
  • If not, how would I go about responding with a proper response when an Err is returned from a route, since it doesn't seem like the error is available to the error handler.
question

Most helpful comment

Here is an example for when I was messing with Diesel:

use diesel::result::Error as DieselError;
use rocket::http::Status;
use rocket::response::{Response, Responder};
use rocket_contrib::JSON;
use std::error::Error;

#[derive(Debug)]
pub enum ApiError {
    DieselError(DieselError)
}

#[derive(Serialize)]
pub struct ErrorResponse {
    pub status: &'static str,
    pub message: String,
}

pub type ApiResult<T> = Result<JSON<T>, ApiError>;

impl<'r> Responder<'r> for ApiError {
    fn respond(self) -> Result<Response<'r>, Status> {
        let message = match self {
            ApiError::DieselError(error) => {
                if error == DieselError::NotFound {
                    return Err(Status::NotFound);
                }
                String::from(error.description())
            }
        };
        JSON(ErrorResponse {
            status: "error",
            message: message,
        }).respond()
    }
}

impl From<DieselError> for ApiError {
    fn from(error: DieselError) -> ApiError {
        ApiError::DieselError(error)
    }
}

In my endpoint function I could do a try! on something that returned a Result that had an error type of diesel::result::Error which would then get converted to my ApiError using From<DieselError> for ApiError. If the endpoint returns an ApiError then my custom Responder impl is used for ApiError.

All 5 comments

@ChrisBuchholz I believe this can be accomplished by creating your own Error type and implementing Responder for it: https://rocket.rs/guide/responses/#errors

@cbrewster: Ah, cool, do you know of any example of implementing Responder on an Error type?

Here is an example for when I was messing with Diesel:

use diesel::result::Error as DieselError;
use rocket::http::Status;
use rocket::response::{Response, Responder};
use rocket_contrib::JSON;
use std::error::Error;

#[derive(Debug)]
pub enum ApiError {
    DieselError(DieselError)
}

#[derive(Serialize)]
pub struct ErrorResponse {
    pub status: &'static str,
    pub message: String,
}

pub type ApiResult<T> = Result<JSON<T>, ApiError>;

impl<'r> Responder<'r> for ApiError {
    fn respond(self) -> Result<Response<'r>, Status> {
        let message = match self {
            ApiError::DieselError(error) => {
                if error == DieselError::NotFound {
                    return Err(Status::NotFound);
                }
                String::from(error.description())
            }
        };
        JSON(ErrorResponse {
            status: "error",
            message: message,
        }).respond()
    }
}

impl From<DieselError> for ApiError {
    fn from(error: DieselError) -> ApiError {
        ApiError::DieselError(error)
    }
}

In my endpoint function I could do a try! on something that returned a Result that had an error type of diesel::result::Error which would then get converted to my ApiError using From<DieselError> for ApiError. If the endpoint returns an ApiError then my custom Responder impl is used for ApiError.

71 asks the same question; take a look at that thread.

P.S: I thought this was a serious case of dejavu for a second there.

@SergioBenitez: Ha, my bad 馃槆

Was this page helpful?
0 / 5 - 0 ratings

Related issues

lambda-fairy picture lambda-fairy  路  4Comments

Perseus101 picture Perseus101  路  4Comments

paulvt picture paulvt  路  4Comments

GoRustafari picture GoRustafari  路  3Comments

marcusball picture marcusball  路  3Comments