pub enum ProblemFormat {
ProblemJson,
Json,
}Expand description
The media type a Problem response is served as: the RFC 9457 default
application/problem+json, or plain application/json for clients that
explicitly prefer it.
This is the opt-in content negotiation half of the problem feature.
Problem’s plain IntoResponse impl always emits
application/problem+json; nothing changes for existing users. To
negotiate, either extract ProblemFormat in a handler (it implements
FromRequestParts infallibly, reading the Accept headers) and finish
with Problem::into_response_with, or call
Problem::into_response_for with the request’s HeaderMap directly.
Both formats serve byte-identical JSON bodies; only the Content-Type
header differs. Default is ProblemFormat::ProblemJson.
§Example
use axum::{http::StatusCode, response::Response, routing::get, Router};
use axum_api_kit::{Problem, ProblemFormat};
// Accept: application/json -> Content-Type: application/json
// Accept: */* (or no Accept header) -> Content-Type: application/problem+json
async fn not_found(format: ProblemFormat) -> Response {
Problem::new(StatusCode::NOT_FOUND, "Not Found").into_response_with(format)
}
let app: Router = Router::new().route("/missing", get(not_found));Variants§
ProblemJson
Serve Content-Type: application/problem+json (the RFC 9457 media
type, and the default in every ambiguous case).
Json
Serve Content-Type: application/json, for clients whose Accept
header strictly prefers plain JSON.
Implementations§
Source§impl ProblemFormat
impl ProblemFormat
Sourcepub fn negotiate(headers: &HeaderMap) -> Self
pub fn negotiate(headers: &HeaderMap) -> Self
Chooses the response format from a request’s Accept headers.
This is a minimal, hand-rolled matcher, deliberately not a full RFC 9110 implementation. The rules:
- Recognized media ranges are
application/problem+json,application/json,application/*, and*/*(ASCII case-insensitive, across allAcceptheaders). Every other range, including other+jsonsuffix types, is ignored. - A range’s weight is its
qparameter (default1). Parameters other than the firstqare ignored. A range whoseqvalue does not parse per the RFC 9110qvaluegrammar (0to1with at most three decimals) is ignored entirely. - Each of the two servable types takes its weight from the most
specific matching range (an exact match beats
application/*, which beats*/*); among equally specific matches the highestqwins. ProblemFormat::Jsonis returned only when plainapplication/jsonends up with a strictly higher weight thanapplication/problem+json. Everything else (noAcceptheader,*/*,application/*, equal weights,q=0on both, unparseable headers) returnsProblemFormat::ProblemJson.
Accept | result |
|---|---|
| (absent) | problem+json |
*/* | problem+json |
application/* | problem+json |
application/json | plain JSON |
application/problem+json | problem+json |
application/json, */* | problem+json (tie at q=1) |
application/json;q=0.9, application/problem+json;q=0.5 | plain JSON |
application/problem+json, application/json | problem+json (tie) |
text/html | problem+json (neither matched) |
§Example
use axum::http::{header, HeaderMap, HeaderValue};
use axum_api_kit::ProblemFormat;
let mut headers = HeaderMap::new();
headers.insert(header::ACCEPT, HeaderValue::from_static("application/json"));
assert_eq!(ProblemFormat::negotiate(&headers), ProblemFormat::Json);
assert_eq!(
ProblemFormat::negotiate(&HeaderMap::new()),
ProblemFormat::ProblemJson
);Sourcepub fn content_type(self) -> &'static str
pub fn content_type(self) -> &'static str
The Content-Type value this format serves.
§Example
use axum_api_kit::{ProblemFormat, APPLICATION_PROBLEM_JSON};
assert_eq!(ProblemFormat::ProblemJson.content_type(), APPLICATION_PROBLEM_JSON);
assert_eq!(ProblemFormat::Json.content_type(), "application/json");Trait Implementations§
Source§impl Clone for ProblemFormat
impl Clone for ProblemFormat
Source§fn clone(&self) -> ProblemFormat
fn clone(&self) -> ProblemFormat
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreimpl Copy for ProblemFormat
Source§impl Debug for ProblemFormat
impl Debug for ProblemFormat
Source§impl Default for ProblemFormat
impl Default for ProblemFormat
Source§fn default() -> ProblemFormat
fn default() -> ProblemFormat
impl Eq for ProblemFormat
Source§impl<S> FromRequestParts<S> for ProblemFormat
impl<S> FromRequestParts<S> for ProblemFormat
Source§impl PartialEq for ProblemFormat
impl PartialEq for ProblemFormat
impl StructuralPartialEq for ProblemFormat
Auto Trait Implementations§
impl Freeze for ProblemFormat
impl RefUnwindSafe for ProblemFormat
impl Send for ProblemFormat
impl Sync for ProblemFormat
impl Unpin for ProblemFormat
impl UnsafeUnpin for ProblemFormat
impl UnwindSafe for ProblemFormat
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<S, T> FromRequest<S, ViaParts> for T
impl<S, T> FromRequest<S, ViaParts> for T
Source§type Rejection = <T as FromRequestParts<S>>::Rejection
type Rejection = <T as FromRequestParts<S>>::Rejection
Source§fn from_request(
req: Request<Body>,
state: &S,
) -> impl Future<Output = Result<T, <T as FromRequest<S, ViaParts>>::Rejection>>
fn from_request( req: Request<Body>, state: &S, ) -> impl Future<Output = Result<T, <T as FromRequest<S, ViaParts>>::Rejection>>
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more