mirror of
https://github.com/tokio-rs/axum.git
synced 2024-12-28 07:20:12 +01:00
Expand accepted content types for JSON requests (#378)
* Expand accepted content types for JSON requests Fixes https://github.com/tokio-rs/axum/issues/375 * changelog * add test for content type without spaces * Don't accept `text/json` * small clean up
This commit is contained in:
parent
fe4c0ae386
commit
ce5834ab80
4 changed files with 60 additions and 5 deletions
|
@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
# Unreleased
|
# Unreleased
|
||||||
|
|
||||||
- Improve performance of `BoxRoute` ([#339])
|
- Improve performance of `BoxRoute` ([#339])
|
||||||
|
- Expand accepted content types for JSON requests ([#378])
|
||||||
- **breaking:** Automatically do percent decoding in `extract::Path`
|
- **breaking:** Automatically do percent decoding in `extract::Path`
|
||||||
([#272])
|
([#272])
|
||||||
- **breaking:** `Router::boxed` now the inner service to implement `Clone` and
|
- **breaking:** `Router::boxed` now the inner service to implement `Clone` and
|
||||||
|
@ -19,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
[#339]: https://github.com/tokio-rs/axum/pull/339
|
[#339]: https://github.com/tokio-rs/axum/pull/339
|
||||||
[#286]: https://github.com/tokio-rs/axum/pull/286
|
[#286]: https://github.com/tokio-rs/axum/pull/286
|
||||||
[#272]: https://github.com/tokio-rs/axum/pull/272
|
[#272]: https://github.com/tokio-rs/axum/pull/272
|
||||||
|
[#378]: https://github.com/tokio-rs/axum/pull/378
|
||||||
|
|
||||||
# 0.2.8 (07. October, 2021)
|
# 0.2.8 (07. October, 2021)
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ members = ["examples/*"]
|
||||||
default = ["http1", "json", "tower-log"]
|
default = ["http1", "json", "tower-log"]
|
||||||
http1 = ["hyper/http1"]
|
http1 = ["hyper/http1"]
|
||||||
http2 = ["hyper/http2"]
|
http2 = ["hyper/http2"]
|
||||||
json = ["serde_json"]
|
json = ["serde_json", "mime"]
|
||||||
multipart = ["multer", "mime"]
|
multipart = ["multer", "mime"]
|
||||||
tower-log = ["tower/log"]
|
tower-log = ["tower/log"]
|
||||||
ws = ["tokio-tungstenite", "sha-1", "base64"]
|
ws = ["tokio-tungstenite", "sha-1", "base64"]
|
||||||
|
|
33
src/json.rs
33
src/json.rs
|
@ -1,6 +1,6 @@
|
||||||
use crate::BoxError;
|
use crate::BoxError;
|
||||||
use crate::{
|
use crate::{
|
||||||
extract::{has_content_type, rejection::*, take_body, FromRequest, RequestParts},
|
extract::{rejection::*, take_body, FromRequest, RequestParts},
|
||||||
response::IntoResponse,
|
response::IntoResponse,
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
@ -103,7 +103,7 @@ where
|
||||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||||
use bytes::Buf;
|
use bytes::Buf;
|
||||||
|
|
||||||
if has_content_type(req, "application/json")? {
|
if json_content_type(req)? {
|
||||||
let body = take_body(req)?;
|
let body = take_body(req)?;
|
||||||
|
|
||||||
let buf = hyper::body::aggregate(body)
|
let buf = hyper::body::aggregate(body)
|
||||||
|
@ -119,6 +119,35 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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> {
|
impl<T> Deref for Json<T> {
|
||||||
type Target = T;
|
type Target = T;
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
handler::{any, delete, get, on, patch, post, Handler},
|
handler::{any, delete, get, on, patch, post, Handler},
|
||||||
response::IntoResponse,
|
response::IntoResponse,
|
||||||
routing::MethodFilter,
|
routing::MethodFilter,
|
||||||
service, Router,
|
service, Json, Router,
|
||||||
};
|
};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use http::{
|
use http::{
|
||||||
|
@ -15,7 +15,7 @@ use http::{
|
||||||
};
|
};
|
||||||
use hyper::Body;
|
use hyper::Body;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::json;
|
use serde_json::{json, Value};
|
||||||
use std::future::Ready;
|
use std::future::Ready;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
@ -515,6 +515,30 @@ async fn captures_dont_match_empty_segments() {
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn json_content_types() {
|
||||||
|
async fn valid_json_content_type(content_type: &str) -> bool {
|
||||||
|
println!("testing {:?}", content_type);
|
||||||
|
|
||||||
|
let app = Router::new().route("/", post(|Json(_): Json<Value>| async {}));
|
||||||
|
|
||||||
|
let res = TestClient::new(app)
|
||||||
|
.post("/")
|
||||||
|
.header("content-type", content_type)
|
||||||
|
.body("{}")
|
||||||
|
.send()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
res.status() == StatusCode::OK
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(valid_json_content_type("application/json").await);
|
||||||
|
assert!(valid_json_content_type("application/json; charset=utf-8").await);
|
||||||
|
assert!(valid_json_content_type("application/json;charset=utf-8").await);
|
||||||
|
assert!(valid_json_content_type("application/cloudevents+json").await);
|
||||||
|
assert!(!valid_json_content_type("text/json").await);
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn assert_send<T: Send>() {}
|
pub(crate) fn assert_send<T: Send>() {}
|
||||||
pub(crate) fn assert_sync<T: Sync>() {}
|
pub(crate) fn assert_sync<T: Sync>() {}
|
||||||
pub(crate) fn assert_unpin<T: Unpin>() {}
|
pub(crate) fn assert_unpin<T: Unpin>() {}
|
||||||
|
|
Loading…
Reference in a new issue