mirror of
https://github.com/tokio-rs/axum.git
synced 2025-03-13 11:18:33 +01:00
Document optional extractors
This commit is contained in:
parent
a8e61360b0
commit
1f2c649804
5 changed files with 67 additions and 29 deletions
|
@ -6,7 +6,8 @@ use crate::response::IntoResponse;
|
|||
|
||||
use super::{private, FromRequest, FromRequestParts, Request};
|
||||
|
||||
/// TODO: DOCS
|
||||
/// Customize the behavior of `Option<Self>` as a [`FromRequestParts`]
|
||||
/// extractor.
|
||||
pub trait OptionalFromRequestParts<S>: Sized {
|
||||
/// If the extractor fails, it will use this "rejection" type.
|
||||
///
|
||||
|
@ -20,7 +21,7 @@ pub trait OptionalFromRequestParts<S>: Sized {
|
|||
) -> impl Future<Output = Result<Option<Self>, Self::Rejection>> + Send;
|
||||
}
|
||||
|
||||
/// TODO: DOCS
|
||||
/// Customize the behavior of `Option<Self>` as a [`FromRequest`] extractor.
|
||||
pub trait OptionalFromRequest<S, M = private::ViaRequest>: Sized {
|
||||
/// If the extractor fails, it will use this "rejection" type.
|
||||
///
|
||||
|
|
|
@ -18,6 +18,19 @@ use std::fmt;
|
|||
/// with the `multiple` attribute. Those values can be collected into a `Vec` or other sequential
|
||||
/// container.
|
||||
///
|
||||
/// # `Option<Query<T>>` behavior
|
||||
///
|
||||
/// If `Query<T>` itself is used as an extractor and there is no query string in
|
||||
/// the request URL, `T`'s `Deserialize` implementation is called on an empty
|
||||
/// string instead.
|
||||
///
|
||||
/// You can avoid this by using `Option<Query<T>>`, which gives you `None` in
|
||||
/// the case that there is no query string in the request URL.
|
||||
///
|
||||
/// Note that an empty query string is not the same as no query string, that is
|
||||
/// `https://example.org/` and `https://example.org/?` are not treated the same
|
||||
/// in this case.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
|
|
|
@ -200,29 +200,11 @@ async fn handler(
|
|||
axum enforces this by requiring the last extractor implements [`FromRequest`]
|
||||
and all others implement [`FromRequestParts`].
|
||||
|
||||
# Optional extractors
|
||||
# Handling extractor rejections
|
||||
|
||||
TODO: Docs, more realistic example
|
||||
|
||||
```rust,no_run
|
||||
use axum::{routing::post, Router};
|
||||
use axum_extra::{headers::UserAgent, TypedHeader};
|
||||
use serde_json::Value;
|
||||
|
||||
async fn foo(user_agent: Option<TypedHeader<UserAgent>>) {
|
||||
if let Some(TypedHeader(user_agent)) = user_agent {
|
||||
// The client sent a user agent
|
||||
} else {
|
||||
// No user agent header
|
||||
}
|
||||
}
|
||||
|
||||
let app = Router::new().route("/foo", post(foo));
|
||||
# let _: Router = app;
|
||||
```
|
||||
|
||||
Wrapping extractors in `Result` makes them optional and gives you the reason
|
||||
the extraction failed:
|
||||
If you want to handle the case of an extractor failing within a specific
|
||||
handler, you can wrap it in `Result`, with the error being the rejection type
|
||||
of the extractor:
|
||||
|
||||
```rust,no_run
|
||||
use axum::{
|
||||
|
@ -261,10 +243,33 @@ let app = Router::new().route("/users", post(create_user));
|
|||
# let _: Router = app;
|
||||
```
|
||||
|
||||
Another option is to make use of the optional extractors in [axum-extra] that
|
||||
either returns `None` if there are no query parameters in the request URI,
|
||||
or returns `Some(T)` if deserialization was successful.
|
||||
If the deserialization was not successful, the request is rejected.
|
||||
# Optional extractors
|
||||
|
||||
Some extractors implement [`OptionalFromRequestParts`] in addition to
|
||||
[`FromRequestParts`], or [`OptionalFromRequest`] in addition to [`FromRequest`].
|
||||
|
||||
These extractors can be used inside of `Option`. It depends on the particular
|
||||
`OptionalFromRequestParts` or `OptionalFromRequest` implementation what this
|
||||
does: For example for `TypedHeader` from axum-extra, you get `None` if the
|
||||
header you're trying to extract is not part of the request, but if the header
|
||||
is present and fails to parse, the request is rejected.
|
||||
|
||||
```rust,no_run
|
||||
use axum::{routing::post, Router};
|
||||
use axum_extra::{headers::UserAgent, TypedHeader};
|
||||
use serde_json::Value;
|
||||
|
||||
async fn foo(user_agent: Option<TypedHeader<UserAgent>>) {
|
||||
if let Some(TypedHeader(user_agent)) = user_agent {
|
||||
// The client sent a user agent
|
||||
} else {
|
||||
// No user agent header
|
||||
}
|
||||
}
|
||||
|
||||
let app = Router::new().route("/foo", post(foo));
|
||||
# let _: Router = app;
|
||||
```
|
||||
|
||||
# Customizing extractor responses
|
||||
|
||||
|
|
|
@ -24,6 +24,12 @@ use std::{fmt, sync::Arc};
|
|||
/// parameters must be valid UTF-8, otherwise `Path` will fail and return a `400
|
||||
/// Bad Request` response.
|
||||
///
|
||||
/// # `Option<Path<T>>` behavior
|
||||
///
|
||||
/// You can use `Option<Path<T>>` as an extractor to allow the same handler to
|
||||
/// be used in a route with parameters that deserialize to `T`, and another
|
||||
/// route with no parameters at all.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// These examples assume the `serde` feature of the [`uuid`] crate is enabled.
|
||||
|
|
|
@ -6,7 +6,20 @@ use serde::de::DeserializeOwned;
|
|||
///
|
||||
/// `T` is expected to implement [`serde::Deserialize`].
|
||||
///
|
||||
/// # Example
|
||||
/// # `Option<Query<T>>` behavior
|
||||
///
|
||||
/// If `Query<T>` itself is used as an extractor and there is no query string in
|
||||
/// the request URL, `T`'s `Deserialize` implementation is called on an empty
|
||||
/// string instead.
|
||||
///
|
||||
/// You can avoid this by using `Option<Query<T>>`, which gives you `None` in
|
||||
/// the case that there is no query string in the request URL.
|
||||
///
|
||||
/// Note that an empty query string is not the same as no query string, that is
|
||||
/// `https://example.org/` and `https://example.org/?` are not treated the same
|
||||
/// in this case.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// use axum::{
|
||||
|
|
Loading…
Add table
Reference in a new issue