Validated JSON Extractor (`VJson<T>`)
10/30/24Less than 1 minute
Validated JSON Extractor (VJson<T>)
In modern decoupled web applications, JSON is the primary data exchange format. When the backend receives a JSON request body, we not only need to deserialize it into Rust structs but also validate the data.
Similar to form handling, we created a custom Axum extractor — VJson<T> (Validated JSON), which integrates JSON deserialization and data validation seamlessly.
Core Concept
If your handler code executes, the data in VJson is guaranteed to be valid data that has passed all validation rules.
How to Use VJson<T>
Step 1: Define Your DTO
use serde::Deserialize;
use validator::Validate;
#[derive(Deserialize, Validate)]
pub struct UserLoginDTO {
// ... field definitions
}Step 2: Add Validation Annotations
#[derive(Deserialize, Validate)]
pub struct UserLoginDTO {
#[validate(email(message = "Please enter a valid email"))]
pub email: String,
#[validate(length(min = 6, max = 20, message = "Password must be 6-20 characters"))]
pub password: String,
}Step 3: Use VJson<T> in Handler
use crate::common::validatedjson::VJson;
pub async fn login(
VJson(dto): VJson<UserLoginDTO>,
) -> Response {
// dto.email and dto.password are guaranteed valid
AuthService::login(dto).await
}Core Advantages
- Clean Code: Removes validation logic from handlers
- Separation of Concerns: Data validation is handled by the DTO via annotations
- Secure by Default: Mandatory validation when using
VJson - Unified Error Handling: Consistent error format for all validation failures
Implementation: FromRequest & ServerError
VJson implements Axum's FromRequest trait:
- Use
Json<T>to parse the request body - Call
.validate()on the parsed data - Return validated data wrapped in
VJson, or a unified error response (400/422)