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:
David Pedersen
2022-08-17 15:13:31 +00:00
committed by GitHub
co-authored by Dani Pardo Jonas Platte
parent 90dbd52ee4
commit 423308de3c
132 changed files with 2404 additions and 1126 deletions
+32 -35
View File
@@ -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