Crate axum_valid

source ·
Expand description

axum-valid

crates.io crates.io download LICENSE dependency status GitHub Workflow Status Coverage Status

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.

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.

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.

Basic usage

cargo add axum-valid
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(
    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?;
    Ok(())
}

When validation errors occur, the extractor will automatically return 400 with validation errors as the HTTP message body.

To see how each extractor can be used with Valid, please refer to the example in the documentation of the corresponding module.

Argument-Based Validation

Here’s a basic example of using the ValidEx extractor to validate data in a Form using arguments:

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]
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?;
    Ok(())
}

Current module documentation predominantly showcases Valid examples, the usage of ValidEx is analogous.

Features

FeatureDescriptionModuleDefaultExampleTests
defaultEnables support for Path, Query, Json and Formpath, query, json, form
jsonEnables support for Jsonjson
queryEnables support for Queryquery
formEnables support for Formform
typed_headerEnables support for TypedHeadertyped_header
typed_multipartEnables support for TypedMultipart and BaseMultipart from axum_typed_multiparttyped_multipart
msgpackEnables support for MsgPack and MsgPackRaw from axum-msgpackmsgpack
yamlEnables support for Yaml from axum-yamlyaml
extraEnables support for Cached, WithRejection from axum-extraextra
extra_typed_pathEnables support for T: TypedPath from axum-extraextra::typed_path
extra_queryEnables support for Query from axum-extraextra::query
extra_formEnables support for Form from axum-extraextra::form
extra_protobufEnables support for Protobuf from axum-extraextra::protobuf
all_extra_typesEnables support for all extractors above from axum-extraN/A
all_typesEnables support for all extractors aboveN/A
422Use 422 Unprocessable Entity instead of 400 Bad Request as the status code when validation failsVALIDATION_ERROR_STATUS
into_jsonValidation errors will be serialized into JSON format and returned as the HTTP bodyN/A
fullEnables all featuresN/A

Compatibility

To determine the compatible versions of axum-valid, axum-extra, axum-yaml and other dependencies that work together, please refer to the dependencies listed in the Cargo.toml file. The version numbers listed there will indicate the compatible versions.

License

This project is licensed under the MIT License.

References

Modules

  • Support for extractors from axum-extra
  • Support for Form<T>
  • Support for Json<T>
  • Support for MsgPack<T> and MsgPackRaw<T> from axum-msgpack
  • Support for Path<T>
  • Support for Query<T>
  • Support for TypedHeader<T>
  • Support for TypedMultipart<T> and BaseMultipart<T, R> from axum_typed_multipart
  • Support for Yaml<T> from axum-yaml

Structs

Enums

  • ValidRejection is returned when the Valid extractor fails.

Constants

Traits

  • Arguments provides the validation arguments for the data type T.
  • Trait for types that can supply a reference that can be validated.
  • Trait for types that can supply a reference that can be validated using arguments.