mirror of
https://github.com/tokio-rs/axum.git
synced 2026-08-07 00:00:15 +02:00
feat: add QUERY method routing (#3801)
This commit is contained in:
Generated
+2
-2
@@ -678,9 +678,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.4.0"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
|
||||
checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"itoa",
|
||||
|
||||
@@ -22,11 +22,13 @@ 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])
|
||||
- **fixed:** Escape multipart `Content-Disposition` parameters and reject
|
||||
newlines in field names and filenames ([#3776])
|
||||
|
||||
[#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
|
||||
[#3776]: https://github.com/tokio-rs/axum/pull/3776
|
||||
|
||||
# 0.12.6
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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])
|
||||
- **fixed:** `MethodRouter::merge` no longer lists a method twice in the `Allow`
|
||||
header after merging `get` and `head` ([#3836])
|
||||
|
||||
@@ -42,6 +43,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
[#3742]: https://github.com/tokio-rs/axum/pull/3742
|
||||
[#3836]: https://github.com/tokio-rs/axum/pull/3836
|
||||
[#3757]: https://github.com/tokio-rs/axum/pull/3757
|
||||
[#3801]: https://github.com/tokio-rs/axum/pull/3801
|
||||
|
||||
# 0.8.9
|
||||
|
||||
|
||||
+1
-1
@@ -112,7 +112,7 @@ axum-core = { path = "../axum-core", version = "0.5.6" }
|
||||
bytes = "1.7"
|
||||
futures-core = "0.3"
|
||||
futures-util = { version = "0.3", default-features = false, features = ["alloc"] }
|
||||
http = "1.0.0"
|
||||
http = "1.5.0"
|
||||
http-body = "1.0.0"
|
||||
http-body-util = "0.1.0"
|
||||
itoa = "1.0.5"
|
||||
|
||||
@@ -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);
|
||||
|
||||
const fn bits(self) -> u16 {
|
||||
let bits = self;
|
||||
@@ -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 }),
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
///
|
||||
@@ -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.
|
||||
///
|
||||
@@ -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,
|
||||
}
|
||||
@@ -604,6 +607,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()
|
||||
@@ -657,6 +661,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
|
||||
@@ -690,6 +695,7 @@ where
|
||||
put,
|
||||
trace,
|
||||
connect,
|
||||
query,
|
||||
fallback,
|
||||
allow_header: _,
|
||||
} = self;
|
||||
@@ -708,6 +714,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))
|
||||
@@ -820,6 +827,7 @@ where
|
||||
put: MethodEndpoint::None,
|
||||
trace: MethodEndpoint::None,
|
||||
connect: MethodEndpoint::None,
|
||||
query: MethodEndpoint::None,
|
||||
allow_header: AllowHeader::None,
|
||||
fallback: Fallback::Default(fallback),
|
||||
}
|
||||
@@ -837,6 +845,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),
|
||||
}
|
||||
@@ -995,6 +1004,16 @@ where
|
||||
&["CONNECT"],
|
||||
);
|
||||
|
||||
set_endpoint(
|
||||
"QUERY",
|
||||
&mut self.query,
|
||||
endpoint,
|
||||
filter,
|
||||
MethodFilter::QUERY,
|
||||
&mut self.allow_header,
|
||||
&["QUERY"],
|
||||
);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
@@ -1007,6 +1026,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
|
||||
@@ -1043,6 +1063,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,
|
||||
}
|
||||
@@ -1068,6 +1089,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. \
|
||||
@@ -1085,7 +1107,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
|
||||
}
|
||||
@@ -1131,6 +1154,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
|
||||
@@ -1206,6 +1230,7 @@ where
|
||||
put,
|
||||
trace,
|
||||
connect,
|
||||
query,
|
||||
fallback,
|
||||
allow_header,
|
||||
} = self;
|
||||
@@ -1220,6 +1245,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);
|
||||
|
||||
@@ -1263,6 +1289,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(),
|
||||
}
|
||||
|
||||
@@ -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, query, query_service, trace, trace_service, MethodRouter,
|
||||
};
|
||||
|
||||
macro_rules! panic_on_err {
|
||||
|
||||
Generated
+2
-2
@@ -2308,9 +2308,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.4.2"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425"
|
||||
checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"itoa",
|
||||
|
||||
Reference in New Issue
Block a user