axum_valid/
typed_header.rs

1//! # Support for `TypedHeader<T>`
2//!
3//! ## Feature
4//!
5//! Enable the `typed_header` feature to use `Valid<TypedHeader<T>>`.
6//!
7//! ## Usage
8//!
9//! 1. Implement `Header` and `Validate` for your data type `T`.
10//! 2. In your handler function, use `Valid<TypedHeader<T>>` as some parameter's type.
11//!
12//! ## Example
13//!
14//! ```no_run
15//! #[cfg(feature = "validator")]
16//! mod validator_example {
17//!     use axum_extra::headers::{Error, Header, HeaderValue};
18//!     use axum_extra::typed_header::TypedHeader;
19//!     use axum::http::HeaderName;
20//!     use axum::routing::post;
21//!     use axum::Router;
22//!     use axum_valid::Valid;
23//!     use validator::Validate;
24//!
25//!     pub fn router() -> Router {
26//!         Router::new().route("/typed_header", post(handler))
27//!     }
28//!
29//!     async fn handler(Valid(TypedHeader(parameter)): Valid<TypedHeader<Parameter>>) {
30//!         assert!(parameter.validate().is_ok());
31//!     }
32//!
33//!     #[derive(Validate)]
34//!     pub struct Parameter {
35//!         #[validate(range(min = 5, max = 10))]
36//!         pub v0: i32,
37//!         #[validate(length(min = 1, max = 10))]
38//!         pub v1: String,
39//!     }
40//!
41//!     static HEADER_NAME: HeaderName = HeaderName::from_static("my-header");
42//!
43//!     impl Header for Parameter {
44//!         fn name() -> &'static HeaderName {
45//!             &HEADER_NAME
46//!         }
47//!
48//!         fn decode<'i, I>(_values: &mut I) -> Result<Self, Error>
49//!         where
50//!             Self: Sized,
51//!             I: Iterator<Item = &'i HeaderValue>,
52//!         {
53//!             todo!()
54//!         }
55//!
56//!         fn encode<E: Extend<HeaderValue>>(&self, _values: &mut E) {
57//!             todo!()
58//!         }
59//!     }
60//! }
61//!
62//! #[cfg(feature = "garde")]
63//! mod garde_example {
64//!     use axum_extra::headers::{Error, Header, HeaderValue};
65//!     use axum_extra::typed_header::TypedHeader;
66//!     use axum::http::HeaderName;
67//!     use axum::routing::post;
68//!     use axum::Router;
69//!     use axum_valid::Garde;
70//!     use garde::Validate;
71//!
72//!     pub fn router() -> Router {
73//!         Router::new().route("/typed_header", post(handler))
74//!     }
75//!
76//!     async fn handler(Garde(TypedHeader(parameter)): Garde<TypedHeader<Parameter>>) {
77//!         assert!(parameter.validate_with(&()).is_ok());
78//!     }
79//!
80//!     #[derive(Validate)]
81//!     pub struct Parameter {
82//!         #[garde(range(min = 5, max = 10))]
83//!         pub v0: i32,
84//!         #[garde(length(min = 1, max = 10))]
85//!         pub v1: String,
86//!     }
87//!
88//!     static HEADER_NAME: HeaderName = HeaderName::from_static("my-header");
89//!
90//!     impl Header for Parameter {
91//!         fn name() -> &'static HeaderName {
92//!             &HEADER_NAME
93//!         }
94//!
95//!         fn decode<'i, I>(_values: &mut I) -> Result<Self, Error>
96//!         where
97//!             Self: Sized,
98//!             I: Iterator<Item = &'i HeaderValue>,
99//!         {
100//!             todo!()
101//!         }
102//!
103//!         fn encode<E: Extend<HeaderValue>>(&self, _values: &mut E) {
104//!             todo!()
105//!         }
106//!     }
107//! }
108//!
109//! # #[tokio::main]
110//! # async fn main() -> anyhow::Result<()> {
111//! #     use std::net::SocketAddr;
112//! #     use axum::Router;
113//! #     use tokio::net::TcpListener;
114//! #     let router = Router::new();
115//! #     #[cfg(feature = "validator")]
116//! #     let router = router.nest("/validator", validator_example::router());
117//! #     #[cfg(feature = "garde")]
118//! #     let router = router.nest("/garde", garde_example::router());
119//! #     let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?;
120//! #     axum::serve(listener, router.into_make_service())
121//! #         .await?;
122//! #     Ok(())
123//! # }
124//! ```
125
126use crate::HasValidate;
127#[cfg(feature = "validator")]
128use crate::HasValidateArgs;
129use axum_extra::typed_header::TypedHeader;
130#[cfg(feature = "validator")]
131use validator::ValidateArgs;
132
133impl<T> HasValidate for TypedHeader<T> {
134    type Validate = T;
135    fn get_validate(&self) -> &T {
136        &self.0
137    }
138}
139
140#[cfg(feature = "validator")]
141impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for TypedHeader<T> {
142    type ValidateArgs = T;
143    fn get_validate_args(&self) -> &Self::ValidateArgs {
144        &self.0
145    }
146}
147
148#[cfg(feature = "validify")]
149impl<T: validify::Modify> crate::HasModify for TypedHeader<T> {
150    type Modify = T;
151
152    fn get_modify(&mut self) -> &mut Self::Modify {
153        &mut self.0
154    }
155}
156
157#[cfg(test)]
158mod tests {
159    use crate::tests::{ValidTest, ValidTestParameter};
160    use axum::http::StatusCode;
161    use axum_extra::headers::Header;
162    use axum_extra::typed_header::TypedHeader;
163    use reqwest::header::{HeaderMap, HeaderValue};
164    use reqwest::RequestBuilder;
165
166    impl<T: ValidTestParameter + Header + Clone> ValidTest for TypedHeader<T> {
167        const ERROR_STATUS_CODE: StatusCode = StatusCode::BAD_REQUEST;
168
169        fn set_valid_request(builder: RequestBuilder) -> RequestBuilder {
170            let mut vec = Vec::new();
171            T::valid().encode(&mut vec);
172            let hv = vec.pop().expect("get header value");
173            let mut headers = HeaderMap::default();
174            headers.insert(
175                T::name().as_str(),
176                HeaderValue::from_bytes(hv.as_bytes()).expect("build header value"),
177            );
178            builder.headers(headers)
179        }
180
181        fn set_error_request(builder: RequestBuilder) -> RequestBuilder {
182            builder
183        }
184
185        fn set_invalid_request(builder: RequestBuilder) -> RequestBuilder {
186            let mut vec = Vec::new();
187            T::invalid().encode(&mut vec);
188            let hv = vec.pop().expect("get header value");
189            let mut headers = HeaderMap::default();
190            headers.insert(
191                T::name().as_str(),
192                HeaderValue::from_bytes(hv.as_bytes()).expect("build header value"),
193            );
194            builder.headers(headers)
195        }
196    }
197}