### Install axum-valid with Typed Multipart feature Source: https://github.com/gengteng/axum-valid/blob/main/README.md Installs additional dependencies for an example that uses `axum-typed-multipart`. This includes `axum_typed_multipart` and `axum-valid` with the `typed_multipart` feature enabled, alongside the base `validify` and `basic` features. ```shell cargo add axum_typed_multipart cargo add axum-valid --features validify,basic,typed_multipart --no-default-features ``` -------------------------------- ### Install axum-valid and validify crates Source: https://github.com/gengteng/axum-valid/blob/main/README.md Installs the `validify` crate for validation logic and the `axum-valid` crate with specific features for integration with Axum. The `--no-default-features` flag is used to avoid including features not explicitly needed for this basic setup. ```shell cargo add validify cargo add axum-valid --features validify,basic --no-default-features ``` -------------------------------- ### Install validator and axum-valid crates Source: https://github.com/gengteng/axum-valid/blob/main/README.md Installs the `validator` crate with the `derive` feature enabled for creating validation macros, and the `axum-valid` crate for integrating validation logic into Axum applications. ```shell cargo add validator --features derive cargo add axum-valid ``` -------------------------------- ### Validify Validation-Only with Axum Query Extraction Source: https://context7.com/gengteng/axum-valid/llms.txt Shows how to perform validation only using the 'validify' crate with Axum. This example focuses on extracting and validating query parameters without modifying the data, ensuring data integrity before processing. ```rust use axum::extract::Query; use axum::routing::get; use axum::Router; use axum_valid::Validated; use serde::Deserialize; use std::net::SocketAddr; use tokio::net::TcpListener; use validify::Validify; #[derive(Debug, Validify, Deserialize)] pub struct Paginator { #[validate(range(min = 1.0, max = 50.0))] pub page_size: usize, #[validate(range(min = 1.0))] pub page_no: usize, } pub async fn paginator_from_query(Validated(Query(paginator)): Validated>) { assert!((1..=50).contains(&paginator.page_size)); assert!((1..).contains(&paginator.page_no)); } #[tokio::main] async fn main() -> anyhow::Result<()> { let router = Router::new().route("/validated", get(paginator_from_query)); let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; axum::serve(listener, router.into_make_service()).await?; Ok(()) } ``` -------------------------------- ### Garde Validation Example Source: https://github.com/gengteng/axum-valid/blob/main/README.md Illustrates how to integrate `axum-valid` with the `garde` crate for request validation. This example showcases custom validation for pagination parameters using `garde`'s validation system and contexts. ```APIDOC ## POST /form (Garde) ### Description This endpoint processes form data for pagination, applying validation rules defined using the `garde` crate via `axum-valid`. It ensures `page_size` and `page_no` conform to predefined constraints. ### Method POST ### Endpoint /form ### Parameters #### Query Parameters None #### Request Body - **page_size** (usize) - Required - The page size, validated to be within the allowed range (e.g., 1 to 50). - **page_no** (usize) - Required - The page number, validated to be a positive integer. ### Request Example ```json { "page_size": 10, "page_no": 5 } ``` ### Response #### Success Response (200) Upon successful validation and processing, this endpoint doesn't return a specific response body. The `paginator_from_form_garde` function includes assertions for the validated parameters. #### Response Example (No content on success) ``` -------------------------------- ### Validator Enabled by Default Example Source: https://github.com/gengteng/axum-valid/blob/main/README.md Demonstrates how to use axum-valid with the `validator` crate for custom validation logic on form data. This example shows custom validation functions for page size and page number, including the use of validation contexts. ```APIDOC ## POST /form ### Description This endpoint accepts a form submission containing pagination parameters (`page_size` and `page_no`) and validates them using custom logic defined with the `validator` crate and `axum-valid`. ### Method POST ### Endpoint /form ### Parameters #### Query Parameters None #### Request Body - **page_size** (usize) - Required - The desired page size, validated against a specific range. - **page_no** (usize) - Required - The desired page number, validated to be at least 1. ### Request Example ```json { "page_size": 25, "page_no": 1 } ``` ### Response #### Success Response (200) This endpoint does not return a response body on success, but the `paginator_from_form_ex` function asserts the validity of the input. #### Response Example (No content on success) ``` -------------------------------- ### Validify Combined Modification and Validation with Axum Form Source: https://context7.com/gengteng/axum-valid/llms.txt Presents a combined approach using 'validify' for both data modification and validation in Axum, processing data from form submissions. This example ensures that data is transformed and validated, treating missing fields as potential validation errors. ```rust use axum::routing::post; use axum::{Form, Router}; use axum_valid::Validified; use serde::Deserialize; use std::net::SocketAddr; use tokio::net::TcpListener; use validify::{Payload, Validify}; #[derive(Debug, Validify, Deserialize, Payload)] pub struct Parameters { #[modify(lowercase)] #[validate(length(min = 1, max = 50))] pub v0: String, #[modify(trim)] #[validate(length(min = 1, max = 100))] pub v1: String, } pub async fn parameters_from_form(parameters: Validified>) { // Data has been modified and validated assert_eq!(parameters.v0, parameters.v0.to_lowercase()); assert_eq!(parameters.v1, parameters.v1.trim()); assert!(parameters.validate().is_ok()); } #[tokio::main] async fn main() -> anyhow::Result<()> { let router = Router::new().route("/validified", post(parameters_from_form)); let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; axum::serve(listener, router.into_make_service()).await?; Ok(()) } ``` -------------------------------- ### Axum Handlers with axum-valid Extractors (Rust) Source: https://github.com/gengteng/axum-valid/blob/main/README.md Demonstrates various Axum handlers using `axum-valid` extractors to validate and modify incoming data. It covers validation for query parameters (`Validated>`), JSON bodies (`Modified>`), form data (`Validified>`), and multipart form data (`ValidifiedByRef>`). The examples utilize the `Validify` derive macro for defining validation rules and modifications. ```rust use axum::extract::Query; use axum::routing::{get, post}; use axum::{Form, Json, Router}; use axum_typed_multipart::{TryFromMultipart, TypedMultipart}; use axum_valid::{Modified, Validated, Validified, ValidifiedByRef}; use serde::Deserialize; use std::net::SocketAddr; use tokio::net::TcpListener; use validify::{Payload, Validate, Validify}; #[derive(Debug, Validify, Deserialize)] pub struct Paginator { #[validate(range(min = 1.0, max = 50.0))] pub page_size: usize, #[validate(range(min = 1.0))] pub page_no: usize, } pub async fn paginator_from_query(Validated(Query(paginator)): Validated>) { assert!((1..=50).contains(&paginator.page_size)); assert!((1..).contains(&paginator.page_no)); } // Payload is now required for Validified. (Added in validify 1.3.0) #[derive(Debug, Validify, Deserialize, Payload)] pub struct Parameters { #[modify(lowercase)] #[validate(length(min = 1, max = 50))] pub v0: String, #[modify(trim)] #[validate(length(min = 1, max = 100))] pub v1: String, } pub async fn parameters_from_json(modified_parameters: Modified>) { assert_eq!( modified_parameters.v0, modified_parameters.v0.to_lowercase() ); assert_eq!(modified_parameters.v1, modified_parameters.v1.trim()) // but modified_parameters may be invalid } // NOTE: missing required fields will be treated as validation errors. pub async fn parameters_from_form(parameters: Validified>) { assert_eq!(parameters.v0, parameters.v0.to_lowercase()); assert_eq!(parameters.v1, parameters.v1.trim()); assert!(parameters.validate().is_ok()); } // NOTE: TypedMultipart doesn't using serde::Deserialize to construct data // we should use ValidifiedByRef instead of Validified #[derive(Debug, Validify, TryFromMultipart)] pub struct FormData { #[modify(lowercase)] #[validate(length(min = 1, max = 50))] pub v0: String, #[modify(trim)] #[validate(length(min = 1, max = 100))] pub v1: String, } pub async fn parameters_from_typed_multipart( ValidifiedByRef(TypedMultipart(data)): ValidifiedByRef>, ) { assert_eq!(data.v0, data.v0.to_lowercase()); assert_eq!(data.v1, data.v1.trim()); assert!(data.validate().is_ok()); } #[tokio::main] async fn main() -> anyhow::Result<()> { let router = Router::new() .route("/validated", get(paginator_from_query)) .route("/modified", post(parameters_from_json)) .route("/validified", post(parameters_from_form)) .route("/validified_by_ref", post(parameters_from_typed_multipart)); let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; axum::serve(listener, router.into_make_service()).await?; Ok(()) } ``` -------------------------------- ### Validify Data Modification with Axum JSON Extraction Source: https://context7.com/gengteng/axum-valid/llms.txt Illustrates data modification using 'validify' attributes in Axum, specifically for JSON payloads. This example demonstrates how to apply transformations like lowercasing strings and trimming whitespace before the data is used. ```rust use axum::routing::post; use axum::{Json, Router}; use axum_valid::Modified; use serde::Deserialize; use std::net::SocketAddr; use tokio::net::TcpListener; use validify::Validify; #[derive(Debug, Validify, Deserialize)] pub struct Parameters { #[modify(lowercase)] pub v0: String, #[modify(trim)] pub v1: String, } pub async fn parameters_from_json(modified_parameters: Modified>) { // Data has been modified but may not be valid assert_eq!( modified_parameters.v0, modified_parameters.v0.to_lowercase() ); assert_eq!(modified_parameters.v1, modified_parameters.v1.trim()); } #[tokio::main] async fn main() -> anyhow::Result<()> { let router = Router::new().route("/modified", post(parameters_from_json)); let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; axum::serve(listener, router.into_make_service()).await?; Ok(()) } ``` -------------------------------- ### Axum Data Validation with Garde Crate Source: https://github.com/gengteng/axum-valid/blob/main/README.md Shows how to integrate axum-valid with the 'garde' crate for data validation in Axum. This setup requires 'garde' with the 'derive' feature, 'axum' with the 'macros' feature, and 'axum-valid' with 'garde' and 'basic' features, disabling the default features. It also includes a note on implementing `FromRef` for states when using Garde. ```rust use axum::extract::{FromRef, Query, State}; use axum::routing::{get, post}; use axum::{Json, Router}; use axum_valid::Garde; use garde::Validate; use serde::Deserialize; use std::net::SocketAddr; use tokio::net::TcpListener; #[derive(Debug, Validate, Deserialize)] pub struct Paginator { #[garde(range(min = 1, max = 50))] pub page_size: usize, #[garde(range(min = 1))] pub page_no: usize, } pub async fn paginator_from_query(Garde(Query(paginator)): Garde>) { assert!((1..=50).contains(&paginator.page_size)); assert!((1..).contains(&paginator.page_no)); } pub async fn paginator_from_json(paginator: Garde>) { assert!((1..=50).contains(&paginator.page_size)); assert!((1..).contains(&paginator.page_no)); println!("page_no: {}, page_size: {}", paginator.page_no, paginator.page_size); } pub async fn get_state(_state: State) {} #[derive(Debug, Clone, FromRef)] pub struct MyState { state_field: i32, without_validation_arguments: () } #[tokio::main] async fn main() -> anyhow::Result<()> { let router = Router::new() .route("/query", get(paginator_from_query)) .route("/json", post(paginator_from_json)); // WARNING: If you are using Garde and also have a state, // even if that state is unrelated to Garde, // you still need to implement FromRef for (). // Tip: You can add an () field to your state and derive FromRef for it. let router = router.route("/state", get(get_state)).with_state(MyState { state_field: 1, without_validation_arguments: (), }); let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; axum::serve(listener, router.into_make_service()).await?; Ok(()) } ``` -------------------------------- ### Axum Validation with Validator Crate Source: https://github.com/gengteng/axum-valid/blob/main/README.md Example of using the 'validator' crate with Axum for form validation. It showcases custom validation functions ('validate_page_size', 'validate_page_no') that use context ('PaginatorValidArgs') to enforce validation rules on struct fields. The `ValidEx` extractor is used to automatically validate incoming form data. ```rust use axum::routing::post; use axum::{Form, Router}; use axum_valid::ValidEx; use serde::Deserialize; use std::net::SocketAddr; use std::ops::{RangeFrom, RangeInclusive}; use tokio::net::TcpListener; use validator::{Validate, ValidationError}; // NOTE: When some fields use custom validation functions with arguments, // `#[derive(Validate)]` will implement `ValidateArgs` instead of `Validate` for the type. #[derive(Debug, Validate, Deserialize)] #[validate(context = PaginatorValidArgs)] // context is required pub struct Paginator { #[validate(custom(function = "validate_page_size", use_context))] pub page_size: usize, #[validate(custom(function = "validate_page_no", use_context))] pub page_no: usize, } fn validate_page_size(v: usize, args: &PaginatorValidArgs) -> Result<(), ValidationError> { args.page_size_range .contains(&v) .then_some(()) .ok_or_else(|| ValidationError::new("page_size is out of range")) } fn validate_page_no(v: usize, args: &PaginatorValidArgs) -> Result<(), ValidationError> { args.page_no_range .contains(&v) .then_some(()) .ok_or_else(|| ValidationError::new("page_no is out of range")) } // NOTE: Clone is required, consider using Arc to reduce deep copying costs. #[derive(Debug, Clone)] pub struct PaginatorValidArgs { page_size_range: RangeInclusive, page_no_range: RangeFrom, } pub async fn paginator_from_form_ex(ValidEx(Form(paginator)): ValidEx>) { assert!((1..=50).contains(&paginator.page_size)); assert!((1..).contains(&paginator.page_no)); } #[tokio::main] async fn main() -> anyhow::Result<()> { let router = Router::new() .route("/form", post(paginator_from_form_ex)) .with_state(PaginatorValidArgs { page_size_range: 1..=50, page_no_range: 1.., }); // NOTE: The PaginatorValidArgs can also be stored in a XxxState, // make sure it implements FromRef. let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; axum::serve(listener, router.into_make_service()).await?; Ok(()) } ``` -------------------------------- ### Axum-Valid: Basic Validation with Validator Crate Source: https://context7.com/gengteng/axum-valid/llms.txt Demonstrates basic data validation using the 'validator' crate with Axum's 'Valid' extractor. It shows how to validate query parameters and JSON request bodies, returning HTTP errors on failure. ```rust use axum::extract::Query; use axum::routing::{get, post}; use axum::{Json, Router}; use axum_valid::Valid; use serde::Deserialize; use std::net::SocketAddr; use tokio::net::TcpListener; use validator::Validate; #[derive(Debug, Validate, Deserialize)] pub struct Paginator { #[validate(range(min = 1, max = 50))] pub page_size: usize, #[validate(range(min = 1))] pub page_no: usize, } // Query parameter validation pub async fn paginator_from_query(Valid(Query(paginator)): Valid>) { assert!((1..=50).contains(&paginator.page_size)); assert!((1..).contains(&paginator.page_no)); } // JSON body validation with automatic dereferencing pub async fn paginator_from_json(paginator: Valid>) { assert!((1..=50).contains(&paginator.page_size)); assert!((1..).contains(&paginator.page_no)); println!("page_no: {}, page_size: {}", paginator.page_no, paginator.page_size); } #[tokio::main] async fn main() -> anyhow::Result<()> { let router = Router::new() .route("/query", get(paginator_from_query)) .route("/json", post(paginator_from_json)); let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; axum::serve(listener, router.into_make_service()).await?; Ok(()) } ``` -------------------------------- ### Garde Stateful Validation with Axum Context Source: https://context7.com/gengteng/axum-valid/llms.txt Demonstrates stateful validation using the 'garde' crate within an Axum application. It shows how to extract validated data from query parameters and JSON bodies, leveraging a custom state struct that implements `FromRef` for context. ```rust use axum::extract::{FromRef, Query, State}; use axum::routing::{get, post}; use axum::{Json, Router}; use axum_valid::Garde; use garde::Validate; use serde::Deserialize; use std::net::SocketAddr; use tokio::net::TcpListener; #[derive(Debug, Validate, Deserialize)] pub struct Paginator { #[garde(range(min = 1, max = 50))] pub page_size: usize, #[garde(range(min = 1))] pub page_no: usize, } pub async fn paginator_from_query(Garde(Query(paginator)): Garde>) { assert!((1..=50).contains(&paginator.page_size)); assert!((1..).contains(&paginator.page_no)); } pub async fn paginator_from_json(paginator: Garde>) { assert!((1..=50).contains(&paginator.page_size)); assert!((1..).contains(&paginator.page_no)); println!("page_no: {}\npage_size: {}", paginator.page_no, paginator.page_size); } pub async fn get_state(_state: State) {} // FromRef implementation required for using Garde with state #[derive(Debug, Clone, FromRef)] pub struct MyState { state_field: i32, without_validation_arguments: (), } #[tokio::main] async fn main() -> anyhow::Result<()> { let router = Router::new() .route("/query", get(paginator_from_query)) .route("/json", post(paginator_from_json)) .route("/state", get(get_state)) .with_state(MyState { state_field: 1, without_validation_arguments: (), }); let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; axum::serve(listener, router.into_make_service()).await?; Ok(()) } ``` -------------------------------- ### Axum-Valid: Argument-Based Validation with Validator Crate Source: https://context7.com/gengteng/axum-valid/llms.txt Illustrates context-dependent validation using Axum's 'ValidEx' extractor and the 'validator' crate. This enables dynamic validation rules based on application state, shown here for form data submission. ```rust use axum::routing::post; use axum::{Form, Router}; use axum_valid::ValidEx; use serde::Deserialize; use std::net::SocketAddr; use std::ops::{RangeFrom, RangeInclusive}; use tokio::net::TcpListener; use validator::{Validate, ValidationError}; #[derive(Debug, Validate, Deserialize)] #[validate(context = PaginatorValidArgs)] pub struct Paginator { #[validate(custom(function = "validate_page_size", use_context))] pub page_size: usize, #[validate(custom(function = "validate_page_no", use_context))] pub page_no: usize, } fn validate_page_size(v: usize, args: &PaginatorValidArgs) -> Result<(), ValidationError> { args.page_size_range .contains(&v) .then_some(()) .ok_or_else(|| ValidationError::new("page_size is out of range")) } fn validate_page_no(v: usize, args: &PaginatorValidArgs) -> Result<(), ValidationError> { args.page_no_range .contains(&v) .then_some(()) .ok_or_else(|| ValidationError::new("page_no is out of range")) } #[derive(Debug, Clone)] pub struct PaginatorValidArgs { page_size_range: RangeInclusive, page_no_range: RangeFrom, } pub async fn paginator_from_form_ex(ValidEx(Form(paginator)): ValidEx>) { assert!((1..=50).contains(&paginator.page_size)); assert!((1..).contains(&paginator.page_no)); } #[tokio::main] async fn main() -> anyhow::Result<()> { let router = Router::new() .route("/form", post(paginator_from_form_ex)) .with_state(PaginatorValidArgs { page_size_range: 1..=50, page_no_range: 1.., }); let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; axum::serve(listener, router.into_make_service()).await?; Ok(()) } ``` -------------------------------- ### ValidifiedByRef for Non-Serde Extractors in Rust Source: https://context7.com/gengteng/axum-valid/llms.txt Demonstrates modification and validation by reference for extractors not using serde deserialization. It utilizes `axum-valid`'s `ValidifiedByRef` with `TypedMultipart` for handling form data with custom modifications and validations. ```rust use axum::routing::post; use axum::Router; use axum_typed_multipart::{TryFromMultipart, TypedMultipart}; use axum_valid::ValidifiedByRef; use std::net::SocketAddr; use tokio::net::TcpListener; use validify::Validify; #[derive(Debug, Validify, TryFromMultipart)] pub struct FormData { #[modify(lowercase)] #[validate(length(min = 1, max = 50))] pub v0: String, #[modify(trim)] #[validate(length(min = 1, max = 100))] pub v1: String, } pub async fn parameters_from_typed_multipart( ValidifiedByRef(TypedMultipart(data)): ValidifiedByRef>, ) { assert_eq!(data.v0, data.v0.to_lowercase()); assert_eq!(data.v1, data.v1.trim()); assert!(data.validate().is_ok()); } #[tokio::main] async fn main() -> anyhow::Result<()> { let router = Router::new().route("/validified_by_ref", post(parameters_from_typed_multipart)); let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; axum::serve(listener, router.into_make_service()).await?; Ok(()) } ``` -------------------------------- ### Axum Data Validation with Validator Crate Source: https://github.com/gengteng/axum-valid/blob/main/README.md Demonstrates how to use axum-valid with the 'validator' crate to validate query parameters and JSON payloads in Axum applications. It requires adding 'validator' with the 'derive' feature and 'axum-valid'. The extractor automatically returns a 400 error on validation failure. ```rust use axum::extract::Query; use axum::routing::{get, post}; use axum::{Json, Router}; use axum_valid::Valid; use serde::Deserialize; use std::net::SocketAddr; use tokio::net::TcpListener; use validator::Validate; #[derive(Debug, Validate, Deserialize)] pub struct Paginator { #[validate(range(min = 1, max = 50))] pub page_size: usize, #[validate(range(min = 1))] pub page_no: usize, } pub async fn paginator_from_query(Valid(Query(paginator)): Valid>) { assert!((1..=50).contains(&paginator.page_size)); assert!((1..).contains(&paginator.page_no)); } pub async fn paginator_from_json(paginator: Valid>) { assert!((1..=50).paginator.page_size); assert!((1..).contains(&paginator.page_no)); // NOTE: all extractors provided support automatic dereferencing println!("page_no: {}, page_size: {}", paginator.page_no, paginator.page_size); } #[tokio::main] async fn main() -> anyhow::Result<()> { let router = Router::new() .route("/query", get(paginator_from_query)) .route("/json", post(paginator_from_json)); let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; axum::serve(listener, router.into_make_service()).await?; Ok(()) } ``` -------------------------------- ### Axum Validation with Garde Crate Source: https://github.com/gengteng/axum-valid/blob/main/README.md Demonstrates form validation in Axum using the 'garde' crate. It features custom validation logic through 'garde' attributes and functions ('validate_page_size', 'validate_page_no') that leverage context ('PaginatorValidContext'). The `Garde` extractor simplifies the process of validating incoming form data. ```rust use axum::routing::post; use axum::{Form, Router}; use axum_valid::Garde; use garde::Validate; use serde::Deserialize; use std::net::SocketAddr; use std::ops::{RangeFrom, RangeInclusive}; use tokio::net::TcpListener; #[derive(Debug, Validate, Deserialize)] #[garde(context(PaginatorValidContext))] pub struct Paginator { #[garde(custom(validate_page_size))] pub page_size: usize, #[garde(custom(validate_page_no))] pub page_no: usize, } fn validate_page_size(v: &usize, args: &PaginatorValidContext) -> garde::Result { args.page_size_range .contains(&v) .then_some(()) .ok_or_else(|| garde::Error::new("page_size is out of range")) } fn validate_page_no(v: &usize, args: &PaginatorValidContext) -> garde::Result { args.page_no_range .contains(&v) .then_some(()) .ok_or_else(|| garde::Error::new("page_no is out of range")) } #[derive(Debug, Clone)] pub struct PaginatorValidContext { page_size_range: RangeInclusive, page_no_range: RangeFrom, } pub async fn paginator_from_form_garde(Garde(Form(paginator)): Garde>) { assert!((1..=50).contains(&paginator.page_size)); assert!((1..).contains(&paginator.page_no)); } #[tokio::main] async fn main() -> anyhow::Result<()> { let router = Router::new() .route("/form", post(paginator_from_form_garde)) .with_state(PaginatorValidContext { page_size_range: 1..=50, page_no_range: 1.., }); // NOTE: The PaginatorValidContext can also be stored in a XxxState, // make sure it implements FromRef. // Consider using Arc to reduce deep copying costs. let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; axum::serve(listener, router.into_make_service()).await?; Ok(()) } ``` -------------------------------- ### Form Data Validation in Rust Source: https://context7.com/gengteng/axum-valid/llms.txt Shows validation of URL-encoded form submissions with automatic error handling using `axum-valid`'s `Valid` extractor. It defines a `LoginForm` struct with email and password validation rules. ```rust use axum::routing::post; use axum::{Form, Router}; use axum_valid::Valid; use serde::Deserialize; use std::net::SocketAddr; use tokio::net::TcpListener; use validator::Validate; #[derive(Validate, Deserialize)] pub struct LoginForm { #[validate(email)] pub email: String, #[validate(length(min = 8))] pub password: String, } async fn handle_login(Valid(Form(login)): Valid>) { println!("Valid login attempt: {}", login.email); // Process validated login } #[tokio::main] async fn main() -> anyhow::Result<()> { let router = Router::new().route("/login", post(handle_login)); let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; axum::serve(listener, router.into_make_service()).await?; Ok(()) } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.