I'm using rust + rocket + diesel (orm) + serde_derive to make a rest api. Currently, I'm dealing with error handling for the api if diesel fails to insert a user for whatever reason. It looks like this:
pub fn create(user: InsertableUser, connection: &MysqlConnection) -> ApiResponse {
let result = diesel::insert_into(users::table)
.values(&InsertableUser::hashed_user(user))
.execute(connection);
match result {
Ok(_) => ApiResponse {
json: json!({"success": true, "error": null}),
status: Status::Ok,
},
Err(error) => {
println!("Cannot create the recipe: {:?}", error);
ApiResponse {
json: json!({"success": false, "error": error}),
status: Status::UnprocessableEntity,
}
}
}
}
However, json: json!({"success": false, "error": error}), gives me this error:
the trait bound `diesel::result::Error: user::_IMPL_DESERIALIZE_FOR_User::_serde::Serialize` is not satisfied
the trait `user::_IMPL_DESERIALIZE_FOR_User::_serde::Serialize` is not implemented for `diesel::result::Error`
note: required because of the requirements on the impl of `user::_IMPL_DESERIALIZE_FOR_User::_serde::Serialize` for `&diesel::result::Error`
note: required by `serde_json::value::to_value`rustc(E0277)
<::serde_json::macros::json_internal macros>(123, 27): the trait `user::_IMPL_DESERIALIZE_FOR_User::_serde::Serialize` is not implemented for `diesel::result::Error`
By the sounds of it, diesel::result::Error does not #[derive(Serialize)], and so cannot be serialized with the json! macro. Thus, I need some way to make the diesel::result::Error implement/derive Serialize.
Thanks in advance for any help.
BTW the ApiResponse looks like:
use rocket::http::{ContentType, Status};
use rocket::request::Request;
use rocket::response;
use rocket::response::{Responder, Response};
use rocket_contrib::json::JsonValue;
#[derive(Debug)]
pub struct ApiResponse {
pub json: JsonValue,
pub status: Status,
}
impl<'r> Responder<'r> for ApiResponse {
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()
}
}
Serde provides a workaround for deriving serialization implementations for external crates - see the section Derive for remote crates in their documentation.
You have to define an enum with the same definition as the one you are trying to serialize (diesel::result::Error in your case), and then identify that as a kind of proxy for the type you are trying to serialize, like this:
#[derive(Serialize, Deserialize)]
#[serde(remote = "diesel::result::Error")]
struct ErrorDef {
// Definition in here the same as the enum diesel::result::Error
// ...
}
Of course you would have to do the same for all of the types enclosed within the Error type as well (or at least any types that don't already implement Serialize).
The documentation states that Serde checks the definition you provide against the one in the 'remote' crate, and throws an error if they differ, which would help keep them in sync.
Also note that this does not result in diesel::result::Error implementing Serialize - rather you now have a stand-in type that you can use like this:
struct JsonErrorRespone {
pub success: bool,
#[serde(with = "ErrorDef")]
pub error: diesel::result::Error,
}
You would then serialize an instance of the above struct instead of your existing json! macro call.
Alternatively the document linked above also gives some tips for manually calling the correct Serialize / Deserialize implementations.
Disclaimer: I haven't used this facility yet, the above was gleaned from the documentation only.
The short answer: yes you can. Personally I've always found it a bit difficult.
As a compromise, you could pull out just the parts of the error that are relevant to you, or even do:
ApiResponse {
json: json!({"success": false, "error": error.to_string() }),
status: Status::UnprocessableEntity,
}
If you're content with just a textual representation of the error.
Related
I have a simple structure that contains a resources field. I would like my resources to always be Urls.
In the actual file that I am trying to deserialize, resources can either be URLs or Path.
Here is my structure:
pub struct Myfile {
pub resources: Vec<Resource>,
}
pub type Resource = Url;
I would like to use serde to:
Try to deserialize each Resource using the implementation from the url crate.
If it fails, try to deserialize each one of them into a Path and then use url::from_*_path() to get a Url.
I am trying to adapt the string or map,map and structure examples but I am struggling to understand where to even start.
Since my end result will by a Url, the examples seem to show that I should be implementing Deserialize for Url. But I still need to current implementation. My Resource is an alias so I can't implement Deserialize for it.
Is there any simple way to deserialize both Paths and Urls into Urls?
PS: I will probably go for the answer proposed but after reading through this post, I tried the following which seems to work too:
#[derive(Clone, Debug)]
pub struct Resource(Url);
impl<'de> Deserialize<'de> for Resource {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de>
{
let s = String::deserialize(deserializer)?;
if let path = Path::new(&s) {
if path.is_file() {
Ok(Resource(Url::from_file_path(path).unwrap()))
} else {
Ok(Resource(Url::from_directory_path(path).unwrap()))
}
} else {
Url::deserialize(s.into_deserializer()).map(|x| Resource(x))
}
}
}
But is less convenient to work with than regular Urls.
I think the key to doing this with reasonable effort is to realize that serde::Deserialize for Url is also just cooking with water, i.e. just expecting a string and calling Url::parse on it.
So it's time to deploy my favourite serde trick: deserialize to a struct that serde can handle easily:
#[derive(Deserialize)]
pub struct MyfileDeserialize {
pub resources: Vec<String>,
}
Tell serde that it should get the struct you finally want from that easily handlable struct:
#[derive(Deserialize, Debug)]
#[serde(try_from = "MyfileDeserialize")]
pub struct Myfile {
pub resources: Vec<Resource>,
}
Finally, you just need to define how to turn MyfileDeserialize into Myfile.
impl TryFrom<MyfileDeserialize> for Myfile {
type Error = &'static str; // TODO: replace with anyhow or a proper error type
fn try_from(t: MyfileDeserialize) -> Result<Self, &'static str> {
Ok(Self {
resources: t
.resources
.into_iter()
.map(|url| {
if let Ok(url) = Url::parse(&url) {
return Ok(url);
};
if let Ok(url) = Url::from_file_path(url) {
return Ok(url);
};
// try more from_*_path here.
Err("Can't as url or path")
})
.collect::<Result<Vec<_>, _>>()?,
})
}
}
Playground
Edit regarding your PS:
If you are willing to mess around with manually implementing deserializer traits and functions, I suppose you do have the option of completely getting rid of any wrapper structs or mediating TryFrom types: add a #[serde(deserialize_with = …)] to your resources, and in there, first do a <Vec<String>>::deserialize(de)?, and then turn that into a Vec<Url> as usual.
Playground
I want to learn how to properly deal with errors in Rust. I have read the book and this example; now I would like to know how I should deal with errors in this function:
fn get_synch_point(&self) -> Result<pv::synch::MeasPeriods, reqwest::Error> {
let url = self.root.join("/term/pv/synch"); // self.root is url::Url
let url = match url {
Ok(url) => url,
// ** this err here is url::ParseError and can be converted to Error::Kind https://docs.rs/reqwest/0.8.3/src/reqwest/error.rs.html#54-57 **//
Err(err) => {
return Err(Error {
kind: ::std::convert::From::from(err),
url: url.ok(),
})
}
};
Ok(reqwest::get(url)?.json()?) //this return reqwest::Error or convert to pv::sych::MeasPeriods automaticly
}
This code is improper; it causes a compilation error:
error[E0451]: field `kind` of struct `reqwest::Error` is private
--> src/main.rs:34:42
|
34 | Err(err) => return Err(Error{kind: ::std::convert::From::from(err), url: url.ok()})
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `kind` is private
error[E0451]: field `url` of struct `reqwest::Error` is private
--> src/main.rs:34:81
|
34 | Err(err) => return Err(Error{kind: ::std::convert::From::from(err), url: url.ok()})
| ^^^^^^^^^^^^^ field `url` is private
What is a proper pattern to deal with that case? For me, reqwest::Error in this case is a good solution so I would like to avoid defining my own error type:
enum MyError {
Request(reqwest::Error),
Url(url::ParseError) // this already a part of request::Error::Kind!!!
}
Update 2020
The rust programming language is evolving quickly so a new answer can be added! I really liked custom_error but now I think thiserror will be my loved one!
use thiserror::Error;
#[derive(Error, Debug)]
pub enum DataStoreError {
#[error("data store disconnected")]
Disconnect(#[from] io::Error),
#[error("the data for key `{0}` is not available")]
Redaction(String),
#[error("invalid header (expected {expected:?}, found {found:?})")]
InvalidHeader {
expected: String,
found: String,
},
#[error("unknown data store error")]
Unknown,
}
This allow change io::Error to DataStoreError::Disconnect with question mark ?. Go here for details
useful links:
great blog about using thiserror in combine with anyhow
Other interesting crates:
anyhow - Flexible concrete Error type built on std::error::Error
snafu - Situation Normal: All Fouled Up - SNAFU is a library to easily assign underlying errors into domain-specific errors while adding context. (similar to thiserror)
custom_error - This crate contains a macro that should make it easier to define custom errors without having to write a lot of boilerplate code.
for panics:
proc-macro-error - This crate aims to make error reporting in proc-macros simple and easy to use.
human-panic - Panic messages for humans. Handles panics by calling std::panic::set_hook to make errors nice for humans.
Unfortunately, in your case you cannot create a reqwest::Error from other error types, if the reqwest library does not provide a way to do so (and it likely doesn't). To solve this problem, which is very common, especially in applications which use multiple libraries, the proper solution would be one of the following:
Declare your own custom enum with all errors your application works with (or one subsystem of your application; granularity highly depends on the project), and declare From conversions from all errors you work with to this enum type.
As an extension of this approach, you can use error-chain (or quick-error, on which error-chain is basically based) to generate such custom types and conversions in a semi-automatic way.
Use a special, generic error type. There are basically two of them:
a. Box<Error> where Error is defined in the standard library.
b. Use the Error type defined in the failure crate.
Then the question mark operator will be able to convert any compatible error to one of these types because of various Into and From trait implementations.
Note that the failure crate is intended to be the way to define errors promoted in the Rust community. Not only does it provide a common error type and trait (which fixes various issues with the std::error::Error trait; see for example here), it also has facilities to define your own error types (for example, with failure_derive), and for tracking error context, causes and generating backtrace. Additionally, it tries to be as compatible with the existing error handling approaches as possible, therefore it can be used to integrate with libraries which use other, older approaches (std::error::Error, error-chain, quick-error) quite easily. So I strongly suggest you to consider using this crate first, before other options.
I have already started using failure in my application projects, and I just can't express how much easier and nicer error handling has become. My approach is as follows:
Define the Result type:
type Result<T> = std::result::Result<T, failure::Error>;
Use Result<Something> everywhere where an error can be returned, using the question mark operator (?) to convert between errors and functions like err_msg or format_err! or bail! to create my own error messages.
I have yet to write a library using failure, but I imagine that for libraries it would be important to create more specific errors declared as an enum, which can be done with the failure_derive crate. For applications, though, the failure::Error type is more than enough.
In that case, reusing the underlying error type is not possible because you cannot construct its hidden fields. And even when it is possible, I would advise against it, in order to make your code more flexible and future-proof.
Defining custom error types can involve writing a lot of boilerplate, but fortunately several libraries exist to alleviate this pain. failure, error-chain and quick-error were already mentioned above, but I would like to point you to a crate I wrote that involves even less boilerplate than the others : custom_error. With it, you can write:
#[macro_use] extern crate custom_error;
custom_error!{ MyError
Request{source: reqwest::Error} = "request error",
Url{source: url::ParseError} = "invalid url"
}
As already stated by
Vladimir Matveev, the failure crate should be your starting point. Here is my solution:
use std::io;
use std::result;
use failure::{Backtrace, Fail};
/// This is a new error type manged by Oxide library.
/// The custom derive for Fail derives an impl of both Fail and Display.
#[derive(Debug, Fail)]
pub enum OxideError {
#[fail(display = "{}", message)]
GeneralError { message: String },
#[fail(display = "{}", message)]
IoError {
message: String,
backtrace: Backtrace,
#[cause]
cause: io::Error,
},
}
/// Create general error
pub fn general(fault: &str) -> OxideError {
OxideError::GeneralError {
message: String::from(fault),
}
}
/// Create I/O error with cause and backtrace
pub fn io(fault: &str, error: io::Error) -> OxideError {
OxideError::IoError {
message: String::from(fault),
backtrace: Backtrace::new(),
cause: error,
}
}
This error enumeration is extendible which allows it to accommodate future modifications that might be made to the program.
It seems the anyhow crate is good excepts for not supporting Box<dyn StdError>.
Most error types implemented Display trait, so use this magic.
Customize an error
Implement From trait from all object that implements Display
pub struct MyError {
pub code: i32,
pub message: String,
}
impl<E: Display> From<E> for MyError {
fn from(value: E) -> Self {
MyError {
code: 0,
message: value.to_string(),
}
}
}
async fn hello() -> std::result::Result<(), &'static str> {
return Err("Hello world");
}
async fn test() -> std::result::Result<(), MyError> {
// &str
hello().await?;
// std::io::Error
std::fs::read("DD")?;
// serde_json::Error
let data = serde_json::from_str("[1,2,3]")?;
Ok(())
}
In a web server application using rocket.rs, I am using an Error type that implement Responder throughout my API. This error type ensures all errors are uniformly rendered (as RFC 7807 json).
However, I can't find a way to use these Error responses in RequestGuards. It seems that the from_request function results in an Outcome which uses an entirely different model, returning Outcome::Failure((Status, T)) on errors.
How can I ensure that errors in these request guards are rendered in the same JSON format? Is it even customizable?
I have tried to use a catcher, but this does not seem to retrieve any error information whatsoever.
The docs for FromRequest's Outcome state:
Note that users can request types of Result<S, E> and Option<S> to catch Failures and retrieve the error value.
At the start of your FromRequest implementation, define type Error = JsonValue;
In the from_request function, make sure it returns request::Outcome<S, Self::Error> where S is what you're implementing for.
In the from_request function, when you want to return a failure do something like Outcome::Failure((Status::Unauthorized, json!({"error": "unauthorised"}))), or whatever it is you want to return.
In your route's function use Result<S, JsonValue> as the type of the request guard, where S is what you where implementing for. In your route, use match to match it to Ok(S) or Err(json_error) for example.
There is probably a way to pass on the status of Outcome::Failure, but the solution I've described means if you're using a custom responder you would set the status in the responder, not based on Outcome::Failure - for example the code below.
Here's an example applied to the ApiKey request guard example from the docs, with an example custom responder called ApiResponse that sets its own status:
#[macro_use]
extern crate rocket;
#[macro_use]
extern crate rocket_contrib;
#[macro_use]
extern crate serde_derive;
use rocket::Outcome;
use rocket::http::{ContentType, Status};
use rocket::request::{self, Request, FromRequest};
use rocket::response::{self, Responder, Response};
use rocket_contrib::json::{Json, JsonValue};
#[derive(Debug)]
pub struct ApiResponse {
pub json: JsonValue,
pub status: Status,
}
impl<'r> Responder<'r> for ApiResponse {
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()
}
}
#[derive(Debug, Deserialize, Serialize)]
struct ApiKey(String);
/// Returns true if `key` is a valid API key string.
fn is_valid(key: &str) -> bool {
key == "valid_api_key"
}
impl<'a, 'r> FromRequest<'a, 'r> for ApiKey {
type Error = JsonValue;
fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
let keys: Vec<_> = request.headers().get("x-api-key").collect();
match keys.len() {
0 => Outcome::Failure((Status::BadRequest, json!({ "error": "api key missing" }))),
1 if is_valid(keys[0]) => Outcome::Success(ApiKey(keys[0].to_string())),
1 => Outcome::Failure((Status::BadRequest, json!({ "error": "api key invalid" }))),
_ => Outcome::Failure((Status::BadRequest, json!({ "error": "bad api key count" }))),
}
}
}
#[get("/sensitive")]
fn sensitive(key: Result<ApiKey, JsonValue>) -> ApiResponse {
match key {
Ok(_ApiKey) => ApiResponse {
json: json!({ "data": "sensitive data." }),
status: Status::Ok
},
Err(json_error) => ApiResponse {
json: json_error,
status: Status::BadRequest
}
}
}
I'm new to Rust and Rocket, so this might not be the best solution.
I have a simple middleware intended to access the application's global state to perform validation of the authentication token:
use actix_web;
use actix_web::HttpMessage;
pub struct Authenticator;
impl<S> actix_web::middleware::Middleware<S> for Authenticator {
fn start(
&self,
request: &mut actix_web::HttpRequest<S>,
) -> actix_web::Result<actix_web::middleware::Started> {
//let _state = request.state() as &::application::State;
match request.headers().get("Authentication") {
Some(_) => Ok(actix_web::middleware::Started::Done),
None => Err(::view::error(
"No authentication header provided",
actix_web::http::StatusCode::FORBIDDEN,
)),
}
}
}
The commented string shows how I tried to get the state. I have tried many ways actually. What is the best way of doing such stuff?
I thought about adding a reference to needed data (e.g. Arc'd RwLock) to the Authenticator structure and constructing it with the reference when I register my middlewares.
I am still not good with trait stuff, but there must be a clean way of casting the S type to my application-defined State structure:
pub struct State {
pub database: actix::Addr<actix::Syn, ::database::Actor>,
pub cache: ::std::sync::Arc<::cache::Cache>,
pub sessions: ::std::sync::Arc<::session::Storage>,
}
Use your state instead of S:
impl actix_web::middleware::Middleware<::Application::State> for Authenticator {
}
By the way, middleware can have state too.
I want to learn how to properly deal with errors in Rust. I have read the book and this example; now I would like to know how I should deal with errors in this function:
fn get_synch_point(&self) -> Result<pv::synch::MeasPeriods, reqwest::Error> {
let url = self.root.join("/term/pv/synch"); // self.root is url::Url
let url = match url {
Ok(url) => url,
// ** this err here is url::ParseError and can be converted to Error::Kind https://docs.rs/reqwest/0.8.3/src/reqwest/error.rs.html#54-57 **//
Err(err) => {
return Err(Error {
kind: ::std::convert::From::from(err),
url: url.ok(),
})
}
};
Ok(reqwest::get(url)?.json()?) //this return reqwest::Error or convert to pv::sych::MeasPeriods automaticly
}
This code is improper; it causes a compilation error:
error[E0451]: field `kind` of struct `reqwest::Error` is private
--> src/main.rs:34:42
|
34 | Err(err) => return Err(Error{kind: ::std::convert::From::from(err), url: url.ok()})
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `kind` is private
error[E0451]: field `url` of struct `reqwest::Error` is private
--> src/main.rs:34:81
|
34 | Err(err) => return Err(Error{kind: ::std::convert::From::from(err), url: url.ok()})
| ^^^^^^^^^^^^^ field `url` is private
What is a proper pattern to deal with that case? For me, reqwest::Error in this case is a good solution so I would like to avoid defining my own error type:
enum MyError {
Request(reqwest::Error),
Url(url::ParseError) // this already a part of request::Error::Kind!!!
}
Update 2020
The rust programming language is evolving quickly so a new answer can be added! I really liked custom_error but now I think thiserror will be my loved one!
use thiserror::Error;
#[derive(Error, Debug)]
pub enum DataStoreError {
#[error("data store disconnected")]
Disconnect(#[from] io::Error),
#[error("the data for key `{0}` is not available")]
Redaction(String),
#[error("invalid header (expected {expected:?}, found {found:?})")]
InvalidHeader {
expected: String,
found: String,
},
#[error("unknown data store error")]
Unknown,
}
This allow change io::Error to DataStoreError::Disconnect with question mark ?. Go here for details
useful links:
great blog about using thiserror in combine with anyhow
Other interesting crates:
anyhow - Flexible concrete Error type built on std::error::Error
snafu - Situation Normal: All Fouled Up - SNAFU is a library to easily assign underlying errors into domain-specific errors while adding context. (similar to thiserror)
custom_error - This crate contains a macro that should make it easier to define custom errors without having to write a lot of boilerplate code.
for panics:
proc-macro-error - This crate aims to make error reporting in proc-macros simple and easy to use.
human-panic - Panic messages for humans. Handles panics by calling std::panic::set_hook to make errors nice for humans.
Unfortunately, in your case you cannot create a reqwest::Error from other error types, if the reqwest library does not provide a way to do so (and it likely doesn't). To solve this problem, which is very common, especially in applications which use multiple libraries, the proper solution would be one of the following:
Declare your own custom enum with all errors your application works with (or one subsystem of your application; granularity highly depends on the project), and declare From conversions from all errors you work with to this enum type.
As an extension of this approach, you can use error-chain (or quick-error, on which error-chain is basically based) to generate such custom types and conversions in a semi-automatic way.
Use a special, generic error type. There are basically two of them:
a. Box<Error> where Error is defined in the standard library.
b. Use the Error type defined in the failure crate.
Then the question mark operator will be able to convert any compatible error to one of these types because of various Into and From trait implementations.
Note that the failure crate is intended to be the way to define errors promoted in the Rust community. Not only does it provide a common error type and trait (which fixes various issues with the std::error::Error trait; see for example here), it also has facilities to define your own error types (for example, with failure_derive), and for tracking error context, causes and generating backtrace. Additionally, it tries to be as compatible with the existing error handling approaches as possible, therefore it can be used to integrate with libraries which use other, older approaches (std::error::Error, error-chain, quick-error) quite easily. So I strongly suggest you to consider using this crate first, before other options.
I have already started using failure in my application projects, and I just can't express how much easier and nicer error handling has become. My approach is as follows:
Define the Result type:
type Result<T> = std::result::Result<T, failure::Error>;
Use Result<Something> everywhere where an error can be returned, using the question mark operator (?) to convert between errors and functions like err_msg or format_err! or bail! to create my own error messages.
I have yet to write a library using failure, but I imagine that for libraries it would be important to create more specific errors declared as an enum, which can be done with the failure_derive crate. For applications, though, the failure::Error type is more than enough.
In that case, reusing the underlying error type is not possible because you cannot construct its hidden fields. And even when it is possible, I would advise against it, in order to make your code more flexible and future-proof.
Defining custom error types can involve writing a lot of boilerplate, but fortunately several libraries exist to alleviate this pain. failure, error-chain and quick-error were already mentioned above, but I would like to point you to a crate I wrote that involves even less boilerplate than the others : custom_error. With it, you can write:
#[macro_use] extern crate custom_error;
custom_error!{ MyError
Request{source: reqwest::Error} = "request error",
Url{source: url::ParseError} = "invalid url"
}
As already stated by
Vladimir Matveev, the failure crate should be your starting point. Here is my solution:
use std::io;
use std::result;
use failure::{Backtrace, Fail};
/// This is a new error type manged by Oxide library.
/// The custom derive for Fail derives an impl of both Fail and Display.
#[derive(Debug, Fail)]
pub enum OxideError {
#[fail(display = "{}", message)]
GeneralError { message: String },
#[fail(display = "{}", message)]
IoError {
message: String,
backtrace: Backtrace,
#[cause]
cause: io::Error,
},
}
/// Create general error
pub fn general(fault: &str) -> OxideError {
OxideError::GeneralError {
message: String::from(fault),
}
}
/// Create I/O error with cause and backtrace
pub fn io(fault: &str, error: io::Error) -> OxideError {
OxideError::IoError {
message: String::from(fault),
backtrace: Backtrace::new(),
cause: error,
}
}
This error enumeration is extendible which allows it to accommodate future modifications that might be made to the program.
It seems the anyhow crate is good excepts for not supporting Box<dyn StdError>.
Most error types implemented Display trait, so use this magic.
Customize an error
Implement From trait from all object that implements Display
pub struct MyError {
pub code: i32,
pub message: String,
}
impl<E: Display> From<E> for MyError {
fn from(value: E) -> Self {
MyError {
code: 0,
message: value.to_string(),
}
}
}
async fn hello() -> std::result::Result<(), &'static str> {
return Err("Hello world");
}
async fn test() -> std::result::Result<(), MyError> {
// &str
hello().await?;
// std::io::Error
std::fs::read("DD")?;
// serde_json::Error
let data = serde_json::from_str("[1,2,3]")?;
Ok(())
}