error-stack with actix-web integration #952
-
Hi, I have a question regarding custom error implementation in use actix_web::http::StatusCode;
use actix_web::{get, web, App, HttpResponse, HttpServer, ResponseError};
use error_stack::{Report, Result};
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(thiserror::Error, Debug)]
pub enum ServerError {
#[error("{0}")]
Gerneric(String),
#[error("{0}")]
Json(#[from] serde_json::Error),
}
#[derive(Serialize, Deserialize)]
struct ErrorResponse {
ok: bool,
error: Value,
}
// implement the status code and error response if an error occours
impl ResponseError for ServerError {
fn error_response(&self) -> HttpResponse {
HttpResponse::build(self.status_code()).json(ErrorResponse {
ok: false,
error: serde_json::json!({
"name": "Internal Server Error",
"message": self.to_string()
}),
})
}
fn status_code(&self) -> StatusCode {
match self {
Self::Gerneric(_) | Self::Json(_) => StatusCode::INTERNAL_SERVER_ERROR,
}
}
}
#[get("/json")]
async fn greet() -> Result<HttpResponse, ServerError> {
let f = json().map_err(|err| ServerError::Gerneric(err.to_string()))?;
Ok(HttpResponse::Ok().body(f))
}
#[actix_web::main] // or #[tokio::main]
async fn main() -> Result<(), ServerError> {
HttpServer::new(|| {
App::new()
.route("/hello", web::get().to(|| async { "Hello World!" }))
.service(greet)
})
.bind(("127.0.0.1", 8080))
.map_err(|err| Report::new(ServerError::Gerneric(err.to_string())))?
.run()
.await
.map_err(|err| Report::new(ServerError::Gerneric(err.to_string())))?;
Ok(())
}
fn json() -> Result<String, ServerError> {
#[derive(Serialize, Deserialize)]
struct First {
value: String,
}
#[derive(Serialize, Deserialize)]
struct Second {
values: String,
}
let ciao = First {
value: "CIAO".to_string(),
};
let first = serde_json::to_value(ciao).map_err(|err| Report::new(ServerError::Json(err)))?;
// should give me an error
let second: Second =
serde_json::from_value(first).map_err(|err| Report::new(ServerError::Json(err)))?;
Ok(second.values)
} I'm trying to use the error[E0277]: the trait bound `error_stack::Report<ServerError>: ResponseError` is not satisfied
--> src/main.rs:39:1
|
39 | #[get("/json")]
| ^^^^^^^^^^^^^^^ the trait `ResponseError` is not implemented for `error_stack::Report<ServerError>`
|
= help: the following other types implement trait `ResponseError`:
BlockingError
Box<(dyn StdError + 'static)>
HttpError
Infallible
InvalidHeaderValue
JsonPayloadError
PathError
PayloadError
and 17 others
= note: required because of the requirements on the impl of `std::convert::From<error_stack::Report<ServerError>>` for `actix_web::Error`
= note: required because of the requirements on the impl of `Into<actix_web::Error>` for `error_stack::Report<ServerError>`
= note: required because of the requirements on the impl of `Responder` for `Result<HttpResponse, error_stack::Report<ServerError>>`
note: required by a bound in `Resource::<T>::to`
--> /Users/sergiupopescu/.cargo/registry/src/github.com-1ecc6299db9ec823/actix-web-4.1.0/src/resource.rs:226:20
|
226 | F::Output: Responder + 'static,
| ^^^^^^^^^ required by this bound in `Resource::<T>::to`
= note: this error originates in the attribute macro `get` (in Nightly builds, run with -Z macro-backtrace for more info)
For more information about this error, try `rustc --explain E0277`. Could Someone help me please? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 4 replies
-
This is a good question, and also thank you very much for providing a minimal reproducible example, this is really helpful! The error is slightly hidden in the error message:
as you are actually expecting it to be The definition of pub type Result<T, E = anyhow::Error> = Result<T, E>; but for pub type Result<T, C> = Result<T, Report<C>>; this means, that your function is defined like this: #[get("/json")]
async fn greet() -> std::result::Result<HttpResponse, Report<ServerError>> {
let f = json().map_err(|err| ServerError::Gerneric(err.to_string()))?;
Ok(HttpResponse::Ok().body(f))
} If you are explicit about your #[get("/json")]
async fn greet() -> std::result::Result<HttpResponse, ServerError> {
let f = json().map_err(|err| ServerError::Gerneric(err.to_string()))?;
Ok(HttpResponse::Ok().body(f))
} Personally, I often see myself importing Do you have a suggestion on how to convey this behavior better, so this error can be avoided in the future? |
Beta Was this translation helpful? Give feedback.
Hi @sergiupopescu199,
This is a good question, and also thank you very much for providing a minimal reproducible example, this is really helpful!
The error is slightly hidden in the error message:
as you are actually expecting it to be
std::convert::From<ServerError>
.The definition of
anyhow::Result
anderror_stack::Result
is slightly different. Foranyhow
, this is defined asbut for
error_stack
, this is defined asthis means, that your function is defined…