problem_details/
actix.rs

1//! Actix response types for [`ProblemDetails`].
2//!
3//! Requires feature `actix`.
4//!
5//! With the `actix` feature enabled, [`ProblemDetails`] implements [`ResponseError`] using
6//! [`JsonProblemDetails`]. You can also return [`JsonProblemDetails`] to be specific.
7//! If you want to return XML, you can use [`XmlProblemDetails`].
8//!
9//! # Example
10//!
11//! ```rust
12//! use actix_web::{App, web, HttpServer};
13//! use http::StatusCode;
14//! use problem_details::ProblemDetails;
15//!
16//! async fn handler() -> Result<&'static str, ProblemDetails> {
17//!     // always return a problem description
18//!     Err(ProblemDetails::from_status_code(StatusCode::IM_A_TEAPOT)
19//!         .with_detail("short and stout"))
20//! }
21//!
22//! fn main() {
23//!     HttpServer::new(|| {
24//!         App::new()
25//!             .route("/", web::get().to(handler))
26//!     // build and run server...
27//!     });
28//! }
29//! ```
30use actix_web::{
31    web::Json,
32    {HttpResponse, ResponseError},
33};
34use http::StatusCode;
35use std::fmt::Debug;
36
37use crate::ProblemDetails;
38
39#[cfg(feature = "json")]
40use crate::JsonProblemDetails;
41
42#[cfg(feature = "xml")]
43use crate::XmlProblemDetails;
44
45#[cfg(feature = "json")]
46impl<Ext> ResponseError for ProblemDetails<Ext>
47where
48    Ext: serde::Serialize + Clone + Send + Debug,
49{
50    fn status_code(&self) -> actix_web::http::StatusCode {
51        // Due to http crate version mismatches we need to translate the status code
52        let status_code = self
53            .status
54            .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR)
55            .as_u16();
56        actix_web::http::StatusCode::from_u16(status_code)
57            .expect("Status code should be translatable")
58    }
59
60    fn error_response(&self) -> HttpResponse {
61        HttpResponse::build(self.status_code())
62            .content_type(JsonProblemDetails::<Ext>::CONTENT_TYPE)
63            .json(Json(self))
64    }
65}
66
67#[cfg(feature = "json")]
68impl<Ext> ResponseError for JsonProblemDetails<Ext>
69where
70    Ext: serde::Serialize + Clone + Send + Debug,
71{
72    fn error_response(&self) -> HttpResponse {
73        HttpResponse::build(self.0.status_code())
74            .content_type(JsonProblemDetails::<Ext>::CONTENT_TYPE)
75            .json(Json(&self.0))
76    }
77}
78
79#[cfg(feature = "xml")]
80impl<Ext> ResponseError for XmlProblemDetails<Ext>
81where
82    Ext: serde::Serialize + Clone + Send + Debug,
83{
84    fn error_response(&self) -> HttpResponse {
85        let content = match self.to_body_string() {
86            Ok(xml) => xml,
87            Err(_) => return HttpResponse::InternalServerError().into(),
88        };
89
90        HttpResponse::build(self.0.status_code())
91            .content_type(XmlProblemDetails::<Ext>::CONTENT_TYPE)
92            .body(content)
93    }
94}