Common JSON wrapper type for response and request (#140)

This commit is contained in:
Sunli
2021-08-07 16:07:13 +02:00
committed by GitHub
parent 3d45a97db9
commit 345163e98d
6 changed files with 162 additions and 141 deletions
-76
View File
@@ -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
View File
@@ -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)
}