//! # Support for extractors from `axum-extra` //! //! ## Feature //! //! Enable the `extra` feature to use `Valid>`, `Valid>` and `WithRejection, R>`. //! //! ## Modules //! //! * [`self`] : `Cache` //! * [`self`] : `WithRejection` //! * [`form`] : `Form` //! * [`protobuf`] : `Protobuf` //! * [`query`] : `Query` //! * [`typed_path`] : `T: TypedPath` //! //! ## `Cached` and `WithRejection` //! //! ### `Valid>` //! //! #### Usage //! //! 0. Implement your own extractor `T`. //! 1. Implement `Clone` and `Validate` for your extractor type `T`. //! 2. In your handler function, use `Valid>` 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>) { //! 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 FromRequestParts for Parameter //! where //! S: Send + Sync, //! { //! type Rejection = ParameterRejection; //! //! async fn from_request_parts(_parts: &mut Parts, _: &S) -> Result { //! todo!() //! } //! } //! ``` //! //! ### `Valid>` //! //! #### 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>` 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, //! >, //! ) { //! 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 FromRequestParts for Parameter //! where //! S: Send + Sync, //! { //! type Rejection = ValidWithRejectionRejection; //! //! async fn from_request_parts(_parts: &mut Parts, _: &S) -> Result { //! todo!() //! } //! } //! ``` //! //! ### `WithRejection, 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>` for `R`. //! 3. In your handler function, use `WithRejection, 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, //! 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 FromRequestParts for Parameter //! where //! S: Send + Sync, //! { //! type Rejection = ParameterRejection; //! //! async fn from_request_parts(_parts: &mut Parts, _: &S) -> Result { //! todo!() //! } //! } //! //! pub struct WithRejectionValidRejection; //! //! impl From> for WithRejectionValidRejection { //! fn from(_inner: ValidRejection) -> Self { //! todo!() //! } //! } //! //! impl IntoResponse for WithRejectionValidRejection { //! fn into_response(self) -> Response { //! todo!() //! } //! } //! ``` #[cfg(feature = "extra_form")] pub mod form; #[cfg(feature = "extra_protobuf")] pub mod protobuf; #[cfg(feature = "extra_query")] pub mod query; #[cfg(feature = "extra_typed_path")] pub mod typed_path; use crate::{HasValidate, HasValidateArgs}; use axum_extra::extract::{Cached, WithRejection}; use validator::{Validate, ValidateArgs}; impl HasValidate for Cached { type Validate = T; fn get_validate(&self) -> &Self::Validate { &self.0 } } impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for Cached { type ValidateArgs = T; fn get_validate_args(&self) -> &Self::ValidateArgs { &self.0 } } impl HasValidate for WithRejection { type Validate = T; fn get_validate(&self) -> &T { &self.0 } } impl<'v, T: ValidateArgs<'v>, R> HasValidateArgs<'v> for WithRejection { type ValidateArgs = T; fn get_validate_args(&self) -> &Self::ValidateArgs { &self.0 } } #[cfg(test)] mod tests { use crate::tests::{Rejection, ValidTest}; use crate::Valid; use axum::http::StatusCode; use axum_extra::extract::{Cached, WithRejection}; use reqwest::RequestBuilder; impl ValidTest for Cached { const ERROR_STATUS_CODE: StatusCode = T::ERROR_STATUS_CODE; fn set_valid_request(builder: RequestBuilder) -> RequestBuilder { T::set_valid_request(builder) } fn set_error_request(builder: RequestBuilder) -> RequestBuilder { // cached never fails T::set_error_request(builder) } fn set_invalid_request(builder: RequestBuilder) -> RequestBuilder { T::set_invalid_request(builder) } } impl ValidTest for WithRejection { const ERROR_STATUS_CODE: StatusCode = R::STATUS_CODE; fn set_valid_request(builder: RequestBuilder) -> RequestBuilder { T::set_valid_request(builder) } fn set_error_request(builder: RequestBuilder) -> RequestBuilder { // cached never fails T::set_error_request(builder) } fn set_invalid_request(builder: RequestBuilder) -> RequestBuilder { T::set_invalid_request(builder) } } impl ValidTest for WithRejection, R> { // just use `418 I'm a teapot` to test const ERROR_STATUS_CODE: StatusCode = StatusCode::IM_A_TEAPOT; // If `WithRejection` is the outermost extractor, // the error code returned will always be the one provided by WithRejection. const INVALID_STATUS_CODE: StatusCode = StatusCode::IM_A_TEAPOT; // If `WithRejection` is the outermost extractor, // the returned body may not be in JSON format. const JSON_SERIALIZABLE: bool = false; fn set_valid_request(builder: RequestBuilder) -> RequestBuilder { T::set_valid_request(builder) } fn set_error_request(builder: RequestBuilder) -> RequestBuilder { // invalid requests will cause the Valid extractor to fail. T::set_invalid_request(builder) } fn set_invalid_request(builder: RequestBuilder) -> RequestBuilder { T::set_invalid_request(builder) } } }