#combinator #guard #layer #axum-server #logical #tower-service #self

已删除 axum_guard_combinator

使用逻辑组合器组合实现从请求中派生的类型

0.2.0 2022年8月27日
0.1.0 2022年8月25日

32 in #axum-server

MIT 许可证

22KB
436 代码行

axum_guard_combinator

https://crates.io/crates/axum_guard_combinator

这个库允许你在 Axum 服务器中的 Tower 服务层内部编写逻辑(或、与)组合器,该组合器提取实现的 Guard 类型的给定类型 T,并检查类型 T 的值与层组合器内部的输入值。以下是一个始终返回 true 的结构。

#[derive(Clone,Copy,Debug,PartialEq)]
    pub struct Always;

    impl Guard for Always {
        fn check_guard(&self,_:&Self) -> bool {
            true
        }
    }
    #[async_trait::async_trait]
    impl<B:Send+Sync> FromRequest<B> for Always {
        type Rejection = ();

        async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
            Ok(Self)
        }
    }

以下是一个将此结构与始终返回 false 的结构结合的测试用例

 #[tokio::test]
    async fn test_or_happy_path() {
        let app = Router::new()
            .route("/",get(ok
                .layer(GuardLayer::with(Always.or(Never)))));
        let response = app
            .oneshot(
                Request::builder()
                    .uri("/")
                    .body(Body::empty())
                    .unwrap(),
            )
            .await
            .unwrap();

        assert_eq!(response.status(), StatusCode::OK);
    }

您可以将组合器嵌套到任意深度

 #[tokio::test]
    async fn test_happy_nested() {
        let app = Router::new()
            .route("/",get(ok
                .layer(GuardLayer::with(
                        Never.or(
                    Always.and(
                            Always.or(
                                Never)))))));
        let response = app
            .oneshot(
                Request::builder()
                    .uri("/")
                    .body(Body::empty())
                    .unwrap(),
            )
            .await
            .unwrap();

        assert_eq!(response.status(), StatusCode::OK);
    }

您可以在层中输入数据,这些数据将在 Guard 实现的 check_guard 方法中评估

#[derive(Clone,Debug,PartialEq)]
    pub struct ArbitraryData{
        data:String,
    }
    impl Guard for ArbitraryData{
        fn check_guard(&self, expected: &Self) -> bool {
            *self == *expected
        }
    }
    #[async_trait::async_trait]
    impl<B:Send+Sync> FromRequest<B> for ArbitraryData {
        type Rejection = ();

        async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
            Ok(Self{
                data: req.headers().get("data").unwrap()
                    .to_str().unwrap().to_string()
            })
        }
    }
  #[tokio::test]
    async fn test_or_happy_path_with_data() {
        let app = Router::new()
            .route("/",get(ok
                .layer(GuardLayer::with(ArbitraryData{
                    data:String::from("Hello World.")
                }))));
        let response = app
            .oneshot(
                Request::builder()
                    .uri("/")
                    .header("data","Hello World.")
                    .body(Body::empty())
                    .unwrap(),
            )
            .await
            .unwrap();

        assert_eq!(response.status(), StatusCode::OK);
    }

依赖关系

~6.5–9MB
~164K SLoC