Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions axum-extra/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ and this project adheres to [Semantic Versioning].
.route_with_tsr("/path", get(/* handler */))
.route_with_tsr("/path", post(/* handler */))
```
- **added:** Add `RouterExt::typed_query` ([#3801])

[#3599]: https://github.com/tokio-rs/axum/pull/3599
[#3586]: https://github.com/tokio-rs/axum/pull/3586
[#3801]: https://github.com/tokio-rs/axum/pull/3801

# 0.12.6

Expand Down
23 changes: 23 additions & 0 deletions axum-extra/src/routing/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,19 @@ pub trait RouterExt<S>: sealed::Sealed {
T: SecondElementIs<P> + 'static,
P: TypedPath;

/// Add a typed `QUERY` route to the router.
///
/// The path will be inferred from the first argument to the handler function which must
/// implement [`TypedPath`].
///
/// See [`TypedPath`] for more details and examples.
#[cfg(feature = "typed-routing")]
fn typed_query<H, T, P>(self, handler: H) -> Self
where
H: axum::handler::Handler<T, S>,
T: SecondElementIs<P> + 'static,
P: TypedPath;

/// Add another route to the router with an additional "trailing slash redirect" route.
///
/// If you add a route _without_ a trailing slash, such as `/foo`, this method will also add a
Expand Down Expand Up @@ -368,6 +381,16 @@ where
self.route(P::PATH, axum::routing::connect(handler))
}

#[cfg(feature = "typed-routing")]
fn typed_query<H, T, P>(self, handler: H) -> Self
where
H: axum::handler::Handler<T, S>,
T: SecondElementIs<P> + 'static,
P: TypedPath,
{
self.route(P::PATH, axum::routing::query(handler))
}

#[track_caller]
fn route_with_tsr(mut self, path: &str, method_router: MethodRouter<S>) -> Self
where
Expand Down
2 changes: 2 additions & 0 deletions axum/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **changed:** `Redirect` constructors now accept any `impl Into<String>` ([#3635])
- **changed:** Updated `matchit` allowing for routes with captures and static prefixes and suffixes ([#3702])
- **fixed:** Responses to `HEAD` will not accidentally reply with `content-length: 0` anymore ([#3742])
- **added:** Add `MethodFilter::QUERY`, `routing::query[_service]` and `MethodRouter::query[_service]` ([#3801])

[#3158]: https://github.com/tokio-rs/axum/pull/3158
[#3261]: https://github.com/tokio-rs/axum/pull/3261
Expand All @@ -39,6 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[#3721]: https://github.com/tokio-rs/axum/pull/3721
[#3742]: https://github.com/tokio-rs/axum/pull/3742
[#3757]: https://github.com/tokio-rs/axum/pull/3757
[#3801]: https://github.com/tokio-rs/axum/pull/3801

# 0.8.9

Expand Down
26 changes: 17 additions & 9 deletions axum/src/routing/method_filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,25 @@ impl MethodFilter {
/// [extended CONNECT]: https://www.rfc-editor.org/rfc/rfc8441.html#section-4
/// [HTTP Upgrade Token Registry]: https://www.iana.org/assignments/http-upgrade-tokens/http-upgrade-tokens.xhtml
/// [`WebSocketUpgrade`]: crate::extract::WebSocketUpgrade
pub const CONNECT: Self = Self::from_bits(0b0_0000_0001);
pub const CONNECT: Self = Self::from_bits(0b00_0000_0001);
/// Match `DELETE` requests.
pub const DELETE: Self = Self::from_bits(0b0_0000_0010);
pub const DELETE: Self = Self::from_bits(0b00_0000_0010);
/// Match `GET` requests.
pub const GET: Self = Self::from_bits(0b0_0000_0100);
pub const GET: Self = Self::from_bits(0b00_0000_0100);
/// Match `HEAD` requests.
pub const HEAD: Self = Self::from_bits(0b0_0000_1000);
pub const HEAD: Self = Self::from_bits(0b00_0000_1000);
/// Match `OPTIONS` requests.
pub const OPTIONS: Self = Self::from_bits(0b0_0001_0000);
pub const OPTIONS: Self = Self::from_bits(0b00_0001_0000);
/// Match `PATCH` requests.
pub const PATCH: Self = Self::from_bits(0b0_0010_0000);
pub const PATCH: Self = Self::from_bits(0b00_0010_0000);
/// Match `POST` requests.
pub const POST: Self = Self::from_bits(0b0_0100_0000);
pub const POST: Self = Self::from_bits(0b00_0100_0000);
/// Match `PUT` requests.
pub const PUT: Self = Self::from_bits(0b0_1000_0000);
pub const PUT: Self = Self::from_bits(0b00_1000_0000);
/// Match `TRACE` requests.
pub const TRACE: Self = Self::from_bits(0b1_0000_0000);
pub const TRACE: Self = Self::from_bits(0b01_0000_0000);
/// Match `QUERY` requests.
pub const QUERY: Self = Self::from_bits(0b10_0000_0000);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unsure if the bit mask is meant to follow a certain protocol or it doesn’t matter.


const fn bits(self) -> u16 {
let bits = self;
Expand Down Expand Up @@ -99,6 +101,7 @@ impl TryFrom<Method> for MethodFilter {
Method::POST => Ok(Self::POST),
Method::PUT => Ok(Self::PUT),
Method::TRACE => Ok(Self::TRACE),
Method::QUERY => Ok(Self::QUERY),
other => Err(NoMatchingMethodFilter { method: other }),
}
}
Expand Down Expand Up @@ -155,6 +158,11 @@ mod tests {
MethodFilter::TRACE
);

assert_eq!(
MethodFilter::try_from(Method::QUERY).unwrap(),
MethodFilter::QUERY
);

assert!(
MethodFilter::try_from(http::Method::from_bytes(b"CUSTOM").unwrap())
.unwrap_err()
Expand Down
29 changes: 28 additions & 1 deletion axum/src/routing/method_routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ top_level_service_fn!(patch_service, PATCH);
top_level_service_fn!(post_service, POST);
top_level_service_fn!(put_service, PUT);
top_level_service_fn!(trace_service, TRACE);
top_level_service_fn!(query_service, QUERY);

/// Route requests with the given method to the service.
///
Expand Down Expand Up @@ -445,6 +446,7 @@ top_level_handler_fn!(patch, PATCH);
top_level_handler_fn!(post, POST);
top_level_handler_fn!(put, PUT);
top_level_handler_fn!(trace, TRACE);
top_level_handler_fn!(query, QUERY);

/// Route requests with the given method to the handler.
///
Expand Down Expand Up @@ -554,6 +556,7 @@ pub struct MethodRouter<S = (), E = Infallible> {
put: MethodEndpoint<S, E>,
trace: MethodEndpoint<S, E>,
connect: MethodEndpoint<S, E>,
query: MethodEndpoint<S, E>,
fallback: Fallback<S, E>,
allow_header: AllowHeader,
}
Expand Down Expand Up @@ -595,6 +598,7 @@ impl<S, E> fmt::Debug for MethodRouter<S, E> {
.field("put", &self.put)
.field("trace", &self.trace)
.field("connect", &self.connect)
.field("query", &self.query)
.field("fallback", &self.fallback)
.field("allow_header", &self.allow_header)
.finish()
Expand Down Expand Up @@ -648,6 +652,7 @@ where
chained_handler_fn!(post, POST);
chained_handler_fn!(put, PUT);
chained_handler_fn!(trace, TRACE);
chained_handler_fn!(query, QUERY);

/// Add a fallback [`Handler`] to the router.
pub fn fallback<H, T>(mut self, handler: H) -> Self
Expand Down Expand Up @@ -681,6 +686,7 @@ where
put,
trace,
connect,
query,
fallback,
allow_header: _,
} = self;
Expand All @@ -699,6 +705,7 @@ where
(put, MethodFilter::PUT),
(trace, MethodFilter::TRACE),
(connect, MethodFilter::CONNECT),
(query, MethodFilter::QUERY),
]
.into_iter()
.filter_map(|(ep, f)| ep.is_some().then_some(f))
Expand Down Expand Up @@ -811,6 +818,7 @@ where
put: MethodEndpoint::None,
trace: MethodEndpoint::None,
connect: MethodEndpoint::None,
query: MethodEndpoint::None,
allow_header: AllowHeader::None,
fallback: Fallback::Default(fallback),
}
Expand All @@ -828,6 +836,7 @@ where
put: self.put.with_state(&state),
trace: self.trace.with_state(&state),
connect: self.connect.with_state(&state),
query: self.query.with_state(&state),
allow_header: self.allow_header,
fallback: self.fallback.with_state(state),
}
Expand Down Expand Up @@ -986,6 +995,16 @@ where
&["CONNECT"],
);

set_endpoint(
"QUERY",
&mut self.query,
endpoint,
filter,
MethodFilter::QUERY,
&mut self.allow_header,
&["QUERY"],
);

self
}

Expand All @@ -998,6 +1017,7 @@ where
chained_service_fn!(post_service, POST);
chained_service_fn!(put_service, PUT);
chained_service_fn!(trace_service, TRACE);
chained_service_fn!(query_service, QUERY);

#[doc = include_str!("../docs/method_routing/fallback.md")]
pub fn fallback_service<T>(mut self, svc: T) -> Self
Expand Down Expand Up @@ -1034,6 +1054,7 @@ where
put: self.put.map(layer_fn.clone()),
trace: self.trace.map(layer_fn.clone()),
connect: self.connect.map(layer_fn.clone()),
query: self.query.map(layer_fn.clone()),
fallback: self.fallback.map(layer_fn),
allow_header: self.allow_header,
}
Expand All @@ -1059,6 +1080,7 @@ where
&& self.put.is_none()
&& self.trace.is_none()
&& self.connect.is_none()
&& self.query.is_none()
{
panic!(
"Adding a route_layer before any routes is a no-op. \
Expand All @@ -1076,7 +1098,8 @@ where
self.post = self.post.map(layer_fn.clone());
self.put = self.put.map(layer_fn.clone());
self.trace = self.trace.map(layer_fn.clone());
self.connect = self.connect.map(layer_fn);
self.connect = self.connect.map(layer_fn.clone());
self.query = self.query.map(layer_fn);

self
}
Expand Down Expand Up @@ -1122,6 +1145,7 @@ where
self.put = merge_inner(path, "PUT", self.put, other.put)?;
self.trace = merge_inner(path, "TRACE", self.trace, other.trace)?;
self.connect = merge_inner(path, "CONNECT", self.connect, other.connect)?;
self.query = merge_inner(path, "QUERY", self.query, other.query)?;

self.fallback = self
.fallback
Expand Down Expand Up @@ -1197,6 +1221,7 @@ where
put,
trace,
connect,
query,
fallback,
allow_header,
} = self;
Expand All @@ -1211,6 +1236,7 @@ where
call!(req, DELETE, delete);
call!(req, TRACE, trace);
call!(req, CONNECT, connect);
call!(req, QUERY, query);

let future = fallback.clone().call_with_state(req, state);

Expand Down Expand Up @@ -1254,6 +1280,7 @@ impl<S, E> Clone for MethodRouter<S, E> {
put: self.put.clone(),
trace: self.trace.clone(),
connect: self.connect.clone(),
query: self.query.clone(),
fallback: self.fallback.clone(),
allow_header: self.allow_header.clone(),
}
Expand Down
2 changes: 1 addition & 1 deletion axum/src/routing/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub use self::{into_make_service::IntoMakeService, method_filter::MethodFilter,
pub use self::method_routing::{
any, any_service, connect, connect_service, delete, delete_service, get, get_service, head,
head_service, on, on_service, options, options_service, patch, patch_service, post,
post_service, put, put_service, trace, trace_service, MethodRouter,
post_service, put, put_service, trace, trace_service, query, query_service, MethodRouter,
};

macro_rules! panic_on_err {
Expand Down
Loading