从 Actix Web 处理程序返回 HTML 或 JSON

问题描述

我希望我的所有路由在请求时返回 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 行。

解决方法

好吧,您的问题(以及我的回答)依赖于 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

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...