Files
axum/src/json.rs
T
David Pedersen 7692baf837 Reorganize method routers for handlers and services (#405)
* Re-organize method routing for handlers

* Re-organize method routing for services

* changelog
2021-10-24 20:05:16 +00:00

198 lines
4.8 KiB
Rust

use crate::BoxError;
use crate::{
extract::{rejection::*, take_body, FromRequest, RequestParts},
response::IntoResponse,
};
use async_trait::async_trait;
use bytes::Bytes;
use http::{
header::{self, HeaderValue},
StatusCode,
};
use http_body::Full;
use hyper::Response;
use serde::{de::DeserializeOwned, Serialize};
use std::{
convert::Infallible,
ops::{Deref, DerefMut},
};
/// JSON Extractor / Response.
///
/// When used as an extractor, it can deserialize request bodies into some type that
/// implements [`serde::Deserialize`]. If the request body cannot be parsed, or it does not contain
/// the `Content-Type: application/json` header, it will reject the request and return a
/// `400 Bad Request` response.
///
/// # Extractor example
///
/// ```rust,no_run
/// use axum::{
/// extract,
/// routing::post,
/// Router,
/// };
/// use serde::Deserialize;
///
/// #[derive(Deserialize)]
/// struct CreateUser {
/// email: String,
/// password: String,
/// }
///
/// async fn create_user(extract::Json(payload): extract::Json<CreateUser>) {
/// // payload is a `CreateUser`
/// }
///
/// let app = Router::new().route("/users", post(create_user));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
///
/// When used as a response, it can serialize any type that implements [`serde::Serialize`] to `JSON`,
/// and will automatically set `Content-Type: application/json` header.
///
/// # Response example
///
/// ```
/// use axum::{
/// extract::Path,
/// routing::get,
/// Router,
/// Json,
/// };
/// use serde::Serialize;
/// use uuid::Uuid;
///
/// #[derive(Serialize)]
/// struct User {
/// id: Uuid,
/// username: String,
/// }
///
/// async fn get_user(Path(user_id) : Path<Uuid>) -> Json<User> {
/// let user = find_user(user_id).await;
/// Json(user)
/// }
///
/// async fn find_user(user_id: Uuid) -> User {
/// // ...
/// # unimplemented!()
/// }
///
/// let app = Router::new().route("/users/:id", get(get_user));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
#[derive(Debug, Clone, Copy, Default)]
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
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<BoxError>,
{
type Rejection = JsonRejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
use bytes::Buf;
if json_content_type(req)? {
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())
}
}
}
fn json_content_type<B>(req: &RequestParts<B>) -> Result<bool, HeadersAlreadyExtracted> {
let content_type = if let Some(content_type) = req
.headers()
.ok_or(HeadersAlreadyExtracted)?
.get(header::CONTENT_TYPE)
{
content_type
} else {
return Ok(false);
};
let content_type = if let Ok(content_type) = content_type.to_str() {
content_type
} else {
return Ok(false);
};
let mime = if let Ok(mime) = content_type.parse::<mime::Mime>() {
mime
} else {
return Ok(false);
};
let is_json_content_type = mime.type_() == "application"
&& (mime.subtype() == "json" || mime.suffix().filter(|name| *name == "json").is_some());
Ok(is_json_content_type)
}
impl<T> Deref for Json<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for Json<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T> From<T> for Json<T> {
fn from(inner: T) -> Self {
Self(inner)
}
}
impl<T> IntoResponse for Json<T>
where
T: Serialize,
{
type Body = Full<Bytes>;
type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
let bytes = match serde_json::to_vec(&self.0) {
Ok(res) => res,
Err(err) => {
return Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.header(header::CONTENT_TYPE, "text/plain")
.body(Full::from(err.to_string()))
.unwrap();
}
};
let mut res = Response::new(Full::from(bytes));
res.headers_mut().insert(
header::CONTENT_TYPE,
HeaderValue::from_static("application/json"),
);
res
}
}