【问题标题】:Return Either HTML or JSON From Actix Web Handler从 Actix Web 处理程序返回 HTML 或 JSON
【发布时间】:2021-05-13 18:43:47
【问题描述】:

我希望我的所有路由在请求时都返回 Json 而不是 HTML。

例如,当请求/index.json 时,它应该返回用于构建/index html 响应的对象。

我目前在第 11 行的路由宏中使用“尾部匹配”来处理这个问题:

  5 #[derive(Serialize)]
  6 struct FollowerPresenter {
  7     follower_name: String,
  8     followee_name: String,
  9 }
 10
 11 #[get("/index{tail:.*}")]
 12 async fn index(
 13     mime: web::Path<String>,
 14     template: web::Data<tera::Tera>,
 15 ) -> Result<HttpResponse, Error> {
 16     let presenter = FollowerPresenter {
 17         follower_name: "Jill".into(),
 18         followee_name: "Jim".into(),
 19     };
 20
 21     match mime.as_str() {
 22         "" | "/" => {
 23             let body = template
 24                 .render("index.html", &Context::from_serialize(&presenter).unwrap())
 25                 .map_err(|_| error::ErrorInternalServerError("Template error"))?;
 26
 27             Ok(HttpResponse::Ok().content_type("text/html").body(body))
 28         }
 29         ".json" => Ok(HttpResponse::Ok().json(presenter)),
 30         _ => Err(error::ErrorNotFound("Resource Not Found")),
 31     }
 32 }
 33
 34 #[actix_web::main]
 35 async fn main() -> std::io::Result<()> {
 36     HttpServer::new(|| {
 37         let tera = Tera::new(concat!(env!("CARGO_MANIFEST_DIR"), "/src/views/**/*")).unwrap();
 38
 39         App::new().data(tera).service(index)
 40     })
 41     .bind("127.0.0.1:3000")?
 42     .run()
 43     .await
 44 }

必须有一种方法,使用中间件或其他东西,以避免将整个控制结构放在每个处理程序的第 21-30 行。

【问题讨论】:

    标签: rust actix-web rust-actix


    【解决方案1】:

    您的问题(以及我的回答也是)依赖于req.query("tail"),并且可以通过检查网址本身来改进(您可以在其中使用str::ends_with)。

    这是一个基于Responder 的工作解决方案。

    use actix_http::{Error, Response};
    use actix_web::*;
    use futures_util::future::{err, ok, Ready};
    use serde::Serialize;
    use tera::{Context, Tera};
    
    struct MyResponder<P: Serialize, T: Into<String>> {
        presenter: P,
        template: T,
    }
    
    impl<P: Serialize, T: Into<String>> Responder for MyResponder<P, T> {
        type Error = Error;
        type Future = Ready<Result<Response, Error>>;
    
        fn respond_to(self, req: &HttpRequest) -> Self::Future {
            let mime = req.match_info().query("tail");
            let template = req.app_data::<web::Data<tera::Tera>>().unwrap();
            let presenter = serde_json::to_value(&self.presenter).unwrap();
            match mime {
                "" | "/" => {
                    let body = template
                        .render(
                            &self.template.into(),
                            &Context::from_serialize(&presenter).unwrap(),
                        )
                        .unwrap();
                    ok(HttpResponse::Ok().content_type("text/html").body(body))
                }
                ".json" => ok(HttpResponse::Ok().json(&presenter)),
                _ => err(error::ErrorNotFound("Resource Not Found")),
            }
        }
    }
    
    #[derive(Serialize)]
    struct FollowerPresenter {
        follower_name: String,
        followee_name: String,
    }
    
    #[get("/index{tail:.*}")]
    async fn index() -> impl Responder {
        let presenter = FollowerPresenter {
            follower_name: "Jill".into(),
            followee_name: "Jim".into(),
        };
    
        MyResponder {
            presenter,
            template: "index.html",
        }
    }
    
    #[actix_web::main]
    async fn main() -> std::io::Result<()> {
        HttpServer::new(|| {
            let tera = Tera::new(concat!(env!("CARGO_MANIFEST_DIR"), "/src/views/**/*")).unwrap();
            App::new().data(tera).service(index)
        })
        .bind("127.0.0.1:3000")?
        .run()
        .await
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
        use actix_web::{body::Body, test, App};
        use serde_json::json;
    
        #[actix_rt::test]
        async fn test_json_get() {
            let tera = Tera::new(concat!(env!("CARGO_MANIFEST_DIR"), "/src/views/**/*")).unwrap();
            let mut app = test::init_service(App::new().data(tera).service(index)).await;
            let req = test::TestRequest::with_uri("/index.json").to_request();
            let mut resp = test::call_service(&mut app, req).await;
            let body = resp.take_body();
            let body = body.as_ref().unwrap();
            assert!(resp.status().is_success());
            assert_eq!(
                &Body::from(json!({"follower_name":"Jill", "followee_name": "Jim" })), // or serde.....
                body
            );
        }
        #[actix_rt::test]
        #[should_panic] /// Template doesnt exist
        async fn test_web_get() {
            let tera = Tera::new(concat!(env!("CARGO_MANIFEST_DIR"), "/src/views/**/*")).unwrap();
            let mut app = test::init_service(App::new().data(tera).service(index)).await;
            let req = test::TestRequest::with_uri("/index").to_request();
            let _resp = test::call_service(&mut app, req).await;
    
        }
    }
    

    运行测试产生:

    running 2 tests
    test tests::test_json_get ... ok
    test tests::test_web_get ... ok
    
    test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
    

    【讨论】:

      猜你喜欢
      • 2020-08-22
      • 1970-01-01
      • 2019-09-05
      • 1970-01-01
      • 2016-05-24
      • 1970-01-01
      • 1970-01-01
      • 2012-01-08
      • 2021-08-08
      相关资源
      最近更新 更多