-
Notifications
You must be signed in to change notification settings - Fork 112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Document or provide examples for more complex error handling #34
Comments
If you use rocket you can catch general errors using: #[derive(Serialize, Deserialize)]
pub struct APIErrorResponse {
error: APIError,
}
#[catch(500)]
pub fn internal_error() -> Json<APIErrorResponse> {
Json(APIErrorResponse {
....some struct info...
}
})
} For the okapi part: /// Example response message, This will be added to the openapi file
fn add_500_error(responses: &mut Responses) {
responses
.responses
.entry("500".to_owned())
.or_insert_with(|| {
let mut response = okapi::openapi3::Response::default();
response.description = format!(
"\
# [500 Internal Server Error]\
(https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500)\n\
This response is given when the server has an internal error that it could not \
recover from.\n\n\
If you get this response please report this as an [issue here]\
({}).\
",
"...some link..."
);
response.into()
});
}
#[derive(Debug, Default)]
pub struct APIErrorNotModified {
pub err: Option<Error>,
}
impl APIErrorNotModified {
pub fn new() -> Self {
APIErrorNotModified::default()
}
}
/// Just to convert Errors to `failure::Error`
impl From<Error> for APIErrorNotModified {
fn from(error: Error) -> Self {
APIErrorNotModified { err: Some(error) }
}
}
/// This is for Rocket to give the proper response
impl<'r> Responder<'r> for APIErrorNotModified {
fn respond_to(self, _: &Request) -> response::Result<'r> {
if let Some(error) = self.err {
error!("{}", error); // this is for logging
let error_response = APIErrorResponse {
error: APIError {
message: error.to_string(),
issue_link: None,
code: 500,
},
};
return Response::build()
.header(ContentType::JSON)
.status(Status::InternalServerError)
.sized_body(std::io::Cursor::new(
serde_json::to_string(&error_response).unwrap(),
))
.ok();
}
Response::build().status(Status::NotModified).ok()
}
}
/// This is for okapi to add all the responses
impl OpenApiResponder<'_> for APIErrorNotModified {
fn responses(_: &mut OpenApiGenerator) -> OpenApiResult<Responses> {
let mut responses = Responses::default();
add_304_error(&mut responses);
add_404_error(&mut responses);
add_500_error(&mut responses);
Ok(responses)
}
} It just so happens that my code is made public today, so you can view my implementation here: You Rocket code can then just return a /// Request a list of all entities
#[openapi]
#[get("/entities?<pagination..>")]
pub fn list_entities(
conn: DfStDatabase,
pagination: ApiPagination,
server_info: State<ServerInfo>,
) -> Result<Json<MyApiPageResult>, APIErrorNotModified> {
....
} If you have questions let me know. |
I ended up finding this https://github.com/Geigerkind/LegacyPlayersV3.git which I used as an example to write a custom BackendError like this: #[derive(Clone, Debug, JsonSchema)]
pub enum BackendError {
Bson(String),
Database(String),
NotFound
}
impl Responder<'static> for BackendError {
fn respond_to(self, _: &Request) -> Result<Response<'static>, Status> {
let body;
let status = match self {
BackendError::Bson(msg) => {
body = msg.clone();
Status::new(500, "Bson")
},
BackendError::Database(msg) => {
body = msg.clone();
Status::new(500, "Database")
},
BackendError::NotFound => {
body = String::new();
Status::NotFound
}
};
Response::build()
.status(status)
.sized_body(Cursor::new(body))
.ok()
}
}
impl OpenApiResponder<'static> for BackendError {
fn responses(gen: &mut OpenApiGenerator) -> rocket_okapi::Result<Responses> {
let mut responses = Responses::default();
let schema = gen.json_schema::<String>();
add_schema_response(&mut responses, 500, "text/plain", schema.clone())?;
add_schema_response(&mut responses, 404, "text/plain", schema.clone())?;
Ok(responses)
}
} Then I can make the return for my openapi fns Result<Json, BackendError>, and I can provide some detail whether it was a bson failure or a mongodb problem, etc. Does that make sense? |
@ralpha also, thanks for the reference to your code, lots for me to learn there! The ApiObject trait / ApiPage idea looks very interesting/convenient. Have you considered turning that into a crate? ;D |
Hey! I have similar issue. Thank you all for this examples. But I have question about how it can be dynamically used? I mean each endpoint can have it's specific list of possible errors (like |
If you just need 1 for all If you need true custom responses I would consider using Generics (
This can never happen. Because But yes I know what you mean and no there is not really a solution for that (except for the solutions on top). In the APIs I have created there are usually a few responses that are possible (according to the documentation) but in reality some can never happen. But then again you could also return error codes that are not documented. I would recommend that for APIs you just document that more error codes are allowed and document them. This will also force the consumers to reuse there error handling code for all endpoints and not just catch 1-2 error codes and not be prepared for others. Just because it might be of use there are some of codes my API's can return:
|
Yes, but not any time soon {{he replies 1,5 years later 😅}}. I might at some point, but I would still do it slightly different then I did there. |
Hello! I am new to Rust and have recently learned about okapi. I am trying to use it for a REST API with a mongodb backend. I would like to properly handle and report mongodb-related failures as a HTTP 500 error, but I cannot figure out how to do that. I suppose I could implement OpenApiResponder for the mongodb errors, but it is not clear to me how to properly implement or how I would set up the return type for my handler. An example or more documentation would be very helpful =)
The text was updated successfully, but these errors were encountered: