refactor: single refusal shape with openapi contract test
This commit is contained in:
101
crates/adapters/http-axum/src/extractors/json.rs
Normal file
101
crates/adapters/http-axum/src/extractors/json.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
//! JSON API extractors whose rejections are refused in the one error shape.
|
||||
|
||||
use axum::{
|
||||
extract::{
|
||||
FromRequest, FromRequestParts, Path, Query, Request,
|
||||
rejection::{JsonRejection, PathRejection, QueryRejection},
|
||||
},
|
||||
http::{StatusCode, request::Parts},
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
use crate::errors::{INVALID_INPUT, refuse};
|
||||
|
||||
/// Malformed request (bad JSON, query or path), refused as `INVALID_INPUT`.
|
||||
/// Status follows axum: 400 syntax, 415 content type, 422 data errors, 413 size.
|
||||
pub struct MalformedRequest {
|
||||
status: StatusCode,
|
||||
message: String,
|
||||
}
|
||||
|
||||
impl IntoResponse for MalformedRequest {
|
||||
fn into_response(self) -> Response {
|
||||
refuse(self.status, INVALID_INPUT, self.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JsonRejection> for MalformedRequest {
|
||||
fn from(r: JsonRejection) -> Self {
|
||||
Self {
|
||||
status: r.status(),
|
||||
message: r.body_text(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<QueryRejection> for MalformedRequest {
|
||||
fn from(r: QueryRejection) -> Self {
|
||||
Self {
|
||||
status: r.status(),
|
||||
message: r.body_text(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PathRejection> for MalformedRequest {
|
||||
fn from(r: PathRejection) -> Self {
|
||||
Self {
|
||||
status: r.status(),
|
||||
message: r.body_text(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `axum::Json` with refusals in the one error shape.
|
||||
pub struct Body<T>(pub T);
|
||||
|
||||
impl<S, T> FromRequest<S> for Body<T>
|
||||
where
|
||||
S: Send + Sync,
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
type Rejection = MalformedRequest;
|
||||
|
||||
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
|
||||
let axum::Json(v) = axum::Json::<T>::from_request(req, state).await?;
|
||||
Ok(Self(v))
|
||||
}
|
||||
}
|
||||
|
||||
/// `axum::extract::Query` with refusals in the one error shape.
|
||||
pub struct Params<T>(pub T);
|
||||
|
||||
impl<S, T> FromRequestParts<S> for Params<T>
|
||||
where
|
||||
S: Send + Sync,
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
type Rejection = MalformedRequest;
|
||||
|
||||
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
|
||||
let Query(v) = Query::<T>::from_request_parts(parts, state).await?;
|
||||
Ok(Self(v))
|
||||
}
|
||||
}
|
||||
|
||||
/// `axum::extract::Path` with refusals in the one error shape.
|
||||
pub struct PathId<T>(pub T);
|
||||
|
||||
impl<S, T> FromRequestParts<S> for PathId<T>
|
||||
where
|
||||
S: Send + Sync,
|
||||
T: DeserializeOwned + Send,
|
||||
{
|
||||
type Rejection = MalformedRequest;
|
||||
|
||||
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
|
||||
let Path(v) = Path::<T>::from_request_parts(parts, state).await?;
|
||||
Ok(Self(v))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user