Extractors: Json for Request Bodies
1. Welcome and Icebreaker:
What’s your favorite data format for APIs: JSON, XML, or something else?
2. Outline the Session:
Agenda:
- Concept Explanation: Using the
Jsonextractor for request bodies in Axum. - Live Coding: Accept and parse JSON in a POST handler.
- Tasks: Practice with your own JSON endpoints.
Learning Outcomes:
- Use the
Jsonextractor to parse JSON request bodies. - Understand how to define and deserialize Rust structs for incoming data.
Expectations: Try sending your own JSON data to the server!
3. Concept Explanation:
The Json extractor in Axum allows you to accept and parse JSON request bodies into Rust structs. Your struct must derive serde::Deserialize. This is commonly used for POST and PUT endpoints where clients send data to your API.
If deserialization fails, Axum rejects the request (typically 400 Bad Request or 422 Unprocessable Entity, depending on the error); an absent/wrong JSON content type returns 415 Unsupported Media Type. Put body-consuming extractors such as Json last in the handler arguments.
4. Live Coding Demonstration:
use axum::{extract::Json, Router, routing::post};
use serde::Deserialize;
#[derive(Deserialize)]
struct CreateUser {
username: String,
email: String,
}
async fn create_user(Json(payload): Json<CreateUser>) -> String {
format!("Created user: {} ({})", payload.username, payload.email)
}
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/users", post(create_user));
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
Best Practices:
- Validate input data before using it.
- Use clear struct field names matching the expected JSON.
Common Pitfalls:
- Forgetting to derive
Deserializefor your struct. - Sending invalid JSON from the client.
5. Tasks
- Add a
/postsPOST endpoint that accepts a JSON body withtitleandcontentfields. - Try sending invalid JSON and observe the error response.
- Add validation logic to check for empty fields.