JSON extractor and response type.
When used as an extractor, it deserializes request bodies into types that implement serde::DeserializeOwned. When used as a response, it serializes types that implement serde::Serialize to JSON.
Type signature
pub struct Json<T>(pub T);
The Json extractor consumes the request body and deserializes it as JSON.
Since parsing JSON requires consuming the request body, the Json extractor must be last if there are multiple extractors in a handler. See the order of extractors.
Basic usage
use axum::{
extract::Json,
routing::post,
Router,
};
use serde::Deserialize;
#[derive(Deserialize)]
struct CreateUser {
email: String,
password: String,
}
async fn create_user(Json(payload): Json<CreateUser>) {
// payload is a CreateUser
let email = payload.email;
let password = payload.password;
// ...
}
let app = Router::new().route("/users", post(create_user));
Content-Type validation
The request must have a Content-Type: application/json header (or similar like application/problem+json). Requests without this header will be rejected with 415 Unsupported Media Type.
// Valid content types:
// - application/json
// - application/json; charset=utf-8
// - application/cloudevents+json
// - application/*+json (any JSON-based media type)
As a response
When returned from a handler, Json serializes the value to JSON and sets the Content-Type: application/json header automatically.
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 {
// ...
# todo!()
}
let app = Router::new().route("/users/{id}", get(get_user));
Error handling
The request doesn’t have a Content-Type: application/json header.
The body doesn’t contain syntactically valid JSON.
The body contains syntactically valid JSON, but it couldn’t be deserialized into the target type.
Buffering the request body failed.
Example error responses
// Missing content-type header:
// Status: 415 Unsupported Media Type
// Invalid JSON syntax:
// Status: 400 Bad Request
// Body: "Failed to parse the request body as JSON: EOF while parsing an object at line 1 column 1"
// Invalid data:
// Status: 422 Unprocessable Entity
// Body: "Failed to deserialize the JSON body into the target type: missing field `email` at line 1 column 25"
Response serialization errors
If serialization fails when using Json as a response, a 500 Internal Server Error will be returned with the error message as the body.
Parsing from bytes
You can construct a Json<T> from bytes directly:
use axum::Json;
use serde::Deserialize;
#[derive(Deserialize)]
struct Data {
value: String,
}
let bytes = br#"{"value": "hello"}"#;
let Json(data): Json<Data> = Json::from_bytes(bytes).unwrap();
assert_eq!(data.value, "hello");
Optional JSON
Use Option<Json<T>> to make JSON extraction optional based on the presence of a Content-Type header:
async fn handler(json: Option<Json<Data>>) {
match json {
Some(Json(data)) => {
// Request had JSON content
}
None => {
// No Content-Type header present
}
}
}