Validated Form Extractor (`VForm<T>`)
10/30/24About 1 min
Validated Form Extractor (VForm<T>)
When handling form submissions, we need not only to parse data from the request but also to validate that data (checking required fields, email format, string length, etc.).
Traditional approaches manually call validation logic inside handlers, which leads to verbose, repetitive code.
To solve this, we created a custom Axum extractor — VForm<T> (Validated Form). It combines form parsing and data validation into one step, allowing you to declare everything in the function signature.
Core Concept
If your handler code executes, then the data in VForm is guaranteed to be valid data that has passed all validation rules.
How to Use VForm<T>
Step 1: Define Your Form Struct
use serde::Deserialize;
use validator::Validate;
#[derive(Deserialize, Validate)]
pub struct CreateArticleForm {
// ... field definitions
}Step 2: Add Validation Annotations
#[derive(Deserialize, Validate)]
pub struct CreateArticleForm {
#[validate(length(min = 1, message = "Article title cannot be empty"))]
pub title: String,
#[validate(length(min = 10, message = "Content needs at least 10 characters"))]
pub content: String,
#[validate(range(min = 0, max = 1, message = "Invalid status value"))]
pub status: i32,
}Step 3: Use VForm<T> in Handler
use crate::common::validatedform::VForm;
pub async fn create_article(
VForm(form): VForm<CreateArticleForm>,
) -> Response {
// form.title, form.content, form.status are guaranteed valid
ArticleService::create(form).await
}Core Advantages
- Clean Code: Validation logic is declarative (annotations), not imperative (if-checks)
- Separation of Concerns: Validation belongs to the data structure, not the handler
- Secure by Default: Eliminates the risk of forgetting validation
- Unified Error Handling: Both parse failures and validation errors return a consistent
400 Bad Requestresponse
Implementation: FromRequest & ServerError
VForm implements Axum's FromRequest trait. When Axum sees VForm<T> in handler parameters:
- Use
Form<T>to parse the request body - Call
.validate()on the parsed data - Return validated data wrapped in
VForm, or a unified error response