mirror of
https://github.com/tokio-rs/axum.git
synced 2026-08-22 00:00:17 +02:00
@@ -0,0 +1,86 @@
|
||||
use super::{rejection::*, FromRequest, RequestParts};
|
||||
use async_trait::async_trait;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Access the path in the router that matches the request.
|
||||
///
|
||||
/// ```
|
||||
/// use axum::{
|
||||
/// Router,
|
||||
/// extract::MatchedPath,
|
||||
/// routing::get,
|
||||
/// };
|
||||
///
|
||||
/// let app = Router::new().route(
|
||||
/// "/users/:id",
|
||||
/// get(|path: MatchedPath| async move {
|
||||
/// let path = path.as_str();
|
||||
/// // `path` will be "/users/:id"
|
||||
/// })
|
||||
/// );
|
||||
/// # async {
|
||||
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
/// # };
|
||||
/// ```
|
||||
///
|
||||
/// `MatchedPath` can also be accessed from middleware via request extensions.
|
||||
/// This is useful for example with [`Trace`](tower_http::trace::Trace) to
|
||||
/// create a span that contains the matched path:
|
||||
///
|
||||
/// ```
|
||||
/// use axum::{
|
||||
/// Router,
|
||||
/// extract::MatchedPath,
|
||||
/// http::Request,
|
||||
/// routing::get,
|
||||
/// };
|
||||
/// use tower_http::trace::TraceLayer;
|
||||
///
|
||||
/// let app = Router::new()
|
||||
/// .route("/users/:id", get(|| async { /* ... */ }))
|
||||
/// .layer(
|
||||
/// TraceLayer::new_for_http().make_span_with(|req: &Request<_>| {
|
||||
/// let path = if let Some(path) = req.extensions().get::<MatchedPath>() {
|
||||
/// path.as_str()
|
||||
/// } else {
|
||||
/// req.uri().path()
|
||||
/// };
|
||||
/// tracing::info_span!("http-request", %path)
|
||||
/// }),
|
||||
/// );
|
||||
/// # async {
|
||||
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
/// # };
|
||||
/// ```
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MatchedPath(pub(crate) Arc<str>);
|
||||
|
||||
impl MatchedPath {
|
||||
/// Returns a `str` representation of the path.
|
||||
pub fn as_str(&self) -> &str {
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for MatchedPath
|
||||
where
|
||||
B: Send,
|
||||
{
|
||||
type Rejection = MatchedPathRejection;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
let extensions =
|
||||
req.extensions()
|
||||
.ok_or(MatchedPathRejection::ExtensionsAlreadyExtracted(
|
||||
ExtensionsAlreadyExtracted,
|
||||
))?;
|
||||
|
||||
let matched_path = extensions
|
||||
.get::<Self>()
|
||||
.ok_or(MatchedPathRejection::MatchedPathMissing(MatchedPathMissing))?
|
||||
.clone();
|
||||
|
||||
Ok(matched_path)
|
||||
}
|
||||
}
|
||||
@@ -172,6 +172,7 @@ pub mod ws;
|
||||
mod content_length_limit;
|
||||
mod extension;
|
||||
mod form;
|
||||
mod matched_path;
|
||||
mod path;
|
||||
mod query;
|
||||
mod raw_query;
|
||||
@@ -186,6 +187,7 @@ pub use self::{
|
||||
extension::Extension,
|
||||
extractor_middleware::extractor_middleware,
|
||||
form::Form,
|
||||
matched_path::MatchedPath,
|
||||
path::Path,
|
||||
query::Query,
|
||||
raw_query::RawQuery,
|
||||
|
||||
@@ -273,6 +273,23 @@ composite_rejection! {
|
||||
}
|
||||
}
|
||||
|
||||
define_rejection! {
|
||||
#[status = INTERNAL_SERVER_ERROR]
|
||||
#[body = "No matched path found"]
|
||||
/// Rejection if no matched path could be found.
|
||||
///
|
||||
/// See [`MatchedPath`](super::MatchedPath) for more details.
|
||||
pub struct MatchedPathMissing;
|
||||
}
|
||||
|
||||
composite_rejection! {
|
||||
/// Rejection used for [`MatchedPath`](super::MatchedPath).
|
||||
pub enum MatchedPathRejection {
|
||||
ExtensionsAlreadyExtracted,
|
||||
MatchedPathMissing,
|
||||
}
|
||||
}
|
||||
|
||||
/// Rejection used for [`ContentLengthLimit`](super::ContentLengthLimit).
|
||||
///
|
||||
/// Contains one variant for each way the
|
||||
|
||||
Reference in New Issue
Block a user