Skip to content

Commit

Permalink
Show the errored path on JsonDataError
Browse files Browse the repository at this point in the history
Previously, it was difficult to find out the path in the deep JSON tree at
which a deserialization error occurred.  This patch makes an error message
to contain the errored path.  In order to find out the path,
I added serde_path_to_error, a new optional dependency.

Co-authored-by: Lee Dogeon <dev.moreal@gmail.com>
  • Loading branch information
dahlia and moreal committed Sep 12, 2022
1 parent 54d8439 commit 16b6672
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 3 deletions.
3 changes: 2 additions & 1 deletion axum/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ default = ["form", "http1", "json", "matched-path", "original-uri", "query", "to
form = ["dep:serde_urlencoded"]
http1 = ["hyper/http1"]
http2 = ["hyper/http2"]
json = ["dep:serde_json"]
json = ["dep:serde_json", "dep:serde_path_to_error"]
macros = ["dep:axum-macros"]
matched-path = []
multipart = ["dep:multer"]
Expand Down Expand Up @@ -57,6 +57,7 @@ base64 = { version = "0.13", optional = true }
headers = { version = "0.3.7", optional = true }
multer = { version = "2.0.0", optional = true }
serde_json = { version = "1.0", features = ["raw_value"], optional = true }
serde_path_to_error = { version = "0.1.8", optional = true }
serde_urlencoded = { version = "0.7", optional = true }
sha-1 = { version = "0.10", optional = true }
tokio-tungstenite = { version = "0.17.2", optional = true }
Expand Down
38 changes: 36 additions & 2 deletions axum/src/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,12 @@ where
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
if json_content_type(req.headers()) {
let bytes = Bytes::from_request(req, state).await?;
let deserializer = &mut serde_json::Deserializer::from_slice(&bytes);

let value = match serde_json::from_slice(&bytes) {
let value = match serde_path_to_error::deserialize(deserializer) {
Ok(value) => value,
Err(err) => {
let rejection = match err.classify() {
let rejection = match err.inner().classify() {
serde_json::error::Category::Data => JsonDataError::from_err(err).into(),
serde_json::error::Category::Syntax | serde_json::error::Category::Eof => {
JsonSyntaxError::from_err(err).into()
Expand Down Expand Up @@ -296,4 +297,37 @@ mod tests {

assert_eq!(res.status(), StatusCode::BAD_REQUEST);
}

#[derive(Deserialize)]
struct Foo {
#[allow(dead_code)]
a: i32,
#[allow(dead_code)]
b: Vec<Bar>,
}

#[derive(Deserialize)]
struct Bar {
#[allow(dead_code)]
x: i32,
#[allow(dead_code)]
y: i32,
}

#[tokio::test]
async fn invalid_json_data() {
let app = Router::new().route("/", post(|_: Json<Foo>| async {}));

let client = TestClient::new(app);
let res = client
.post("/")
.body("{\"a\": 1, \"b\": [{\"x\": 2}]}")
.header("content-type", "application/json")
.send()
.await;

assert_eq!(res.status(), StatusCode::UNPROCESSABLE_ENTITY);
let body_text = res.text().await;
assert!(body_text.contains("b[0]"), "body: {:?}", body_text);
}
}

0 comments on commit 16b6672

Please sign in to comment.