# problem_details ![Maintenance](https://img.shields.io/badge/maintenance-experimental-blue.svg) RFC 9457 / RFC 7807 problem details for HTTP APIs. This crate provides the [`ProblemDetails`](https://docs.rs/problem_details/latest/problem_details/struct.ProblemDetails.html) struct which implements the [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457.html) / [RFC 7807](https://www.rfc-editor.org/rfc/rfc7807.html) problem details specification. It supports serializing and deserializing problem details using JSON, and provides integration with the [axum (0.7)](https://crates.io/crates/axum) and [poem (2.0)](https://crates.io/crates/poem) web frameworks. ## Usage The following example shows how to create a problem details object that produces the [example JSON from the RFC](https://www.rfc-editor.org/rfc/rfc9457.pdf#name-the-problem-details-json-ob). ```rust use http::Uri; use problem_details::ProblemDetails; #[derive(serde::Serialize)] struct OutOfCreditExt { balance: u32, accounts: Vec, } let details = ProblemDetails::new() .with_type(Uri::from_static("https://example.com/probs/out-of-credit")) .with_title("You do not have enough credit.") .with_detail("Your current balance is 30, but that costs 50.") .with_instance(Uri::from_static("/account/12345/msgs/abc")) .with_extensions(OutOfCreditExt { balance: 30, accounts: vec![ "/account/12345".to_string(), "/account/67890".to_string(), ], }); let json = serde_json::to_value(&details).unwrap(); assert_eq!(json, serde_json::json!({ "type": "https://example.com/probs/out-of-credit", "title": "You do not have enough credit.", "detail": "Your current balance is 30, but that costs 50.", "instance": "/account/12345/msgs/abc", "balance": 30, "accounts": [ "/account/12345", "/account/67890" ] })); ``` ## Extensions [Extensions](https://www.rfc-editor.org/rfc/rfc9457.html#name-extension-members) can be added to the problem details object using the [`with_extensions`](https://docs.rs/problem_details/latest/problem_details/struct.ProblemDetails.html#method.with_extensions) method. The extensions are passed using a struct defining the extension fields. During serialization, the extension fields are flattened into the problem details object. ```rust use problem_details::ProblemDetails; #[derive(serde::Serialize, serde::Deserialize)] struct MyExt { foo: String, bar: u32, } let details = ProblemDetails::new() .with_title("Extensions test") .with_extensions(MyExt { foo: "Hello".to_string(), bar: 42, }); let json = serde_json::to_value(&details).unwrap(); assert_eq!(json, serde_json::json!({ "title": "Extensions test", "foo": "Hello", "bar": 42 })); ``` To deserialize with extensions, provide the extensions type as the generic parameter to the [`ProblemDetails`](https://docs.rs/problem_details/latest/problem_details/struct.ProblemDetails.html) struct. ```rust let details: ProblemDetails = serde_json::from_str(json).unwrap(); ``` If you need dynamic extensions, you can use a `HashMap` as extensions object. ## Features - **serde**: Enables serde support for the `ProblemDetails` struct (_enabled by default_) - **json**: Enables serialization to JSON when using web framework integrations (_enabled by default, implies `serde`) - **xml**: Enables serialization to XML when using web framework integrations (_implies `serde`_) - **axum**: Enables integration with the [`axum`](https://crates.io/crates/axum) web framework, enabling to return `ProblemDetails` as responses. - **poem**: Enables integration with the [`poem`](https://crates.io/crates/poem) web framework, enabling to return `ProblemDetails` as responses and errors. ## Caveats This crate is not fully compliant with RFC 9457, because it fails to deserialize JSON values containing properties with incorrect types (required by [Chapter 3.1 of the RFC](https://www.rfc-editor.org/rfc/rfc9457.pdf#name-members-of-a-problem-detail)). ## License Licensed under either of - [Apache License, Version 2.0](https://opensource.org/license/apache-2-0/) - [The MIT License](https://opensource.org/license/mit/) at your option.