add examples and support for typed_path

This commit is contained in:
gengteng
2023-09-12 13:02:09 +08:00
parent 6f3915bb87
commit e7dba8fd0b
17 changed files with 904 additions and 39 deletions

View File

@@ -1,11 +1,12 @@
[package]
name = "axum-valid"
version = "0.6.0"
version = "0.7.0"
description = "Provide validator extractor for your axum application."
authors = ["GengTeng <me@gteng.org>"]
license = "MIT"
homepage = "https://github.com/gengteng/axum-valid"
repository = "https://github.com/gengteng/axum-valid"
documentation = "https://docs.rs/axum-valid"
keywords = [
"axum",
"validator",
@@ -19,6 +20,9 @@ categories = [
]
edition = "2021"
[package.metadata.docs.rs]
features = ["all_types"]
[dependencies]
axum = { version = "0.6.18", default-features = false }
validator = "0.16.0"
@@ -70,9 +74,10 @@ yaml = ["axum-yaml"]
into_json = ["json"]
422 = []
extra = ["axum-extra"]
extra_typed_path = ["axum-extra/typed-routing"]
extra_query = ["axum-extra/query"]
extra_form = ["axum-extra/form"]
extra_protobuf = ["axum-extra/protobuf"]
all_extra_types = ["extra", "extra_query", "extra_form", "extra_protobuf"]
all_extra_types = ["extra", "extra_typed_path", "extra_query", "extra_form", "extra_protobuf"]
all_types = ["json", "form", "query", "typed_header", "typed_multipart", "msgpack", "yaml", "all_extra_types"]
full = ["all_types", "422", "into_json"]

View File

@@ -10,13 +10,13 @@ This crate provides a `Valid` type that can be used in combination with `Json`,
Additional extractors like `TypedHeader`, `MsgPack`, `Yaml` etc. are supported through optional features. The full list of supported extractors is in the Features section below.
## Usage
## Basic usage
```shell
cargo add axum-valid
```
```rust
```rust,no_run
use validator::Validate;
use serde::Deserialize;
use axum_valid::Valid;
@@ -31,44 +31,58 @@ pub struct Pager {
pub page_no: usize,
}
pub async fn get_page_by_query(
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 get_page_by_json(
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](#modules) of the corresponding module.
## Features
| Feature | Description | Default | Tests |
|-----------------|------------------------------------------------------------------------------------------------------|---------|-------|
| default | Enables support for `Path`, `Query`, `Json` and `Form` | ✅ | ✅ |
| json | Enables support for `Json` | ✅ | ✅ |
| query | Enables support for `Query` | ✅ | ✅ |
| form | Enables support for `Form` | ✅ | ✅ |
| typed_header | Enables support for `TypedHeader` | ❌ | ✅ |
| typed_multipart | Enables support for `TypedMultipart` and `BaseMultipart` from `axum_typed_multipart` | ❌ | ✅ |
| msgpack | Enables support for `MsgPack` and `MsgPackRaw` from `axum-msgpack` | ❌ | ✅ |
| yaml | Enables support for `Yaml` from `axum-yaml` | ❌ | ✅ |
| extra | Enables support for `Cached`, `WithRejection` from `axum-extra` | ❌ | ✅ |
| extra_query | Enables support for `Query` from `axum-extra` | ❌ | ✅ |
| extra_form | Enables support for `Form` from `axum-extra` | ❌ | ✅ |
| extra_protobuf | Enables support for `Protobuf` from `axum-extra` | ❌ | ✅ |
| all_extra_types | Enables support for all extractors above from `axum-extra` | ❌ | ✅ |
| all_types | Enables support for all extractors above | ❌ | ✅ |
| 422 | Use `422 Unprocessable Entity` instead of `400 Bad Request` as the status code when validation fails | ❌ | ✅ |
| into_json | Validation errors will be serialized into JSON format and returned as the HTTP body | | ✅ |
| full | Enables all features | | ✅ |
| Feature | Description | Default | Example | Tests |
|------------------|------------------------------------------------------------------------------------------------------|---------|---------|-------|
| default | Enables support for `Path`, `Query`, `Json` and `Form` | ✅ | ✅ | ✅ |
| json | Enables support for `Json` | ✅ | ✅ | ✅ |
| query | Enables support for `Query` | ✅ | ✅ | ✅ |
| form | Enables support for `Form` | ✅ | ✅ | ✅ |
| typed_header | Enables support for `TypedHeader` | ❌ | ✅ | ✅ |
| typed_multipart | Enables support for `TypedMultipart` and `BaseMultipart` from `axum_typed_multipart` | ❌ | ✅ | ✅ |
| msgpack | Enables support for `MsgPack` and `MsgPackRaw` from `axum-msgpack` | ❌ | ✅ | ✅ |
| yaml | Enables support for `Yaml` from `axum-yaml` | ❌ | ✅ | ✅ |
| extra | Enables support for `Cached`, `WithRejection` from `axum-extra` | ❌ | ✅ | ✅ |
| extra_typed_path | Enables support for `T: TypedPath` from `axum-extra` | ❌ | ✅ | ✅ |
| extra_query | Enables support for `Query` from `axum-extra` | ❌ | ✅ | ✅ |
| extra_form | Enables support for `Form` from `axum-extra` | ❌ | ✅ | ✅ |
| extra_protobuf | Enables support for `Protobuf` from `axum-extra` | ❌ | ✅ | ✅ |
| all_extra_types | Enables support for all extractors above from `axum-extra` | ❌ | ✅ | ✅ |
| all_types | Enables support for all extractors above | ❌ | ✅ | ✅ |
| 422 | Use `422 Unprocessable Entity` instead of `400 Bad Request` as the status code when validation fails | ❌ | | ✅ |
| into_json | Validation errors will be serialized into JSON format and returned as the HTTP body | ❌ | | ✅ |
| full | Enables all features | ❌ | ✅ | ✅ |
## License
@@ -79,3 +93,7 @@ This project is licensed under the MIT License.
* [axum](https://crates.io/crates/axum)
* [validator](https://crates.io/crates/validator)
* [serde](https://crates.io/crates/serde)
* [axum-extra](https://crates.io/crates/axum-extra)
* [axum-yaml](https://crates.io/crates/axum-yaml)
* [axum-msgpack](https://crates.io/crates/axum-msgpack)
* [axum_typed_multipart](https://crates.io/crates/axum_typed_multipart)

View File

@@ -1,5 +1,220 @@
//! # Implementation of the `HasValidate` trait for the extractor in `axum-extra`.
//! # Support for `Cached<T>` and `WithRejection<T, R>`
//!
//! ## Feature
//!
//! Enable the `extra` feature to use `Valid<Cached<T>>`, `Valid<WithRejection<T, R>>` and `WithRejection<Valid<T>, R>`.
//!
//! ## `Valid<Cached<T>>`
//!
//! ### Usage
//!
//! 0. Implement your own extractor `T`.
//! 1. Implement `Clone` and `Validate` for your extractor type `T`.
//! 2. In your handler function, use `Valid<Cached<T>>` as some parameter's type.
//!
//! ### Example
//!
//! ```no_run
//! use axum::extract::FromRequestParts;
//! use axum::http::request::Parts;
//! use axum::response::{IntoResponse, Response};
//! use axum::routing::post;
//! use axum::Router;
//! use axum_extra::extract::Cached;
//! use axum_valid::Valid;
//! use validator::Validate;
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let router = Router::new().route("/cached", post(handler));
//! axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
//! .serve(router.into_make_service())
//! .await?;
//! Ok(())
//! }
//! async fn handler(Valid(Cached(parameter)): Valid<Cached<Parameter>>) {
//! assert!(parameter.validate().is_ok());
//! }
//! #[derive(Validate, Clone)]
//! pub struct Parameter {
//! #[validate(range(min = 5, max = 10))]
//! pub v0: i32,
//! #[validate(length(min = 1, max = 10))]
//! pub v1: String,
//! }
//!
//! pub struct ParameterRejection;
//!
//! impl IntoResponse for ParameterRejection {
//! fn into_response(self) -> Response {
//! todo!()
//! }
//! }
//!
//! #[axum::async_trait]
//! impl<S> FromRequestParts<S> for Parameter
//! where
//! S: Send + Sync,
//! {
//! type Rejection = ParameterRejection;
//!
//! async fn from_request_parts(_parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
//! todo!()
//! }
//! }
//! ```
//!
//! ## `Valid<WithRejection<T, R>>`
//!
//! ### Usage
//!
//! 0. Implement your own extractor `T` and rejection type `R`.
//! 1. Implement `Validate` for your extractor type `T`.
//! 2. In your handler function, use `Valid<WithRejection<T, R>>` as some parameter's type.
//!
//! ### Example
//!
//! ```no_run
//! use axum::extract::FromRequestParts;
//! use axum::http::request::Parts;
//! use axum::http::StatusCode;
//! use axum::response::{IntoResponse, Response};
//! use axum::routing::post;
//! use axum::Router;
//! use axum_extra::extract::WithRejection;
//! use axum_valid::Valid;
//! use validator::Validate;
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let router = Router::new().route("/valid_with_rejection", post(handler));
//! axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
//! .serve(router.into_make_service())
//! .await?;
//! Ok(())
//! }
//! async fn handler(
//! Valid(WithRejection(parameter, _)): Valid<
//! WithRejection<Parameter, ValidWithRejectionRejection>,
//! >,
//! ) {
//! assert!(parameter.validate().is_ok());
//! }
//!
//! #[derive(Validate)]
//! pub struct Parameter {
//! #[validate(range(min = 5, max = 10))]
//! pub v0: i32,
//! #[validate(length(min = 1, max = 10))]
//! pub v1: String,
//! }
//!
//! pub struct ValidWithRejectionRejection;
//!
//! impl IntoResponse for ValidWithRejectionRejection {
//! fn into_response(self) -> Response {
//! StatusCode::BAD_REQUEST.into_response()
//! }
//! }
//!
//! #[axum::async_trait]
//! impl<S> FromRequestParts<S> for Parameter
//! where
//! S: Send + Sync,
//! {
//! type Rejection = ValidWithRejectionRejection;
//!
//! async fn from_request_parts(_parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
//! todo!()
//! }
//! }
//! ```
//!
//! ## `WithRejection<Valid<T>, R>`
//!
//! ### Usage
//!
//! 0. Implement your own extractor `T` and rejection type `R`.
//! 1. Implement `Validate` and `HasValidate` for your extractor type `T`.
//! 2. Implement `From<ValidRejection<T::Rejection>>` for `R`.
//! 3. In your handler function, use `WithRejection<Valid<T>, R>` as some parameter's type.
//!
//! ### Example
//!
//! ```no_run
//! use axum::extract::FromRequestParts;
//! use axum::http::request::Parts;
//! use axum::response::{IntoResponse, Response};
//! use axum::routing::post;
//! use axum::Router;
//! use axum_extra::extract::WithRejection;
//! use axum_valid::{HasValidate, Valid, ValidRejection};
//! use validator::Validate;
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let router = Router::new().route("/with_rejection_valid", post(handler));
//! axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
//! .serve(router.into_make_service())
//! .await?;
//! Ok(())
//! }
//! async fn handler(
//! WithRejection(Valid(parameter), _): WithRejection<
//! Valid<Parameter>,
//! WithRejectionValidRejection,
//! >,
//! ) {
//! assert!(parameter.validate().is_ok());
//! }
//!
//! #[derive(Validate)]
//! pub struct Parameter {
//! #[validate(range(min = 5, max = 10))]
//! pub v0: i32,
//! #[validate(length(min = 1, max = 10))]
//! pub v1: String,
//! }
//!
//! impl HasValidate for Parameter {
//! type Validate = Self;
//!
//! fn get_validate(&self) -> &Self::Validate {
//! self
//! }
//! }
//!
//! pub struct ParameterRejection;
//!
//! impl IntoResponse for ParameterRejection {
//! fn into_response(self) -> Response {
//! todo!()
//! }
//! }
//!
//! #[axum::async_trait]
//! impl<S> FromRequestParts<S> for Parameter
//! where
//! S: Send + Sync,
//! {
//! type Rejection = ParameterRejection;
//!
//! async fn from_request_parts(_parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
//! todo!()
//! }
//! }
//!
//! pub struct WithRejectionValidRejection;
//!
//! impl From<ValidRejection<ParameterRejection>> for WithRejectionValidRejection {
//! fn from(_inner: ValidRejection<ParameterRejection>) -> Self {
//! todo!()
//! }
//! }
//!
//! impl IntoResponse for WithRejectionValidRejection {
//! fn into_response(self) -> Response {
//! todo!()
//! }
//! }
//! ```
#[cfg(feature = "extra_form")]
pub mod form;
@@ -7,6 +222,8 @@ pub mod form;
pub mod protobuf;
#[cfg(feature = "extra_query")]
pub mod query;
#[cfg(feature = "extra_typed_path")]
pub mod typed_path;
use crate::HasValidate;
use axum_extra::extract::{Cached, WithRejection};

View File

@@ -1,5 +1,42 @@
//! # Implementation of the `HasValidate` trait for the `Form` extractor in `axum-extra`.
//! # Support for `Form<T>` from `axum-extra`
//!
//! ## Feature
//!
//! Enable the `extra_form` feature to use `Valid<Form<T>>`.
//!
//! ## Usage
//!
//! 1. Implement `Deserialize` and `Validate` for your data type `T`.
//! 2. In your handler function, use `Valid<Form<T>>` as some parameter's type.
//!
//! ## Example
//!
//! ```no_run
//! use axum::routing::post;
//! use axum::Router;
//! use axum_extra::extract::Form;
//! use axum_valid::Valid;
//! use serde::Deserialize;
//! use validator::Validate;
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let router = Router::new().route("/extra_form", post(handler));
//! axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
//! .serve(router.into_make_service())
//! .await?;
//! Ok(())
//! }
//! async fn handler(Valid(Form(parameter)): Valid<Form<Parameter>>) {
//! assert!(parameter.validate().is_ok());
//! }
//! #[derive(Validate, Deserialize)]
//! pub struct Parameter {
//! #[validate(range(min = 5, max = 10))]
//! pub v0: i32,
//! #[validate(length(min = 1, max = 10))]
//! pub v1: String,
//! }
//! ```
use crate::HasValidate;
use axum_extra::extract::Form;

View File

@@ -1,5 +1,44 @@
//! # Implementation of the `HasValidate` trait for the `Form` extractor.
//! # Support for `Protobuf<T>` from `axum-extra`
//!
//! ## Feature
//!
//! Enable the `extra_protobuf` feature to use `Valid<Protobuf<T>>`.
//!
//! ## Usage
//!
//! 1. Implement `prost::Message` and `Validate` for your data type `T`.
//! 2. In your handler function, use `Valid<Protobuf<T>>` as some parameter's type.
//!
//! ## Example
//!
//! ```no_run
//! use axum::routing::post;
//! use axum::Router;
//! use axum_extra::protobuf::Protobuf;
//! use axum_valid::Valid;
//! use validator::Validate;
//!
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let router = Router::new().route("/protobuf", post(handler));
//! axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
//! .serve(router.into_make_service())
//! .await?;
//! Ok(())
//! }
//! async fn handler(Valid(Protobuf(parameter)): Valid<Protobuf<Parameter>>) {
//! assert!(parameter.validate().is_ok());
//! }
//! #[derive(Validate, prost::Message)]
//! pub struct Parameter {
//! #[validate(range(min = 5, max = 10))]
//! #[prost(int32, tag = "1")]
//! pub v0: i32,
//! #[validate(length(min = 1, max = 10))]
//! #[prost(string, tag = "2")]
//! pub v1: String,
//! }
//! ```
use crate::HasValidate;
use axum_extra::protobuf::Protobuf;

View File

@@ -1,5 +1,45 @@
//! # Implementation of the `HasValidate` trait for the `Query` extractor in `axum-extra`.
//! # Support for `Query<T>` from `axum-extra`
//!
//! ## Feature
//!
//! Enable the `extra_query` feature to use `Valid<Query<T>>`.
//!
//! ## Usage
//!
//! 1. Implement `Deserialize` and `Validate` for your data type `T`.
//! 2. In your handler function, use `Valid<Query<T>>` as some parameter's type.
//!
//! ## Example
//!
//! ```no_run
//! use axum::routing::post;
//! use axum::Router;
//! use axum_extra::extract::Query;
//! use axum_valid::Valid;
//! use serde::Deserialize;
//! use validator::Validate;
//!
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let router = Router::new().route("/extra_query", post(handler));
//! axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
//! .serve(router.into_make_service())
//! .await?;
//! Ok(())
//! }
//!
//! async fn handler(Valid(Query(parameter)): Valid<Query<Parameter>>) {
//! assert!(parameter.validate().is_ok());
//! }
//!
//! #[derive(Validate, Deserialize)]
//! pub struct Parameter {
//! #[validate(range(min = 5, max = 10))]
//! pub v0: i32,
//! #[validate(length(min = 1, max = 10))]
//! pub v1: String,
//! }
//! ```
use crate::HasValidate;
use axum_extra::extract::Query;

64
src/extra/typed_path.rs Normal file
View File

@@ -0,0 +1,64 @@
//! # Support for `T: TypedPath` from `axum-extra`
//!
//! ## Feature
//!
//! Enable the `extra_typed_path` feature to use `Valid<T: TypedPath>`.
//!
//! ## Usage
//!
//! 1. Implement `TypedPath`, `Deserialize`, `Validate` and `HasValidate` for your data type `T`.
//! 2. In your handler function, use `Valid<T>` as some parameter's type.
//!
//! ## Example
//!
//! ```no_run
//! use axum::Router;
//! use axum_extra::routing::{RouterExt, TypedPath};
//! use axum_valid::{HasValidate, Valid};
//! use serde::Deserialize;
//! use validator::Validate;
//!
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let router = Router::new().typed_get(handler);
//! axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
//! .serve(router.into_make_service())
//! .await?;
//! Ok(())
//! }
//!
//! async fn handler(parameter: Valid<Parameter>) {
//! assert!(parameter.validate().is_ok());
//! }
//!
//! #[derive(TypedPath, Deserialize, Validate)]
//! #[typed_path("/extra_typed_path/:v0/:v1")]
//! struct Parameter {
//! #[validate(range(min = 5, max = 10))]
//! v0: i32,
//! #[validate(length(min = 1, max = 10))]
//! v1: String,
//! }
//!
//! impl HasValidate for Parameter {
//! type Validate = Self;
//!
//! fn get_validate(&self) -> &Self::Validate {
//! self
//! }
//! }
//! ```
use crate::Valid;
use axum_extra::routing::TypedPath;
use std::fmt::{Display, Formatter};
impl<T: Display> Display for Valid<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl<T: TypedPath + Display> TypedPath for Valid<T> {
const PATH: &'static str = T::PATH;
}

View File

@@ -1,5 +1,42 @@
//! # Implementation of the `HasValidate` trait for the `Form` extractor.
//! # Support for `Form<T>`
//!
//! ## Feature
//!
//! Enable the `form` feature (enabled by default) to use `Valid<Form<T>>`.
//!
//! ## Usage
//!
//! 1. Implement `Deserialize` and `Validate` for your data type `T`.
//! 2. In your handler function, use `Valid<Form<T>>` as some parameter's type.
//!
//! ## Example
//!
//! ```no_run
//! use axum::routing::post;
//! use axum::Form;
//! use axum::Router;
//! use axum_valid::Valid;
//! use serde::Deserialize;
//! use validator::Validate;
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let router = Router::new().route("/form", post(handler));
//! axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
//! .serve(router.into_make_service())
//! .await?;
//! Ok(())
//! }
//! async fn handler(Valid(Form(parameter)): Valid<Form<Parameter>>) {
//! assert!(parameter.validate().is_ok());
//! }
//! #[derive(Validate, Deserialize)]
//! pub struct Parameter {
//! #[validate(range(min = 5, max = 10))]
//! pub v0: i32,
//! #[validate(length(min = 1, max = 10))]
//! pub v1: String,
//! }
//! ```
use crate::HasValidate;
use axum::Form;

View File

@@ -1,5 +1,42 @@
//! # Implementation of the `HasValidate` trait for the `Json` extractor.
//! # Support for `Json<T>`
//!
//! ## Feature
//!
//! Enable the `json` feature (enabled by default) to use `Valid<Json<T>>`.
//!
//! ## Usage
//!
//! 1. Implement `Deserialize` and `Validate` for your data type `T`.
//! 2. In your handler function, use `Valid<Json<T>>` as some parameter's type.
//!
//! ## Example
//!
//! ```no_run
//! use axum::routing::post;
//! use axum::Json;
//! use axum::Router;
//! use axum_valid::Valid;
//! use serde::Deserialize;
//! use validator::Validate;
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let router = Router::new().route("/json", post(handler));
//! axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
//! .serve(router.into_make_service())
//! .await?;
//! Ok(())
//! }
//! async fn handler(Valid(Json(parameter)): Valid<Json<Parameter>>) {
//! assert!(parameter.validate().is_ok());
//! }
//! #[derive(Validate, Deserialize)]
//! pub struct Parameter {
//! #[validate(range(min = 5, max = 10))]
//! pub v0: i32,
//! #[validate(length(min = 1, max = 10))]
//! pub v1: String,
//! }
//! ```
use crate::HasValidate;
use axum::Json;

View File

@@ -116,6 +116,9 @@ impl<E: IntoResponse> IntoResponse for ValidRejection<E> {
}
/// Trait for types that can provide a reference that can be validated for correctness.
///
/// Extractor types `T` that implement this trait can be used with `Valid`.
///
pub trait HasValidate {
/// Inner type that can be validated for correctness
type Validate: Validate;

View File

@@ -1,5 +1,51 @@
//! # Implementation of the `HasValidate` trait for the `MsgPack` extractor.
//! # Support for `MsgPack<T>` and `MsgPackRaw<T>` from `axum-msgpack`
//!
//! ## Feature
//!
//! Enable the `msgpack` feature to use `Valid<MsgPack<T>>` and `Valid<MsgPackRaw<T>>`.
//!
//! ## Usage
//!
//! 1. Implement `Deserialize` and `Validate` for your data type `T`.
//! 2. In your handler function, use `Valid<MsgPack<T>>` or `Valid<MsgPackRaw<T>>` as some parameter's type.
//!
//! ## Example
//!
//! ```no_run
//! use axum::routing::post;
//! use axum::Router;
//! use axum_msgpack::{MsgPack, MsgPackRaw};
//! use axum_valid::Valid;
//! use serde::Deserialize;
//! use validator::Validate;
//!
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let router = Router::new()
//! .route("/msgpack", post(handler))
//! .route("/msgpackraw", post(raw_handler));
//! axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
//! .serve(router.into_make_service())
//! .await?;
//! Ok(())
//! }
//!
//! async fn handler(Valid(MsgPack(parameter)): Valid<MsgPack<Parameter>>) {
//! assert!(parameter.validate().is_ok());
//! }
//!
//! async fn raw_handler(Valid(MsgPackRaw(parameter)): Valid<MsgPackRaw<Parameter>>) {
//! assert!(parameter.validate().is_ok());
//! }
//!
//! #[derive(Validate, Deserialize)]
//! pub struct Parameter {
//! #[validate(range(min = 5, max = 10))]
//! pub v0: i32,
//! #[validate(length(min = 1, max = 10))]
//! pub v1: String,
//! }
//! ```
use crate::HasValidate;
use axum_msgpack::{MsgPack, MsgPackRaw};

View File

@@ -1,5 +1,41 @@
//! # Implementation of the `HasValidate` trait for the `Path` extractor.
//! # Support for `Path<T>`
//!
//! ## Usage
//!
//! 1. Implement `Deserialize` and `Validate` for your data type `T`.
//! 2. In your handler function, use `Valid<Path<T>>` as some parameter's type.
//!
//! ## Example
//!
//! ```no_run
//! use axum::extract::Path;
//! use axum::routing::post;
//! use axum::Router;
//! use axum_valid::Valid;
//! use serde::Deserialize;
//! use validator::Validate;
//!
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let router = Router::new().route("/path/:v0/:v1", post(handler));
//! axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
//! .serve(router.into_make_service())
//! .await?;
//! Ok(())
//! }
//!
//! async fn handler(Valid(Path(parameter)): Valid<Path<Parameter>>) {
//! assert!(parameter.validate().is_ok());
//! }
//!
//! #[derive(Validate, Deserialize)]
//! pub struct Parameter {
//! #[validate(range(min = 5, max = 10))]
//! pub v0: i32,
//! #[validate(length(min = 1, max = 10))]
//! pub v1: String,
//! }
//! ```
use crate::HasValidate;
use axum::extract::Path;

View File

@@ -1,5 +1,45 @@
//! # Implementation of the `HasValidate` trait for the `Query` extractor.
//! # Support for `Query<T>`
//!
//! ## Feature
//!
//! Enable the `query` feature (enabled by default) to use `Valid<Query<T>>`.
//!
//! ## Usage
//!
//! 1. Implement `Deserialize` and `Validate` for your data type `T`.
//! 2. In your handler function, use `Valid<Query<T>>` as some parameter's type.
//!
//! ## Example
//!
//! ```no_run
//! use axum::extract::Query;
//! use axum::routing::post;
//! use axum::Router;
//! use axum_valid::Valid;
//! use serde::Deserialize;
//! use validator::Validate;
//!
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let router = Router::new().route("/query", post(handler));
//! axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
//! .serve(router.into_make_service())
//! .await?;
//! Ok(())
//! }
//!
//! async fn handler(Valid(Query(parameter)): Valid<Query<Parameter>>) {
//! assert!(parameter.validate().is_ok());
//! }
//!
//! #[derive(Validate, Deserialize)]
//! pub struct Parameter {
//! #[validate(range(min = 5, max = 10))]
//! pub v0: i32,
//! #[validate(length(min = 1, max = 10))]
//! pub v1: String,
//! }
//! ```
use crate::HasValidate;
use axum::extract::Query;

View File

@@ -96,6 +96,12 @@ async fn test_main() -> anyhow::Result<()> {
post(extra::extract_with_rejection_valid),
);
#[cfg(feature = "extra_typed_path")]
let router = router.route(
extra_typed_path::route::EXTRA_TYPED_PATH,
get(extra_typed_path::extract_extra_typed_path),
);
#[cfg(feature = "extra_query")]
let router = router.route(
extra_query::route::EXTRA_QUERY,
@@ -246,6 +252,61 @@ async fn test_main() -> anyhow::Result<()> {
.await?;
}
#[cfg(feature = "extra_typed_path")]
{
{
let extra_typed_path_type_name = "T: TypedPath";
let valid_extra_typed_path_response = test_executor
.client()
.get(format!(
"{}/extra_typed_path/{}/{}",
server_url, VALID_PARAMETERS.v0, VALID_PARAMETERS.v1
))
.send()
.await?;
assert_eq!(
valid_extra_typed_path_response.status(),
StatusCode::OK,
"Valid '{}' test failed.",
extra_typed_path_type_name
);
let error_extra_typed_path_response = test_executor
.client()
.get(format!("{}/extra_typed_path/not_i32/path", server_url))
.send()
.await?;
assert_eq!(
error_extra_typed_path_response.status(),
StatusCode::BAD_REQUEST,
"Error '{}' test failed.",
extra_typed_path_type_name
);
let invalid_extra_typed_path_response = test_executor
.client()
.get(format!(
"{}/extra_typed_path/{}/{}",
server_url, INVALID_PARAMETERS.v0, INVALID_PARAMETERS.v1
))
.send()
.await?;
assert_eq!(
invalid_extra_typed_path_response.status(),
VALIDATION_ERROR_STATUS,
"Invalid '{}' test failed.",
extra_typed_path_type_name
);
#[cfg(feature = "into_json")]
check_json(
extra_typed_path_type_name,
invalid_extra_typed_path_response,
)
.await;
println!("All {} tests passed.", extra_typed_path_type_name);
}
}
#[cfg(feature = "extra_query")]
{
use axum_extra::extract::Query;
@@ -411,7 +472,7 @@ fn validate_again<V: Validate>(validate: V) -> StatusCode {
mod typed_header {
pub(crate) mod route {
pub const TYPED_HEADER: &str = "/typedHeader";
pub const TYPED_HEADER: &str = "/typed_header";
}
use super::{validate_again, Parameters};
@@ -665,6 +726,41 @@ mod extra {
}
}
#[cfg(feature = "extra_typed_path")]
mod extra_typed_path {
use crate::test::validate_again;
use crate::{HasValidate, Valid};
use axum::http::StatusCode;
use axum_extra::routing::TypedPath;
use serde::Deserialize;
use validator::Validate;
pub mod route {
pub const EXTRA_TYPED_PATH: &str = "/extra_typed_path/:v0/:v1";
}
#[derive(Validate, TypedPath, Deserialize)]
#[typed_path("/extra_typed_path/:v0/:v1")]
pub struct TypedPathParam {
#[validate(range(min = 5, max = 10))]
v0: i32,
#[validate(length(min = 1, max = 10))]
v1: String,
}
impl HasValidate for TypedPathParam {
type Validate = Self;
fn get_validate(&self) -> &Self::Validate {
self
}
}
pub async fn extract_extra_typed_path(Valid(param): Valid<TypedPathParam>) -> StatusCode {
validate_again(param)
}
}
#[cfg(feature = "extra_query")]
mod extra_query {
use crate::test::{validate_again, Parameters};

View File

@@ -1,5 +1,65 @@
//! # Implementation of the `HasValidate` trait for the `TypedHeader` extractor.
//! # Support for `TypedHeader<T>`
//!
//! ## Feature
//!
//! Enable the `typed_header` feature to use `Valid<TypedHeader<T>>`.
//!
//! ## Usage
//!
//! 1. Implement `Header` and `Validate` for your data type `T`.
//! 2. In your handler function, use `Valid<TypedHeader<T>>` as some parameter's type.
//!
//! ## Example
//!
//! ```no_run
//! use axum::headers::{Error, Header, HeaderValue};
//! use axum::http::HeaderName;
//! use axum::routing::post;
//! use axum::{Router, TypedHeader};
//! use axum_valid::Valid;
//! use validator::Validate;
//!
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let router = Router::new().route("/typed_header", post(handler));
//! axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
//! .serve(router.into_make_service())
//! .await?;
//! Ok(())
//! }
//!
//! async fn handler(Valid(TypedHeader(parameter)): Valid<TypedHeader<Parameter>>) {
//! assert!(parameter.validate().is_ok());
//! }
//!
//! #[derive(Validate)]
//! pub struct Parameter {
//! #[validate(range(min = 5, max = 10))]
//! pub v0: i32,
//! #[validate(length(min = 1, max = 10))]
//! pub v1: String,
//! }
//!
//! static HEADER_NAME: HeaderName = HeaderName::from_static("my-header");
//!
//! impl Header for Parameter {
//! fn name() -> &'static HeaderName {
//! &HEADER_NAME
//! }
//!
//! fn decode<'i, I>(_values: &mut I) -> Result<Self, Error>
//! where
//! Self: Sized,
//! I: Iterator<Item = &'i HeaderValue>,
//! {
//! todo!()
//! }
//!
//! fn encode<E: Extend<HeaderValue>>(&self, _values: &mut E) {
//! todo!()
//! }
//! }
//! ```
use crate::HasValidate;
use axum::TypedHeader;

View File

@@ -1,5 +1,54 @@
//! # Implementation of the `HasValidate` trait for the `TypedMultipart` extractor.
//! # Support for `TypedMultipart<T>` and `BaseMultipart<T, R>` from `axum_typed_multipart`
//!
//! ## Feature
//!
//! Enable the `typed_multipart` feature to use `Valid<TypedMultipart<T>>` and `Valid<BaseMultipart<T, R>>`.
//!
//! ## Usage
//!
//! 1. Implement `TryFromMultipart` and `Validate` for your data type `T`.
//! 2. In your handler function, use `Valid<TypedMultipart<T>>` or `Valid<BaseMultipart<T, E>` as some parameter's type.
//!
//! ## Example
//!
//! ```no_run
//! use axum::routing::post;
//! use axum::Router;
//! use axum_typed_multipart::{BaseMultipart, TryFromMultipart, TypedMultipart, TypedMultipartError};
//! use axum_valid::Valid;
//! use validator::Validate;
//!
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let router = Router::new()
//! .route("/typed_multipart", post(handler))
//! .route("/base_multipart", post(base_handler));
//! axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
//! .serve(router.into_make_service())
//! .await?;
//! Ok(())
//! }
//!
//! async fn handler(Valid(TypedMultipart(parameter)): Valid<TypedMultipart<Parameter>>) {
//! assert!(parameter.validate().is_ok());
//! }
//!
//! async fn base_handler(
//! Valid(BaseMultipart {
//! data: parameter, ..
//! }): Valid<BaseMultipart<Parameter, TypedMultipartError>>,
//! ) {
//! assert!(parameter.validate().is_ok());
//! }
//!
//! #[derive(TryFromMultipart, Validate)]
//! struct Parameter {
//! #[validate(range(min = 5, max = 10))]
//! v0: i32,
//! #[validate(length(min = 1, max = 10))]
//! v1: String,
//! }
//! ```
use crate::HasValidate;
use axum_typed_multipart::{BaseMultipart, TypedMultipart};

View File

@@ -1,5 +1,46 @@
//! # Implementation of the `HasValidate` trait for the `Yaml` extractor.
//! # Support for `Yaml<T>` from `axum-yaml`
//!
//! ## Feature
//!
//! Enable the `yaml` feature to use `Valid<Yaml<T>>`.
//!
//! ## Usage
//!
//! 1. Implement `Deserialize` and `Validate` for your data type `T`.
//! 2. In your handler function, use `Valid<Yaml<T>>` as some parameter's type.
//!
//! ## Example
//!
//! ```no_run
//! use axum::routing::post;
//! use axum::Router;
//! use axum_valid::Valid;
//! use axum_yaml::Yaml;
//! use serde::Deserialize;
//! use validator::Validate;
//!
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let router = Router::new().route("/yaml", post(handler));
//! axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
//! .serve(router.into_make_service())
//! .await?;
//! Ok(())
//! }
//!
//! async fn handler(parameter: Valid<Yaml<Parameter>>) {
//! assert!(parameter.validate().is_ok());
//! }
//!
//! #[derive(Deserialize, Validate)]
//! struct Parameter {
//! #[validate(range(min = 5, max = 10))]
//! v0: i32,
//! #[validate(length(min = 1, max = 10))]
//! v1: String,
//! }
//! ```
use crate::HasValidate;
use axum_yaml::Yaml;