refactor doc tests
This commit is contained in:
285
README.md
285
README.md
@@ -7,11 +7,15 @@
|
||||
[](https://github.com/gengteng/axum-valid/actions/workflows/ci.yml)
|
||||
[](https://coveralls.io/github/gengteng/axum-valid?branch=main)
|
||||
|
||||
This crate provides a `Valid` type for use with `Json`, `Path`, `Query`, and `Form` extractors to validate entities implementing the `Validate` trait from the `validator` crate.
|
||||
This crate provides data validation capabilities for Axum based on the `validator` and `garde` crates.
|
||||
|
||||
A `ValidEx` type is also available. Similar to `Valid`, `ValidEx` can execute validations requiring extra arguments with types that implement the `ValidateArgs` trait from the `validator` crate.
|
||||
`validator` support is included by default. To use `garde`, enable it via the `garde` feature. `garde` alone can also be enabled by using `default-features = false`.
|
||||
|
||||
Additional extractors such as `TypedHeader`, `MsgPack`, `Yaml`, and others are supported through optional features. The complete list of supported extractors can be found in the Features section below.
|
||||
The `Valid` type enables validation using `validator` for extractors like `Json`, `Path`, `Query` and `Form`. For validations requiring extra arguments, the `ValidEx` type is offered.
|
||||
|
||||
The `Garde` type supports both argument and non-argument validations using `garde` in a unified way.
|
||||
|
||||
Additional extractors like `TypedHeader`, `MsgPack` and `Yaml` are also supported through optional features. Refer to `Features` for details.
|
||||
|
||||
## Basic usage
|
||||
|
||||
@@ -20,43 +24,101 @@ cargo add axum-valid
|
||||
```
|
||||
|
||||
```rust,no_run
|
||||
use validator::Validate;
|
||||
use serde::Deserialize;
|
||||
use axum_valid::Valid;
|
||||
use axum::extract::Query;
|
||||
use axum::{Json, Router};
|
||||
use axum::routing::{get, post};
|
||||
|
||||
#[derive(Debug, Validate, Deserialize)]
|
||||
pub struct Pager {
|
||||
#[validate(range(min = 1, max = 50))]
|
||||
pub page_size: usize,
|
||||
#[validate(range(min = 1))]
|
||||
pub page_no: usize,
|
||||
#[cfg(feature = "validator")]
|
||||
mod validator_example {
|
||||
use validator::Validate;
|
||||
use serde::Deserialize;
|
||||
use axum_valid::Valid;
|
||||
use axum::extract::Query;
|
||||
use axum::{Json, Router};
|
||||
use axum::routing::{get, post};
|
||||
|
||||
#[derive(Debug, Validate, Deserialize)]
|
||||
pub struct Pager {
|
||||
#[validate(range(min = 1, max = 50))]
|
||||
pub page_size: usize,
|
||||
#[validate(range(min = 1))]
|
||||
pub page_no: usize,
|
||||
}
|
||||
|
||||
pub async fn pager_from_query(
|
||||
Valid(Query(pager)): Valid<Query<Pager>>,
|
||||
) {
|
||||
assert!((1..=50).contains(&pager.page_size));
|
||||
assert!((1..).contains(&pager.page_no));
|
||||
}
|
||||
|
||||
pub async fn pager_from_json(
|
||||
pager: Valid<Json<Pager>>,
|
||||
) {
|
||||
assert!((1..=50).contains(&pager.page_size));
|
||||
assert!((1..).contains(&pager.page_no));
|
||||
// NOTE: support automatic dereferencing
|
||||
println!("page_no: {}, page_size: {}", pager.page_no, pager.page_size);
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn launch() -> anyhow::Result<()> {
|
||||
let router = Router::new()
|
||||
.route("/query", get(pager_from_query))
|
||||
.route("/json", post(pager_from_json));
|
||||
axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
|
||||
.serve(router.into_make_service())
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn pager_from_query(
|
||||
Valid(Query(pager)): Valid<Query<Pager>>,
|
||||
) {
|
||||
assert!((1..=50).contains(&pager.page_size));
|
||||
assert!((1..).contains(&pager.page_no));
|
||||
#[cfg(feature = "garde")]
|
||||
mod garde_example {
|
||||
use garde::Validate;
|
||||
use serde::Deserialize;
|
||||
use axum_valid::Garde;
|
||||
use axum::extract::Query;
|
||||
use axum::{Json, Router};
|
||||
use axum::routing::{get, post};
|
||||
|
||||
#[derive(Debug, Validate, Deserialize)]
|
||||
pub struct Pager {
|
||||
#[garde(range(min = 1, max = 50))]
|
||||
pub page_size: usize,
|
||||
#[garde(range(min = 1))]
|
||||
pub page_no: usize,
|
||||
}
|
||||
|
||||
pub async fn pager_from_query(
|
||||
Garde(Query(pager)): Garde<Query<Pager>>,
|
||||
) {
|
||||
assert!((1..=50).contains(&pager.page_size));
|
||||
assert!((1..).contains(&pager.page_no));
|
||||
}
|
||||
|
||||
pub async fn pager_from_json(
|
||||
pager: Garde<Json<Pager>>,
|
||||
) {
|
||||
assert!((1..=50).contains(&pager.page_size));
|
||||
assert!((1..).contains(&pager.page_no));
|
||||
// NOTE: support automatic dereferencing
|
||||
println!("page_no: {}, page_size: {}", pager.page_no, pager.page_size);
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn launch() -> anyhow::Result<()> {
|
||||
let router = Router::new()
|
||||
.route("/query", get(pager_from_query))
|
||||
.route("/json", post(pager_from_json));
|
||||
axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
|
||||
.serve(router.into_make_service())
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn pager_from_json(
|
||||
Valid(Json(pager)): Valid<Json<Pager>>,
|
||||
) {
|
||||
assert!((1..=50).contains(&pager.page_size));
|
||||
assert!((1..).contains(&pager.page_no));
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let router = Router::new()
|
||||
.route("/query", get(pager_from_query))
|
||||
.route("/json", post(pager_from_json));
|
||||
axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
|
||||
.serve(router.into_make_service())
|
||||
.await?;
|
||||
fn main() -> anyhow::Result<()> {
|
||||
#[cfg(feature = "validator")]
|
||||
validator_example::launch()?;
|
||||
#[cfg(feature = "garde")]
|
||||
garde_example::launch()?;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
@@ -70,77 +132,96 @@ To see how each extractor can be used with `Valid`, please refer to the example
|
||||
Here's a basic example of using the `ValidEx` extractor to validate data in a `Form` using arguments:
|
||||
|
||||
```rust,no_run
|
||||
use axum::routing::post;
|
||||
use axum::{Form, Router};
|
||||
use axum_valid::{Arguments, ValidEx};
|
||||
use serde::Deserialize;
|
||||
use std::ops::{RangeFrom, RangeInclusive};
|
||||
use validator::{Validate, ValidateArgs, ValidationError};
|
||||
|
||||
// NOTE: When some fields use custom validation functions with arguments,
|
||||
// `#[derive(Validate)]` will implement `ValidateArgs` instead of `Validate` for the type.
|
||||
// The validation arguments will be a tuple of all the field validation args.
|
||||
// In this example it is (&RangeInclusive<usize>, &RangeFrom<usize>).
|
||||
// For more detailed information and understanding of `ValidateArgs` and their argument types,
|
||||
// please refer to the `validator` crate documentation.
|
||||
#[derive(Debug, Validate, Deserialize)]
|
||||
pub struct Pager {
|
||||
#[validate(custom(function = "validate_page_size", arg = "&'v_a RangeInclusive<usize>"))]
|
||||
pub page_size: usize,
|
||||
#[validate(custom(function = "validate_page_no", arg = "&'v_a RangeFrom<usize>"))]
|
||||
pub page_no: usize,
|
||||
}
|
||||
|
||||
fn validate_page_size(v: usize, args: &RangeInclusive<usize>) -> Result<(), ValidationError> {
|
||||
args.contains(&v)
|
||||
.then_some(())
|
||||
.ok_or_else(|| ValidationError::new("page_size is out of range"))
|
||||
}
|
||||
|
||||
fn validate_page_no(v: usize, args: &RangeFrom<usize>) -> Result<(), ValidationError> {
|
||||
args.contains(&v)
|
||||
.then_some(())
|
||||
.ok_or_else(|| ValidationError::new("page_no is out of range"))
|
||||
}
|
||||
|
||||
// NOTE: Clone is required
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PagerValidArgs {
|
||||
page_size_range: RangeInclusive<usize>,
|
||||
page_no_range: RangeFrom<usize>,
|
||||
}
|
||||
|
||||
// NOTE: This implementation allows PagerValidArgs to be the second member of ValidEx, and provides arguments for actual validation.
|
||||
// The type mapping <Pager as ValidateArgs<'a>>::Args represents the combination of validators applied on each field of Pager.
|
||||
// get() method returns the validating arguments to be used during validation.
|
||||
impl<'a> Arguments<'a> for PagerValidArgs {
|
||||
type T = Pager;
|
||||
|
||||
// NOTE: <Pager as ValidateArgs<'a>>::Args == (&RangeInclusive<usize>, &RangeFrom<usize>)
|
||||
fn get(&'a self) -> <Pager as ValidateArgs<'a>>::Args {
|
||||
(&self.page_size_range, &self.page_no_range)
|
||||
#[cfg(feature = "validator")]
|
||||
mod validator_example {
|
||||
use axum::routing::post;
|
||||
use axum::{Form, Router};
|
||||
use axum_valid::{Arguments, ValidEx};
|
||||
use serde::Deserialize;
|
||||
use std::ops::{RangeFrom, RangeInclusive};
|
||||
use validator::{Validate, ValidateArgs, ValidationError};
|
||||
|
||||
// NOTE: When some fields use custom validation functions with arguments,
|
||||
// `#[derive(Validate)]` will implement `ValidateArgs` instead of `Validate` for the type.
|
||||
// The validation arguments will be a tuple of all the field validation args.
|
||||
// In this example it is (&RangeInclusive<usize>, &RangeFrom<usize>).
|
||||
// For more detailed information and understanding of `ValidateArgs` and their argument types,
|
||||
// please refer to the `validator` crate documentation.
|
||||
#[derive(Debug, Validate, Deserialize)]
|
||||
pub struct Pager {
|
||||
#[validate(custom(function = "validate_page_size", arg = "&'v_a RangeInclusive<usize>"))]
|
||||
pub page_size: usize,
|
||||
#[validate(custom(function = "validate_page_no", arg = "&'v_a RangeFrom<usize>"))]
|
||||
pub page_no: usize,
|
||||
}
|
||||
|
||||
fn validate_page_size(v: usize, args: &RangeInclusive<usize>) -> Result<(), ValidationError> {
|
||||
args.contains(&v)
|
||||
.then_some(())
|
||||
.ok_or_else(|| ValidationError::new("page_size is out of range"))
|
||||
}
|
||||
|
||||
fn validate_page_no(v: usize, args: &RangeFrom<usize>) -> Result<(), ValidationError> {
|
||||
args.contains(&v)
|
||||
.then_some(())
|
||||
.ok_or_else(|| ValidationError::new("page_no is out of range"))
|
||||
}
|
||||
|
||||
// NOTE: Clone is required
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PagerValidArgs {
|
||||
page_size_range: RangeInclusive<usize>,
|
||||
page_no_range: RangeFrom<usize>,
|
||||
}
|
||||
|
||||
// NOTE: This implementation allows PagerValidArgs to be the second member of ValidEx, and provides arguments for actual validation.
|
||||
// The type mapping <Pager as ValidateArgs<'a>>::Args represents the combination of validators applied on each field of Pager.
|
||||
// get() method returns the validating arguments to be used during validation.
|
||||
impl<'a> Arguments<'a> for PagerValidArgs {
|
||||
type T = Pager;
|
||||
|
||||
// NOTE: <Pager as ValidateArgs<'a>>::Args == (&RangeInclusive<usize>, &RangeFrom<usize>)
|
||||
fn get(&'a self) -> <Pager as ValidateArgs<'a>>::Args {
|
||||
(&self.page_size_range, &self.page_no_range)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn pager_from_form_ex(ValidEx(Form(pager), _): ValidEx<Form<Pager>, PagerValidArgs>) {
|
||||
assert!((1..=50).contains(&pager.page_size));
|
||||
assert!((1..).contains(&pager.page_no));
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn launch() -> anyhow::Result<()> {
|
||||
let router = Router::new()
|
||||
.route("/form", post(pager_from_form_ex))
|
||||
.with_state(PagerValidArgs {
|
||||
page_size_range: 1..=50,
|
||||
page_no_range: 1..,
|
||||
});
|
||||
// NOTE: The PagerValidArgs can also be stored in a XxxState,
|
||||
// make sure it implements FromRef<XxxState>.
|
||||
|
||||
axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
|
||||
.serve(router.into_make_service())
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn pager_from_form_ex(ValidEx(Form(pager), _): ValidEx<Form<Pager>, PagerValidArgs>) {
|
||||
assert!((1..=50).contains(&pager.page_size));
|
||||
assert!((1..).contains(&pager.page_no));
|
||||
#[cfg(feature = "garde")]
|
||||
mod garde_example {
|
||||
#[tokio::main]
|
||||
pub async fn launch() -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let router = Router::new()
|
||||
.route("/form", post(pager_from_form_ex))
|
||||
.with_state(PagerValidArgs {
|
||||
page_size_range: 1..=50,
|
||||
page_no_range: 1..,
|
||||
});
|
||||
// NOTE: The PagerValidArgs can also be stored in a XxxState,
|
||||
// make sure it implements FromRef<XxxState>.
|
||||
|
||||
axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
|
||||
.serve(router.into_make_service())
|
||||
.await?;
|
||||
fn main() -> anyhow::Result<()> {
|
||||
#[cfg(feature = "validator")]
|
||||
validator_example::launch()?;
|
||||
#[cfg(feature = "garde")]
|
||||
garde_example::launch()?;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user