1

I'm searching a clean and good way to return json responses with the right HTTP status using Rocket 0.5.0-rc.2. I want something versatile that I can use to return error messages (in case of error) but also JSON results (in case of 200) from structs.

I'm using Rocket 0.5.0-rc.2 with the latest stable Rust version.

I have an Rocket API like this:

#[delete("/customers/<id>")]
pub async fn delete_customer(
    db: &State<Database>,
    id: String,
) -> ApiResponse<Customer> {

When I call this API with a valid id I want a status 200 with a JSON payload like this:

{
   "name": "removed-customer" // this is only an example to show some data
}

When I call this API with an un-existing id I want a status 404 with a JSON payload like this:

{
   "message": "unexisting id"
}

I can accept a fixed struct as json error, for instance a generic one like:

{
  "message": "bad params error message",
}

with "message" and the status is the "HTTP status".

But for 200 response I need to return variable structs as JSON response, for instance to return an array of Customers if I create a get_customer API.

I saw an interesting solution HERE using a struct ApiResponse:

#[derive(Debug)]
struct ApiResponse<T> {
    json: Json<T>,
    status: Status,
}

impl<'r, T: serde::Serialize> Responder<'r> for ApiResponse<T> {
    fn respond_to(self, req: &Request) -> response::Result<'r> {
        Response::build_from(self.json.respond_to(&req).unwrap())
            .status(self.status)
            .header(ContentType::JSON)
            .ok()
    }
}

but this is not working in 0.5.0-rc.2 because Respondertakes 2 lifetime arguments, so I updated this:

#[derive(Debug)]
pub struct ApiResponse<T> {
    pub json: Json<T>,
    pub status: Status,
}

#[rocket::async_trait]
impl<'r, T> Responder<'r, 'r> for ApiResponse<T> {
    fn respond_to(self, req: &'r Request<'_>) -> Result<'static> {
        Response::build_from(self.json.respond_to(&req).unwrap())
            .status(self.status)
            .header(ContentType::JSON)
            .ok()
    }
}

But again, it isn't working, because:

the method `respond_to` exists for struct `rocket::serde::json::Json<T>`,
but its trait bounds were not satisfied E0599 method cannot be called on
`rocket::serde::json::Json<T>` due to unsatisfied trait bounds
Note: the following trait bounds were not satisfied:
`T: models::customer::_::_serde::Serialize` 
which is required by `rocket::serde::json::Json<T>: rocket::response::Responder`

Probably, this is the wrong way to do this, because I have to use T = Customer in api method definition, so I cannot return a struct for Errors and Customer and the same time. I'm open to new and better approaches. Which is the recommended way to do this?

Is it better to use Value like this? :

#[derive(Debug)]
pub struct ApiResponse {
    pub json: Value,
    pub status: Status,
}

and then use it in this way?:

  • error case
return ApiResponse {
 json: json!({"message": "error message"}),
 status: Status::BadRequest,
};

or also creating a custom Error struct with "message" field?

  • success case
return ApiResponse {
 json: serde_json::to_value(customer).unwrap(),
 status: Status::BadRequest,
};
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Stefano Cappa
  • 575
  • 3
  • 17

0 Answers0