mirror of
https://github.com/tokio-rs/axum.git
synced 2026-08-29 00:00:18 +02:00
Common JSON wrapper type for response and request (#140)
This commit is contained in:
@@ -1,76 +0,0 @@
|
||||
use super::{has_content_type, rejection::*, take_body, FromRequest, RequestParts};
|
||||
use async_trait::async_trait;
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::ops::Deref;
|
||||
|
||||
/// Extractor that deserializes request bodies into some type.
|
||||
///
|
||||
/// `T` is expected to implement [`serde::Deserialize`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// use axum::prelude::*;
|
||||
/// use serde::Deserialize;
|
||||
///
|
||||
/// #[derive(Deserialize)]
|
||||
/// struct CreateUser {
|
||||
/// email: String,
|
||||
/// password: String,
|
||||
/// }
|
||||
///
|
||||
/// async fn create_user(payload: extract::Json<CreateUser>) {
|
||||
/// let payload: CreateUser = payload.0;
|
||||
///
|
||||
/// // ...
|
||||
/// }
|
||||
///
|
||||
/// let app = route("/users", post(create_user));
|
||||
/// # async {
|
||||
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
/// # };
|
||||
/// ```
|
||||
///
|
||||
/// If the query string cannot be parsed it will reject the request with a `400
|
||||
/// Bad Request` response.
|
||||
///
|
||||
/// The request is required to have a `Content-Type: application/json` header.
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct Json<T>(pub T);
|
||||
|
||||
#[async_trait]
|
||||
impl<T, B> FromRequest<B> for Json<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
B: http_body::Body + Send,
|
||||
B::Data: Send,
|
||||
B::Error: Into<tower::BoxError>,
|
||||
{
|
||||
type Rejection = JsonRejection;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
use bytes::Buf;
|
||||
|
||||
if has_content_type(req, "application/json")? {
|
||||
let body = take_body(req)?;
|
||||
|
||||
let buf = hyper::body::aggregate(body)
|
||||
.await
|
||||
.map_err(InvalidJsonBody::from_err)?;
|
||||
|
||||
let value = serde_json::from_reader(buf.reader()).map_err(InvalidJsonBody::from_err)?;
|
||||
|
||||
Ok(Json(value))
|
||||
} else {
|
||||
Err(MissingJsonContentType.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Json<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -257,7 +257,6 @@ pub mod rejection;
|
||||
mod content_length_limit;
|
||||
mod extension;
|
||||
mod form;
|
||||
mod json;
|
||||
mod path;
|
||||
mod query;
|
||||
mod raw_query;
|
||||
@@ -274,7 +273,6 @@ pub use self::{
|
||||
extension::Extension,
|
||||
extractor_middleware::extractor_middleware,
|
||||
form::Form,
|
||||
json::Json,
|
||||
path::Path,
|
||||
query::Query,
|
||||
raw_query::RawQuery,
|
||||
@@ -282,6 +280,8 @@ pub use self::{
|
||||
url_params::UrlParams,
|
||||
url_params_map::UrlParamsMap,
|
||||
};
|
||||
#[doc(no_inline)]
|
||||
pub use crate::Json;
|
||||
|
||||
#[cfg(feature = "multipart")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "multipart")))]
|
||||
@@ -568,7 +568,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn has_content_type<B>(
|
||||
pub(crate) fn has_content_type<B>(
|
||||
req: &RequestParts<B>,
|
||||
expected_content_type: &str,
|
||||
) -> Result<bool, HeadersAlreadyExtracted> {
|
||||
@@ -591,6 +591,6 @@ fn has_content_type<B>(
|
||||
Ok(content_type.starts_with(expected_content_type))
|
||||
}
|
||||
|
||||
fn take_body<B>(req: &mut RequestParts<B>) -> Result<B, BodyAlreadyExtracted> {
|
||||
pub(crate) fn take_body<B>(req: &mut RequestParts<B>) -> Result<B, BodyAlreadyExtracted> {
|
||||
req.take_body().ok_or(BodyAlreadyExtracted)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user