axum/extract/
original_uri.rs

1use super::{Extension, FromRequestParts};
2use http::{request::Parts, Uri};
3use std::convert::Infallible;
4
5/// Extractor that gets the original request URI regardless of nesting.
6///
7/// This is necessary since [`Uri`](http::Uri), when used as an extractor, will
8/// have the prefix stripped if used in a nested service.
9///
10/// # Example
11///
12/// ```
13/// use axum::{
14///     routing::get,
15///     Router,
16///     extract::OriginalUri,
17///     http::Uri
18/// };
19///
20/// let api_routes = Router::new()
21///     .route(
22///         "/users",
23///         get(|uri: Uri, OriginalUri(original_uri): OriginalUri| async {
24///             // `uri` is `/users`
25///             // `original_uri` is `/api/users`
26///         }),
27///     );
28///
29/// let app = Router::new().nest("/api", api_routes);
30/// # let _: Router = app;
31/// ```
32///
33/// # Extracting via request extensions
34///
35/// `OriginalUri` can also be accessed from middleware via request extensions.
36/// This is useful for example with [`Trace`](tower_http::trace::Trace) to
37/// create a span that contains the full path, if your service might be nested:
38///
39/// ```
40/// use axum::{
41///     Router,
42///     extract::OriginalUri,
43///     http::Request,
44///     routing::get,
45/// };
46/// use tower_http::trace::TraceLayer;
47///
48/// let api_routes = Router::new()
49///     .route("/users/{id}", get(|| async { /* ... */ }))
50///     .layer(
51///         TraceLayer::new_for_http().make_span_with(|req: &Request<_>| {
52///             let path = if let Some(path) = req.extensions().get::<OriginalUri>() {
53///                 // This will include `/api`
54///                 path.0.path().to_owned()
55///             } else {
56///                 // The `OriginalUri` extension will always be present if using
57///                 // `Router` unless another extractor or middleware has removed it
58///                 req.uri().path().to_owned()
59///             };
60///             tracing::info_span!("http-request", %path)
61///         }),
62///     );
63///
64/// let app = Router::new().nest("/api", api_routes);
65/// # let _: Router = app;
66/// ```
67#[derive(Debug, Clone)]
68pub struct OriginalUri(pub Uri);
69
70impl<S> FromRequestParts<S> for OriginalUri
71where
72    S: Send + Sync,
73{
74    type Rejection = Infallible;
75
76    async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
77        let uri = Extension::<Self>::from_request_parts(parts, state)
78            .await
79            .unwrap_or_else(|_| Extension(OriginalUri(parts.uri.clone())))
80            .0;
81        Ok(uri)
82    }
83}
84
85axum_core::__impl_deref!(OriginalUri: Uri);