mirror of
https://github.com/tokio-rs/axum.git
synced 2026-09-08 00:00:24 +02:00
Add type safe state extractor (#1155)
* begin threading the state through * Pass state to extractors * make state extractor work * make sure nesting with different states work * impl Service for MethodRouter<()> * Fix some of axum-macro's tests * Implement more traits for `State` * Update examples to use `State` * consistent naming of request body param * swap type params * Default the state param to () * fix docs references * Docs and handler state refactoring * docs clean ups * more consistent naming * when does MethodRouter implement Service? * add missing docs * use `Router`'s default state type param * changelog * don't use default type param for FromRequest and RequestParts probably safer for library authors so you don't accidentally forget * fix examples * minor docs tweaks * clarify how to convert handlers into services * group methods in one impl block * make sure merged `MethodRouter`s can access state * fix docs link * test merge with same state type * Document how to access state from middleware * Port cookie extractors to use state to extract keys (#1250) * Updates ECOSYSTEM with a new sample project (#1252) * Avoid unhelpful compiler suggestion (#1251) * fix docs typo * document how library authors should access state * Add `RequestParts::with_state` * fix example * apply suggestions from review * add relevant changes to axum-extra and axum-core changelogs * Add `route_service_with_tsr` * fix trybuild expectations * make sure `SpaRouter` works with routers that have state * Change order of type params on FromRequest and RequestParts * reverse order of `RequestParts::with_state` args to match type params * Add `FromRef` trait (#1268) * Add `FromRef` trait * Remove unnecessary type params * format * fix docs link * format examples * Avoid unnecessary `MethodRouter` * apply suggestions from review Co-authored-by: Dani Pardo <[email protected]> Co-authored-by: Jonas Platte <[email protected]>
This commit is contained in:
co-authored by
Dani Pardo
Jonas Platte
parent
90dbd52ee4
commit
423308de3c
@@ -1,13 +1,9 @@
|
||||
use axum::{
|
||||
body::Body,
|
||||
handler::Handler,
|
||||
http::Request,
|
||||
response::IntoResponse,
|
||||
routing::{delete, get, on, post, MethodFilter},
|
||||
routing::{delete, get, on, post, MethodFilter, MethodRouter},
|
||||
Router,
|
||||
};
|
||||
use std::{convert::Infallible, fmt};
|
||||
use tower_service::Service;
|
||||
|
||||
/// A resource which defines a set of conventional CRUD routes.
|
||||
///
|
||||
@@ -34,14 +30,15 @@ use tower_service::Service;
|
||||
/// .destroy(|Path(user_id): Path<u64>| async {});
|
||||
///
|
||||
/// let app = Router::new().merge(users);
|
||||
/// # let _: Router<axum::body::Body> = app;
|
||||
/// # let _: Router = app;
|
||||
/// ```
|
||||
pub struct Resource<B = Body> {
|
||||
#[derive(Debug)]
|
||||
pub struct Resource<S = (), B = Body> {
|
||||
pub(crate) name: String,
|
||||
pub(crate) router: Router<B>,
|
||||
pub(crate) router: Router<S, B>,
|
||||
}
|
||||
|
||||
impl<B> Resource<B>
|
||||
impl<B> Resource<(), B>
|
||||
where
|
||||
B: axum::body::HttpBody + Send + 'static,
|
||||
{
|
||||
@@ -49,16 +46,29 @@ where
|
||||
///
|
||||
/// All routes will be nested at `/{resource_name}`.
|
||||
pub fn named(resource_name: &str) -> Self {
|
||||
Self::named_with((), resource_name)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, B> Resource<S, B>
|
||||
where
|
||||
B: axum::body::HttpBody + Send + 'static,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
/// Create a `Resource` with the given name and state.
|
||||
///
|
||||
/// All routes will be nested at `/{resource_name}`.
|
||||
pub fn named_with(state: S, resource_name: &str) -> Self {
|
||||
Self {
|
||||
name: resource_name.to_owned(),
|
||||
router: Default::default(),
|
||||
router: Router::with_state(state),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a handler at `GET /{resource_name}`.
|
||||
pub fn index<H, T>(self, handler: H) -> Self
|
||||
where
|
||||
H: Handler<T, B>,
|
||||
H: Handler<T, S, B>,
|
||||
T: 'static,
|
||||
{
|
||||
let path = self.index_create_path();
|
||||
@@ -68,7 +78,7 @@ where
|
||||
/// Add a handler at `POST /{resource_name}`.
|
||||
pub fn create<H, T>(self, handler: H) -> Self
|
||||
where
|
||||
H: Handler<T, B>,
|
||||
H: Handler<T, S, B>,
|
||||
T: 'static,
|
||||
{
|
||||
let path = self.index_create_path();
|
||||
@@ -78,7 +88,7 @@ where
|
||||
/// Add a handler at `GET /{resource_name}/new`.
|
||||
pub fn new<H, T>(self, handler: H) -> Self
|
||||
where
|
||||
H: Handler<T, B>,
|
||||
H: Handler<T, S, B>,
|
||||
T: 'static,
|
||||
{
|
||||
let path = format!("/{}/new", self.name);
|
||||
@@ -88,7 +98,7 @@ where
|
||||
/// Add a handler at `GET /{resource_name}/:{resource_name}_id`.
|
||||
pub fn show<H, T>(self, handler: H) -> Self
|
||||
where
|
||||
H: Handler<T, B>,
|
||||
H: Handler<T, S, B>,
|
||||
T: 'static,
|
||||
{
|
||||
let path = self.show_update_destroy_path();
|
||||
@@ -98,7 +108,7 @@ where
|
||||
/// Add a handler at `GET /{resource_name}/:{resource_name}_id/edit`.
|
||||
pub fn edit<H, T>(self, handler: H) -> Self
|
||||
where
|
||||
H: Handler<T, B>,
|
||||
H: Handler<T, S, B>,
|
||||
T: 'static,
|
||||
{
|
||||
let path = format!("/{0}/:{0}_id/edit", self.name);
|
||||
@@ -108,7 +118,7 @@ where
|
||||
/// Add a handler at `PUT or PATCH /resource_name/:{resource_name}_id`.
|
||||
pub fn update<H, T>(self, handler: H) -> Self
|
||||
where
|
||||
H: Handler<T, B>,
|
||||
H: Handler<T, S, B>,
|
||||
T: 'static,
|
||||
{
|
||||
let path = self.show_update_destroy_path();
|
||||
@@ -118,7 +128,7 @@ where
|
||||
/// Add a handler at `DELETE /{resource_name}/:{resource_name}_id`.
|
||||
pub fn destroy<H, T>(self, handler: H) -> Self
|
||||
where
|
||||
H: Handler<T, B>,
|
||||
H: Handler<T, S, B>,
|
||||
T: 'static,
|
||||
{
|
||||
let path = self.show_update_destroy_path();
|
||||
@@ -133,13 +143,8 @@ where
|
||||
format!("/{0}/:{0}_id", self.name)
|
||||
}
|
||||
|
||||
fn route<T>(mut self, path: &str, svc: T) -> Self
|
||||
where
|
||||
T: Service<Request<B>, Error = Infallible> + Clone + Send + 'static,
|
||||
T::Response: IntoResponse,
|
||||
T::Future: Send + 'static,
|
||||
{
|
||||
self.router = self.router.route(path, svc);
|
||||
fn route(mut self, path: &str, method_router: MethodRouter<S, B>) -> Self {
|
||||
self.router = self.router.route(path, method_router);
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -150,21 +155,13 @@ impl<B> From<Resource<B>> for Router<B> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> fmt::Debug for Resource<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Resource")
|
||||
.field("name", &self.name)
|
||||
.field("router", &self.router)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[allow(unused_imports)]
|
||||
use super::*;
|
||||
use axum::{extract::Path, http::Method, Router};
|
||||
use tower::ServiceExt;
|
||||
use http::Request;
|
||||
use tower::{Service, ServiceExt};
|
||||
|
||||
#[tokio::test]
|
||||
async fn works() {
|
||||
@@ -220,7 +217,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
async fn call_route(app: &mut Router, method: Method, uri: &str) -> String {
|
||||
async fn call_route(app: &mut Router<()>, method: Method, uri: &str) -> String {
|
||||
let res = app
|
||||
.ready()
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user