mirror of
https://github.com/tokio-rs/axum.git
synced 2025-09-28 21:40:55 +00:00
Change how Resource
s are added to Router
s (#544)
This commit is contained in:
parent
44a49cb199
commit
ab9f1ef993
@ -1,6 +1,6 @@
|
|||||||
//! Additional types for defining routes.
|
//! Additional types for defining routes.
|
||||||
|
|
||||||
use axum::Router;
|
use axum::{body::Body, Router};
|
||||||
|
|
||||||
mod resource;
|
mod resource;
|
||||||
|
|
||||||
@ -8,24 +8,55 @@ pub use self::resource::Resource;
|
|||||||
|
|
||||||
/// Extension trait that adds additional methods to [`Router`].
|
/// Extension trait that adds additional methods to [`Router`].
|
||||||
pub trait RouterExt<B>: sealed::Sealed {
|
pub trait RouterExt<B>: sealed::Sealed {
|
||||||
/// Add a [`Resource`] to the router.
|
/// Add the routes from `T`'s [`HasRoutes::routes`] to this router.
|
||||||
///
|
///
|
||||||
/// See [`Resource`] for more details.
|
/// # Example
|
||||||
fn resource<F>(self, name: &str, f: F) -> Self
|
///
|
||||||
|
/// Using [`Resource`] which implements [`HasRoutes`]:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use axum::{Router, routing::get};
|
||||||
|
/// use axum_extra::routing::{RouterExt, Resource};
|
||||||
|
///
|
||||||
|
/// let app = Router::new()
|
||||||
|
/// .with(
|
||||||
|
/// Resource::named("users")
|
||||||
|
/// .index(|| async {})
|
||||||
|
/// .create(|| async {})
|
||||||
|
/// )
|
||||||
|
/// .with(
|
||||||
|
/// Resource::named("teams").index(|| async {})
|
||||||
|
/// );
|
||||||
|
/// # let _: Router<axum::body::Body> = app;
|
||||||
|
/// ```
|
||||||
|
fn with<T>(self, routes: T) -> Self
|
||||||
where
|
where
|
||||||
F: FnOnce(resource::Resource<B>) -> resource::Resource<B>;
|
T: HasRoutes<B>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B> RouterExt<B> for Router<B> {
|
impl<B> RouterExt<B> for Router<B>
|
||||||
fn resource<F>(self, name: &str, f: F) -> Self
|
|
||||||
where
|
where
|
||||||
F: FnOnce(resource::Resource<B>) -> resource::Resource<B>,
|
B: Send + 'static,
|
||||||
{
|
{
|
||||||
f(resource::Resource {
|
fn with<T>(self, routes: T) -> Self
|
||||||
name: name.to_owned(),
|
where
|
||||||
router: self,
|
T: HasRoutes<B>,
|
||||||
})
|
{
|
||||||
.router
|
self.merge(routes.routes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait for things that can provide routes.
|
||||||
|
///
|
||||||
|
/// Used with [`RouterExt::with`].
|
||||||
|
pub trait HasRoutes<B = Body> {
|
||||||
|
/// Get the routes.
|
||||||
|
fn routes(self) -> Router<B>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B> HasRoutes<B> for Router<B> {
|
||||||
|
fn routes(self) -> Router<B> {
|
||||||
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use super::HasRoutes;
|
||||||
use axum::{
|
use axum::{
|
||||||
body::{Body, BoxBody},
|
body::{Body, BoxBody},
|
||||||
handler::Handler,
|
handler::Handler,
|
||||||
@ -14,11 +15,11 @@ use tower_service::Service;
|
|||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use axum::{Router, routing::get, extract::Path};
|
/// use axum::{Router, routing::get, extract::Path};
|
||||||
/// use axum_extra::routing::RouterExt;
|
/// use axum_extra::routing::{RouterExt, Resource};
|
||||||
///
|
///
|
||||||
/// let app = Router::new().resource("users", |r| {
|
/// let users = Resource::named("users")
|
||||||
/// // Define a route for `GET /users`
|
/// // Define a route for `GET /users`
|
||||||
/// r.index(|| async {})
|
/// .index(|| async {})
|
||||||
/// // `POST /users`
|
/// // `POST /users`
|
||||||
/// .create(|| async {})
|
/// .create(|| async {})
|
||||||
/// // `GET /users/new`
|
/// // `GET /users/new`
|
||||||
@ -41,8 +42,9 @@ use tower_service::Service;
|
|||||||
/// // This defines a route for `GET /users/featured`
|
/// // This defines a route for `GET /users/featured`
|
||||||
/// .nest_collection(
|
/// .nest_collection(
|
||||||
/// Router::new().route("/featured", get(|| async {})),
|
/// Router::new().route("/featured", get(|| async {})),
|
||||||
/// )
|
/// );
|
||||||
/// });
|
///
|
||||||
|
/// let app = Router::new().with(users);
|
||||||
/// # let _: Router<axum::body::Body> = app;
|
/// # let _: Router<axum::body::Body> = app;
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -52,27 +54,17 @@ pub struct Resource<B = Body> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<B: Send + 'static> Resource<B> {
|
impl<B: Send + 'static> Resource<B> {
|
||||||
fn index_create_path(&self) -> String {
|
/// Create a `Resource` with the given name.
|
||||||
format!("/{}", self.name)
|
///
|
||||||
|
/// All routes will be nested at `/{resource_name}`.
|
||||||
|
pub fn named(resource_name: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
name: resource_name.to_string(),
|
||||||
|
router: Default::default(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_update_destroy_path(&self) -> String {
|
/// Add a handler at `GET /{resource_name}`.
|
||||||
format!("/{0}/:{0}_id", self.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn route<T>(mut self, path: &str, svc: T) -> Self
|
|
||||||
where
|
|
||||||
T: Service<Request<B>, Response = Response<BoxBody>, Error = Infallible>
|
|
||||||
+ Clone
|
|
||||||
+ Send
|
|
||||||
+ 'static,
|
|
||||||
T::Future: Send + 'static,
|
|
||||||
{
|
|
||||||
self.router = self.router.route(path, svc);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a handler at `GET /resource_name`.
|
|
||||||
pub fn index<H, T>(self, handler: H) -> Self
|
pub fn index<H, T>(self, handler: H) -> Self
|
||||||
where
|
where
|
||||||
H: Handler<T, B>,
|
H: Handler<T, B>,
|
||||||
@ -173,6 +165,32 @@ impl<B: Send + 'static> Resource<B> {
|
|||||||
self.router = self.router.nest(&path, svc);
|
self.router = self.router.nest(&path, svc);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn index_create_path(&self) -> String {
|
||||||
|
format!("/{}", self.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_update_destroy_path(&self) -> String {
|
||||||
|
format!("/{0}/:{0}_id", self.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn route<T>(mut self, path: &str, svc: T) -> Self
|
||||||
|
where
|
||||||
|
T: Service<Request<B>, Response = Response<BoxBody>, Error = Infallible>
|
||||||
|
+ Clone
|
||||||
|
+ Send
|
||||||
|
+ 'static,
|
||||||
|
T::Future: Send + 'static,
|
||||||
|
{
|
||||||
|
self.router = self.router.route(path, svc);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B> HasRoutes<B> for Resource<B> {
|
||||||
|
fn routes(self) -> Router<B> {
|
||||||
|
self.router
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -185,8 +203,8 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn works() {
|
async fn works() {
|
||||||
let mut app = Router::new().resource("users", |r| {
|
let users = Resource::named("users")
|
||||||
r.index(|| async { "users#index" })
|
.index(|| async { "users#index" })
|
||||||
.create(|| async { "users#create" })
|
.create(|| async { "users#create" })
|
||||||
.new(|| async { "users#new" })
|
.new(|| async { "users#new" })
|
||||||
.show(|Path(id): Path<u64>| async move { format!("users#show id={}", id) })
|
.show(|Path(id): Path<u64>| async move { format!("users#show id={}", id) })
|
||||||
@ -199,8 +217,9 @@ mod tests {
|
|||||||
))
|
))
|
||||||
.nest_collection(
|
.nest_collection(
|
||||||
Router::new().route("/featured", get(|| async move { "users#featured" })),
|
Router::new().route("/featured", get(|| async move { "users#featured" })),
|
||||||
)
|
);
|
||||||
});
|
|
||||||
|
let mut app = Router::new().with(users);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
call_route(&mut app, Method::GET, "/users").await,
|
call_route(&mut app, Method::GET, "/users").await,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user