diff --git a/README.md b/README.md index 6f4e833..4041d3b 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,11 @@ [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/gengteng/axum-valid/.github/workflows/main.yml?branch=main)](https://github.com/gengteng/axum-valid/actions/workflows/ci.yml) [![Coverage Status](https://coveralls.io/repos/github/gengteng/axum-valid/badge.svg?branch=main)](https://coveralls.io/github/gengteng/axum-valid?branch=main) -This crate provides a `Valid` type that can be used in combination with `Json`, `Path`, `Query`, and `Form` extractors to validate the entities that implement the `Validate` trait from the `validator` crate. +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. -It also provides a `ValidEx` type that works similarly to `Valid`, but can perform validation requiring additional arguments by using types that implement the `ValidateArgs` trait. +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 like `TypedHeader`, `MsgPack`, `Yaml` etc. are supported through optional features. The full list of supported extractors is in the Features section below. +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 diff --git a/src/test.rs b/src/test.rs index e4b5d5a..f7ba171 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,5 +1,5 @@ use crate::tests::{ValidTest, ValidTestParameter}; -use crate::{Arguments, HasValidate, Valid, ValidEx, VALIDATION_ERROR_STATUS}; +use crate::{Arguments, HasValidate, HasValidateArgs, Valid, ValidEx, VALIDATION_ERROR_STATUS}; use axum::extract::{FromRef, Path, Query}; use axum::routing::{get, post}; use axum::{Form, Json, Router}; @@ -114,6 +114,14 @@ impl HasValidate for Parameters { } } +impl<'v> HasValidateArgs<'v> for ParametersEx { + type ValidateArgs = ParametersEx; + + fn get_validate_args(&self) -> &Self::ValidateArgs { + self + } +} + #[derive(Debug, Clone, FromRef)] struct MyState { param_validation_ctx: ParametersExValidationArguments, @@ -172,13 +180,22 @@ async fn test_main() -> anyhow::Result<()> { #[cfg(feature = "extra")] let router = router .route(extra::route::CACHED, post(extra::extract_cached)) + .route(extra::route::CACHED_EX, post(extra::extract_cached_ex)) .route( extra::route::WITH_REJECTION, post(extra::extract_with_rejection), ) + .route( + extra::route::WITH_REJECTION_EX, + post(extra::extract_with_rejection_ex), + ) .route( extra::route::WITH_REJECTION_VALID, post(extra::extract_with_rejection_valid), + ) + .route( + extra::route::WITH_REJECTION_VALID_EX, + post(extra::extract_with_rejection_valid_ex), ); #[cfg(feature = "extra_typed_path")] @@ -875,9 +892,12 @@ mod typed_multipart { #[cfg(feature = "extra")] mod extra { - use crate::test::{validate_again, Parameters}; + use crate::test::{ + validate_again, validate_again_ex, Parameters, ParametersEx, + ParametersExValidationArguments, + }; use crate::tests::{Rejection, ValidTest, ValidTestParameter}; - use crate::{Valid, ValidRejection}; + use crate::{Arguments, Valid, ValidEx, ValidRejection}; use axum::extract::FromRequestParts; use axum::http::request::Parts; use axum::http::StatusCode; @@ -887,8 +907,11 @@ mod extra { pub mod route { pub const CACHED: &str = "/cached"; + pub const CACHED_EX: &str = "/cached_ex"; pub const WITH_REJECTION: &str = "/with_rejection"; + pub const WITH_REJECTION_EX: &str = "/with_rejection_ex"; pub const WITH_REJECTION_VALID: &str = "/with_rejection_valid"; + pub const WITH_REJECTION_VALID_EX: &str = "/with_rejection_valid_ex"; } pub const PARAMETERS_HEADER: &str = "parameters-header"; pub const CACHED_REJECTION_STATUS: StatusCode = StatusCode::FORBIDDEN; @@ -931,6 +954,22 @@ mod extra { } } + #[axum::async_trait] + impl FromRequestParts for ParametersEx + where + S: Send + Sync, + { + type Rejection = ParametersRejection; + + async fn from_request_parts(parts: &mut Parts, _: &S) -> Result { + let Some(value) = parts.headers.get(PARAMETERS_HEADER) else { + return Err(ParametersRejection::Null); + }; + + serde_json::from_slice(value.as_bytes()).map_err(ParametersRejection::InvalidJson) + } + } + impl ValidTest for Parameters { const ERROR_STATUS_CODE: StatusCode = CACHED_REJECTION_STATUS; @@ -987,6 +1026,15 @@ mod extra { validate_again(parameters) } + pub async fn extract_cached_ex( + ValidEx(Cached(parameters), args): ValidEx< + Cached, + ParametersExValidationArguments, + >, + ) -> StatusCode { + validate_again_ex(parameters, args.get()) + } + pub async fn extract_with_rejection( Valid(WithRejection(parameters, _)): Valid< WithRejection, @@ -995,6 +1043,15 @@ mod extra { validate_again(parameters) } + pub async fn extract_with_rejection_ex( + ValidEx(WithRejection(parameters, _), args): ValidEx< + WithRejection, + ParametersExValidationArguments, + >, + ) -> StatusCode { + validate_again_ex(parameters, args.get()) + } + pub struct WithRejectionValidRejection { inner: ValidRejection, } @@ -1021,6 +1078,15 @@ mod extra { ) -> StatusCode { validate_again(parameters) } + + pub async fn extract_with_rejection_valid_ex( + WithRejection(ValidEx(parameters, args), _): WithRejection< + ValidEx, + WithRejectionValidRejection, + >, + ) -> StatusCode { + validate_again_ex(parameters, args.get()) + } } #[cfg(feature = "extra_typed_path")]