-
Notifications
You must be signed in to change notification settings - Fork 76
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
error codes for dropshot-provided errors need work #41
Comments
I have implemented Rust APIs like these that are running in production so I'll give my grain of salt. I think something along the lines of (2) is a good solution, Dropshot provides 2 error types, one Which leads me to another suggestion about errors that I think is pretty valuable, I think users shouldn't be encouraged to implement For example: use std::fs;
use std::io;
// Simplification, missing headers etc.
struct HTTPResponse {
code: u16, // Simplification, use enum.
body: Vec<u8>, // Simplification, use serializable object.
}
// Simplification, you might want to assert that the status code is non 2xx.
type HTTPError = HTTPResponse;
#[derive(Debug)]
enum MyAppError {
IO(io::Error),
OtherError { cause: String, code: u16 },
}
type MyAppResult<T> = Result<T, MyAppError>;
impl From<io::Error> for MyAppError {
fn from(error: io::Error) -> Self {
Self::IO(error)
}
}
// This trait needs to be defined by the app itself because Rust doesn't allow implementing
// traits if you don't own the trait or the type.
// However it could be provided by Dropshot using a macro.
trait IntoHttpResultEXT<T> {
fn with_code(self, code: u16) -> Result<T, HTTPError>;
fn not_found(self) -> Result<T, HTTPError>;
// fn internal_error(), etc...
// You can also have functions that add some additional context about the error.
}
impl<T> IntoHttpResultEXT<T> for MyAppResult<T> {
fn with_code(self, code: u16) -> Result<T, HTTPError> {
// At this step you might want to log the error since here is the last point
// where it containts the most context.
self.map_err(|err| HTTPError {
code,
// Simplification, normally you would have a mapping depending on the kind of MyAppError.
body: format!("{:?}", err).into_bytes(),
})
}
fn not_found(self) -> Result<T, HTTPError> {
self.with_code(404)
}
}
fn handler(path: &str) -> Result<HTTPResponse, HTTPError> {
// You can imagine a scenario where a different io::Error maps to an internal error
// instead of not found depending on the call site.
let file = fs::read(path).map_err(Into::into).not_found()?;
Ok(HTTPResponse {
code: 200,
body: file,
})
} |
Thanks for that -- that's helpful. We're largely working on other areas right now but this will be helpful when we get back to it. |
As it says in
error.rs
, we envision that errors from an API typically have a string code (separate from the HTTP status code):dropshot/dropshot/src/error.rs
Lines 52 to 69 in 308be87
I've generally assumed that the namespace of these codes needs to be under the control of the Dropshot consumer, since they will likely be defining most of the codes for their own application-specific conditions. However, Dropshot currently generates a bunch of different errors of its own (e.g., when a request arrives for a path that has no handler associated with it). What code should it use?
I'm thinking out loud through a bunch of options here:
match
, they'll get a compile error that forces them to decide how to map the new condition.ObjectNotFound
,BadArgs
, etc. and require that consumers use these. This might actually be nice because many consumers will wind up using a lot of the same codes, but it seems like a non-starter that consumers can't extend this.The behavior today is that the code is optional and Dropshot generally provides
None
. That was basically a hack to be able to make forward progress -- it's pretty crappy for consumers and their users.See also #39.
The text was updated successfully, but these errors were encountered: