doc: update README.md
This commit is contained in:
98
README.md
98
README.md
@@ -11,8 +11,7 @@
|
|||||||
|
|
||||||
**axum-valid** is a library that provides data validation extractors for the Axum web framework.
|
**axum-valid** is a library that provides data validation extractors for the Axum web framework.
|
||||||
It integrates **validator**, **garde** and **validify**, three popular validation crates in the Rust ecosystem, to offer
|
It integrates **validator**, **garde** and **validify**, three popular validation crates in the Rust ecosystem, to offer
|
||||||
convenient
|
convenient validation and data handling extractors for Axum applications.
|
||||||
validation and data handling extractors for Axum applications.
|
|
||||||
|
|
||||||
## 🚀 Basic usage
|
## 🚀 Basic usage
|
||||||
|
|
||||||
@@ -39,30 +38,30 @@ use tokio::net::TcpListener;
|
|||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|
||||||
#[derive(Debug, Validate, Deserialize)]
|
#[derive(Debug, Validate, Deserialize)]
|
||||||
pub struct Pager {
|
pub struct Paginator {
|
||||||
#[validate(range(min = 1, max = 50))]
|
#[validate(range(min = 1, max = 50))]
|
||||||
pub page_size: usize,
|
pub page_size: usize,
|
||||||
#[validate(range(min = 1))]
|
#[validate(range(min = 1))]
|
||||||
pub page_no: usize,
|
pub page_no: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn pager_from_query(Valid(Query(pager)): Valid<Query<Pager>>) {
|
pub async fn paginator_from_query(Valid(Query(paginator)): Valid<Query<Paginator>>) {
|
||||||
assert!((1..=50).contains(&pager.page_size));
|
assert!((1..=50).contains(&paginator.page_size));
|
||||||
assert!((1..).contains(&pager.page_no));
|
assert!((1..).contains(&paginator.page_no));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn pager_from_json(pager: Valid<Json<Pager>>) {
|
pub async fn paginator_from_json(paginator: Valid<Json<Paginator>>) {
|
||||||
assert!((1..=50).contains(&pager.page_size));
|
assert!((1..=50).contains(&paginator.page_size));
|
||||||
assert!((1..).contains(&pager.page_no));
|
assert!((1..).contains(&paginator.page_no));
|
||||||
// NOTE: all extractors provided support automatic dereferencing
|
// NOTE: all extractors provided support automatic dereferencing
|
||||||
println!("page_no: {}, page_size: {}", pager.page_no, pager.page_size);
|
println!("page_no: {}, page_size: {}", paginator.page_no, paginator.page_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
let router = Router::new()
|
let router = Router::new()
|
||||||
.route("/query", get(pager_from_query))
|
.route("/query", get(paginator_from_query))
|
||||||
.route("/json", post(pager_from_json));
|
.route("/json", post(paginator_from_json));
|
||||||
let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?;
|
let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?;
|
||||||
axum::serve(listener, router.into_make_service()).await?;
|
axum::serve(listener, router.into_make_service()).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -78,6 +77,7 @@ occur, the outer extractor will automatically return 400 with validation errors
|
|||||||
|
|
||||||
```shell
|
```shell
|
||||||
cargo add garde --features derive
|
cargo add garde --features derive
|
||||||
|
cargo add axum --features macros # for FromRef derive macro
|
||||||
cargo add axum-valid --features garde,basic --no-default-features
|
cargo add axum-valid --features garde,basic --no-default-features
|
||||||
# excluding validator
|
# excluding validator
|
||||||
```
|
```
|
||||||
@@ -95,22 +95,22 @@ use std::net::SocketAddr;
|
|||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
|
|
||||||
#[derive(Debug, Validate, Deserialize)]
|
#[derive(Debug, Validate, Deserialize)]
|
||||||
pub struct Pager {
|
pub struct Paginator {
|
||||||
#[garde(range(min = 1, max = 50))]
|
#[garde(range(min = 1, max = 50))]
|
||||||
pub page_size: usize,
|
pub page_size: usize,
|
||||||
#[garde(range(min = 1))]
|
#[garde(range(min = 1))]
|
||||||
pub page_no: usize,
|
pub page_no: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn pager_from_query(Garde(Query(pager)): Garde<Query<Pager>>) {
|
pub async fn paginator_from_query(Garde(Query(paginator)): Garde<Query<Paginator>>) {
|
||||||
assert!((1..=50).contains(&pager.page_size));
|
assert!((1..=50).contains(&paginator.page_size));
|
||||||
assert!((1..).contains(&pager.page_no));
|
assert!((1..).contains(&paginator.page_no));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn pager_from_json(pager: Garde<Json<Pager>>) {
|
pub async fn paginator_from_json(paginator: Garde<Json<Paginator>>) {
|
||||||
assert!((1..=50).contains(&pager.page_size));
|
assert!((1..=50).contains(&paginator.page_size));
|
||||||
assert!((1..).contains(&pager.page_no));
|
assert!((1..).contains(&paginator.page_no));
|
||||||
println!("page_no: {}, page_size: {}", pager.page_no, pager.page_size);
|
println!("page_no: {}, page_size: {}", paginator.page_no, paginator.page_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_state(_state: State<MyState>) {}
|
pub async fn get_state(_state: State<MyState>) {}
|
||||||
@@ -124,8 +124,8 @@ pub struct MyState {
|
|||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
let router = Router::new()
|
let router = Router::new()
|
||||||
.route("/query", get(pager_from_query))
|
.route("/query", get(paginator_from_query))
|
||||||
.route("/json", post(pager_from_json));
|
.route("/json", post(paginator_from_json));
|
||||||
|
|
||||||
// WARNING: If you are using Garde and also have a state,
|
// WARNING: If you are using Garde and also have a state,
|
||||||
// even if that state is unrelated to Garde,
|
// even if that state is unrelated to Garde,
|
||||||
@@ -171,16 +171,16 @@ use tokio::net::TcpListener;
|
|||||||
use validify::{Payload, Validate, Validify};
|
use validify::{Payload, Validate, Validify};
|
||||||
|
|
||||||
#[derive(Debug, Validify, Deserialize)]
|
#[derive(Debug, Validify, Deserialize)]
|
||||||
pub struct Pager {
|
pub struct Paginator {
|
||||||
#[validate(range(min = 1.0, max = 50.0))]
|
#[validate(range(min = 1.0, max = 50.0))]
|
||||||
pub page_size: usize,
|
pub page_size: usize,
|
||||||
#[validate(range(min = 1.0))]
|
#[validate(range(min = 1.0))]
|
||||||
pub page_no: usize,
|
pub page_no: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn pager_from_query(Validated(Query(pager)): Validated<Query<Pager>>) {
|
pub async fn paginator_from_query(Validated(Query(paginator)): Validated<Query<Paginator>>) {
|
||||||
assert!((1..=50).contains(&pager.page_size));
|
assert!((1..=50).contains(&paginator.page_size));
|
||||||
assert!((1..).contains(&pager.page_no));
|
assert!((1..).contains(&paginator.page_no));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Payload is now required for Validified. (Added in validify 1.3.0)
|
// Payload is now required for Validified. (Added in validify 1.3.0)
|
||||||
@@ -233,7 +233,7 @@ pub async fn parameters_from_typed_multipart(
|
|||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
let router = Router::new()
|
let router = Router::new()
|
||||||
.route("/validated", get(pager_from_query))
|
.route("/validated", get(paginator_from_query))
|
||||||
.route("/modified", post(parameters_from_json))
|
.route("/modified", post(parameters_from_json))
|
||||||
.route("/validified", post(parameters_from_form))
|
.route("/validified", post(parameters_from_form))
|
||||||
.route("/validified_by_ref", post(parameters_from_typed_multipart));
|
.route("/validified_by_ref", post(parameters_from_typed_multipart));
|
||||||
@@ -273,22 +273,22 @@ use validator::{Validate, ValidationError};
|
|||||||
// NOTE: When some fields use custom validation functions with arguments,
|
// NOTE: When some fields use custom validation functions with arguments,
|
||||||
// `#[derive(Validate)]` will implement `ValidateArgs` instead of `Validate` for the type.
|
// `#[derive(Validate)]` will implement `ValidateArgs` instead of `Validate` for the type.
|
||||||
#[derive(Debug, Validate, Deserialize)]
|
#[derive(Debug, Validate, Deserialize)]
|
||||||
#[validate(context = PagerValidArgs)] // context is required
|
#[validate(context = PaginatorValidArgs)] // context is required
|
||||||
pub struct Pager {
|
pub struct Paginator {
|
||||||
#[validate(custom(function = "validate_page_size", use_context))]
|
#[validate(custom(function = "validate_page_size", use_context))]
|
||||||
pub page_size: usize,
|
pub page_size: usize,
|
||||||
#[validate(custom(function = "validate_page_no", use_context))]
|
#[validate(custom(function = "validate_page_no", use_context))]
|
||||||
pub page_no: usize,
|
pub page_no: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_page_size(v: &usize, args: &PagerValidArgs) -> Result<(), ValidationError> {
|
fn validate_page_size(v: usize, args: &PaginatorValidArgs) -> Result<(), ValidationError> {
|
||||||
args.page_size_range
|
args.page_size_range
|
||||||
.contains(&v)
|
.contains(&v)
|
||||||
.then_some(())
|
.then_some(())
|
||||||
.ok_or_else(|| ValidationError::new("page_size is out of range"))
|
.ok_or_else(|| ValidationError::new("page_size is out of range"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_page_no(v: &usize, args: &PagerValidArgs) -> Result<(), ValidationError> {
|
fn validate_page_no(v: usize, args: &PaginatorValidArgs) -> Result<(), ValidationError> {
|
||||||
args.page_no_range
|
args.page_no_range
|
||||||
.contains(&v)
|
.contains(&v)
|
||||||
.then_some(())
|
.then_some(())
|
||||||
@@ -297,25 +297,25 @@ fn validate_page_no(v: &usize, args: &PagerValidArgs) -> Result<(), ValidationEr
|
|||||||
|
|
||||||
// NOTE: Clone is required, consider using Arc to reduce deep copying costs.
|
// NOTE: Clone is required, consider using Arc to reduce deep copying costs.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PagerValidArgs {
|
pub struct PaginatorValidArgs {
|
||||||
page_size_range: RangeInclusive<usize>,
|
page_size_range: RangeInclusive<usize>,
|
||||||
page_no_range: RangeFrom<usize>,
|
page_no_range: RangeFrom<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn pager_from_form_ex(ValidEx(Form(pager)): ValidEx<Form<Pager>>) {
|
pub async fn paginator_from_form_ex(ValidEx(Form(paginator)): ValidEx<Form<Paginator>>) {
|
||||||
assert!((1..=50).contains(&pager.page_size));
|
assert!((1..=50).contains(&paginator.page_size));
|
||||||
assert!((1..).contains(&pager.page_no));
|
assert!((1..).contains(&paginator.page_no));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
let router = Router::new()
|
let router = Router::new()
|
||||||
.route("/form", post(pager_from_form_ex))
|
.route("/form", post(paginator_from_form_ex))
|
||||||
.with_state(PagerValidArgs {
|
.with_state(PaginatorValidArgs {
|
||||||
page_size_range: 1..=50,
|
page_size_range: 1..=50,
|
||||||
page_no_range: 1..,
|
page_no_range: 1..,
|
||||||
});
|
});
|
||||||
// NOTE: The PagerValidArgs can also be stored in a XxxState,
|
// NOTE: The PaginatorValidArgs can also be stored in a XxxState,
|
||||||
// make sure it implements FromRef<XxxState>.
|
// make sure it implements FromRef<XxxState>.
|
||||||
|
|
||||||
let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?;
|
let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?;
|
||||||
@@ -347,22 +347,22 @@ use std::ops::{RangeFrom, RangeInclusive};
|
|||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
|
|
||||||
#[derive(Debug, Validate, Deserialize)]
|
#[derive(Debug, Validate, Deserialize)]
|
||||||
#[garde(context(PagerValidContext))]
|
#[garde(context(PaginatorValidContext))]
|
||||||
pub struct Pager {
|
pub struct Paginator {
|
||||||
#[garde(custom(validate_page_size))]
|
#[garde(custom(validate_page_size))]
|
||||||
pub page_size: usize,
|
pub page_size: usize,
|
||||||
#[garde(custom(validate_page_no))]
|
#[garde(custom(validate_page_no))]
|
||||||
pub page_no: usize,
|
pub page_no: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_page_size(v: &usize, args: &PagerValidContext) -> garde::Result {
|
fn validate_page_size(v: &usize, args: &PaginatorValidContext) -> garde::Result {
|
||||||
args.page_size_range
|
args.page_size_range
|
||||||
.contains(&v)
|
.contains(&v)
|
||||||
.then_some(())
|
.then_some(())
|
||||||
.ok_or_else(|| garde::Error::new("page_size is out of range"))
|
.ok_or_else(|| garde::Error::new("page_size is out of range"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_page_no(v: &usize, args: &PagerValidContext) -> garde::Result {
|
fn validate_page_no(v: &usize, args: &PaginatorValidContext) -> garde::Result {
|
||||||
args.page_no_range
|
args.page_no_range
|
||||||
.contains(&v)
|
.contains(&v)
|
||||||
.then_some(())
|
.then_some(())
|
||||||
@@ -370,25 +370,25 @@ fn validate_page_no(v: &usize, args: &PagerValidContext) -> garde::Result {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PagerValidContext {
|
pub struct PaginatorValidContext {
|
||||||
page_size_range: RangeInclusive<usize>,
|
page_size_range: RangeInclusive<usize>,
|
||||||
page_no_range: RangeFrom<usize>,
|
page_no_range: RangeFrom<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn pager_from_form_garde(Garde(Form(pager)): Garde<Form<Pager>>) {
|
pub async fn paginator_from_form_garde(Garde(Form(paginator)): Garde<Form<Paginator>>) {
|
||||||
assert!((1..=50).contains(&pager.page_size));
|
assert!((1..=50).contains(&paginator.page_size));
|
||||||
assert!((1..).contains(&pager.page_no));
|
assert!((1..).contains(&paginator.page_no));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
let router = Router::new()
|
let router = Router::new()
|
||||||
.route("/form", post(pager_from_form_garde))
|
.route("/form", post(paginator_from_form_garde))
|
||||||
.with_state(PagerValidContext {
|
.with_state(PaginatorValidContext {
|
||||||
page_size_range: 1..=50,
|
page_size_range: 1..=50,
|
||||||
page_no_range: 1..,
|
page_no_range: 1..,
|
||||||
});
|
});
|
||||||
// NOTE: The PagerValidContext can also be stored in a XxxState,
|
// NOTE: The PaginatorValidContext can also be stored in a XxxState,
|
||||||
// make sure it implements FromRef<XxxState>.
|
// make sure it implements FromRef<XxxState>.
|
||||||
// Consider using Arc to reduce deep copying costs.
|
// Consider using Arc to reduce deep copying costs.
|
||||||
let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?;
|
let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?;
|
||||||
|
|||||||
Reference in New Issue
Block a user