mirror of
https://github.com/tokio-rs/axum.git
synced 2025-03-26 00:27:01 +01:00
Add WithRejection
(#1262)
* new(axum-extra): Added `WithRejection` base impl Based on @jplatte's version (https://github.com/tokio-rs/axum/issues/1116#issuecomment-1215048273), with slight changes - Using `From<E::Rejection>` to define the trait bound on a more concise way - Renamed variables to something more meaningfull * revert(axum-extra): Removed `with_rejection` feat * ref(axum-extra): Replaced `match` with `?` * tests(axum-extra): Added test for `WithRejection` * examples: Replaced custom `Json` extractor with `WithRejection` * docs(axum-extra): Added doc to `WithRejection` * fmt(cargo-check): removed whitespaces * fmt(customize-extractor-error): missing fmt * docs(axum-extra): doctest includes `Handler` test Co-authored-by: David Pedersen <david.pdrsn@gmail.com> * docs(axum-extra):` _ `-> `rejection` Co-authored-by: David Pedersen <david.pdrsn@gmail.com> * docs(axum-extra): fixed suggestions * fix(axum-extra): `WithRejection` manual trait impl * revert(customize-extractor-error): Undo example changes refs:d878eede18
,f9200bf4b9
* example(customize-extractor-error): Added reference to `WithRejection` * docs(axum-extra): Removed `customize-extractor-error` reference * fmt(axum-extra): cargo fmt * docs(axum-extra): Added `WithRejection` to CHANGELOG.md Co-authored-by: David Pedersen <david.pdrsn@gmail.com>
This commit is contained in:
parent
01630cfef6
commit
fb21561616
4 changed files with 174 additions and 0 deletions
axum-extra
examples/customize-extractor-error/src
|
@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning].
|
|||
- **added:** Support chaining handlers with `HandlerCallWithExtractors::or` ([#1170])
|
||||
- **change:** axum-extra's MSRV is now 1.60 ([#1239])
|
||||
- **added:** Add Protocol Buffer extractor and response ([#1239])
|
||||
- **added:** `WithRejection` extractor for customizing other extractors' rejections ([#1262])
|
||||
- **added:** Add sync constructors to `CookieJar`, `PrivateCookieJar`, and
|
||||
`SignedCookieJar` so they're easier to use in custom middleware
|
||||
|
||||
|
@ -26,6 +27,7 @@ and this project adheres to [Semantic Versioning].
|
|||
[#1170]: https://github.com/tokio-rs/axum/pull/1170
|
||||
[#1214]: https://github.com/tokio-rs/axum/pull/1214
|
||||
[#1239]: https://github.com/tokio-rs/axum/pull/1239
|
||||
[#1262]: https://github.com/tokio-rs/axum/pull/1262
|
||||
|
||||
# 0.3.5 (27. June, 2022)
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ pub mod cookie;
|
|||
#[cfg(feature = "query")]
|
||||
mod query;
|
||||
|
||||
mod with_rejection;
|
||||
|
||||
pub use self::cached::Cached;
|
||||
|
||||
#[cfg(feature = "cookie")]
|
||||
|
@ -31,3 +33,5 @@ pub use self::query::Query;
|
|||
#[cfg(feature = "json-lines")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::json_lines::JsonLines;
|
||||
|
||||
pub use self::with_rejection::WithRejection;
|
||||
|
|
165
axum-extra/src/extract/with_rejection.rs
Normal file
165
axum-extra/src/extract/with_rejection.rs
Normal file
|
@ -0,0 +1,165 @@
|
|||
use axum::async_trait;
|
||||
use axum::extract::{FromRequest, RequestParts};
|
||||
use axum::response::IntoResponse;
|
||||
use std::fmt::Debug;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
/// Extractor for customizing extractor rejections
|
||||
///
|
||||
/// `WithRejection` wraps another extractor and gives you the result. If the
|
||||
/// extraction fails, the `Rejection` is transformed into `R` and returned as a
|
||||
/// response
|
||||
///
|
||||
/// `E` is expected to implement [`FromRequest`]
|
||||
///
|
||||
/// `R` is expected to implement [`IntoResponse`] and [`From<E::Rejection>`]
|
||||
///
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use axum::extract::rejection::JsonRejection;
|
||||
/// use axum::response::{Response, IntoResponse};
|
||||
/// use axum::Json;
|
||||
/// use axum_extra::extract::WithRejection;
|
||||
/// use serde::Deserialize;
|
||||
///
|
||||
/// struct MyRejection { /* ... */ }
|
||||
///
|
||||
/// impl From<JsonRejection> for MyRejection {
|
||||
/// fn from(rejection: JsonRejection) -> MyRejection {
|
||||
/// // ...
|
||||
/// # todo!()
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl IntoResponse for MyRejection {
|
||||
/// fn into_response(self) -> Response {
|
||||
/// // ...
|
||||
/// # todo!()
|
||||
/// }
|
||||
/// }
|
||||
/// #[derive(Debug, Deserialize)]
|
||||
/// struct Person { /* ... */ }
|
||||
///
|
||||
/// async fn handler(
|
||||
/// // If the `Json` extractor ever fails, `MyRejection` will be sent to the
|
||||
/// // client using the `IntoResponse` impl
|
||||
/// WithRejection(Json(Person), _): WithRejection<Json<Person>, MyRejection>
|
||||
/// ) { /* ... */ }
|
||||
/// # let _: axum::Router = axum::Router::new().route("/", axum::routing::get(handler));
|
||||
/// ```
|
||||
///
|
||||
/// [`FromRequest`]: axum::extract::FromRequest
|
||||
/// [`IntoResponse`]: axum::response::IntoResponse
|
||||
/// [`From<E::Rejection>`]: std::convert::From
|
||||
pub struct WithRejection<E, R>(pub E, pub PhantomData<R>);
|
||||
|
||||
impl<E, R> WithRejection<E, R> {
|
||||
/// Returns the wrapped extractor
|
||||
fn into_inner(self) -> E {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, R> Debug for WithRejection<E, R>
|
||||
where
|
||||
E: Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("WithRejection")
|
||||
.field(&self.0)
|
||||
.field(&self.1)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, R> Clone for WithRejection<E, R>
|
||||
where
|
||||
E: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone(), self.1.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, R> Copy for WithRejection<E, R> where E: Copy {}
|
||||
|
||||
impl<E: Default, R> Default for WithRejection<E, R> {
|
||||
fn default() -> Self {
|
||||
Self(Default::default(), Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, R> Deref for WithRejection<E, R> {
|
||||
type Target = E;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, R> DerefMut for WithRejection<E, R> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<B, E, R> FromRequest<B> for WithRejection<E, R>
|
||||
where
|
||||
B: Send,
|
||||
E: FromRequest<B>,
|
||||
R: From<E::Rejection> + IntoResponse,
|
||||
{
|
||||
type Rejection = R;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
let extractor = req.extract::<E>().await?;
|
||||
Ok(WithRejection(extractor, PhantomData))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use axum::http::Request;
|
||||
use axum::response::Response;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn extractor_rejection_is_transformed() {
|
||||
struct TestExtractor;
|
||||
struct TestRejection;
|
||||
|
||||
#[async_trait]
|
||||
impl<B: Send> FromRequest<B> for TestExtractor {
|
||||
type Rejection = ();
|
||||
|
||||
async fn from_request(_: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoResponse for TestRejection {
|
||||
fn into_response(self) -> Response {
|
||||
().into_response()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<()> for TestRejection {
|
||||
fn from(_: ()) -> Self {
|
||||
TestRejection
|
||||
}
|
||||
}
|
||||
|
||||
let mut req = RequestParts::new(Request::new(()));
|
||||
|
||||
let result = req
|
||||
.extract::<WithRejection<TestExtractor, TestRejection>>()
|
||||
.await;
|
||||
|
||||
assert!(matches!(result, Err(TestRejection)))
|
||||
}
|
||||
}
|
|
@ -3,6 +3,9 @@
|
|||
//! ```not_rust
|
||||
//! cd examples && cargo run -p example-customize-extractor-error
|
||||
//! ```
|
||||
//!
|
||||
//! See https://docs.rs/axum-extra/0.3.7/axum_extra/extract/struct.WithRejection.html
|
||||
//! example for creating custom errors from already existing extractors
|
||||
|
||||
use axum::{
|
||||
async_trait,
|
||||
|
|
Loading…
Add table
Reference in a new issue