From 75c3968e66b6160d1f39c09fc023cebaba0b2d44 Mon Sep 17 00:00:00 2001 From: Lily Mara Date: Mon, 19 Jul 2021 17:24:50 -0700 Subject: [PATCH] Add `ServiceConfig::default_service` The `App` struct supports a "default" service which will be called when there are no other matching services available. I propose that we support adding this default service from the `ServiceConfig` struct, which will enable it to be used from `App::configure`. Currently if you want to test a default service (if you're creating a proxy for example) you must re-specify this default service each time you construct your app. This change will allow users to put their default services into the shared `App::configure` helper. --- src/app.rs | 4 ++++ src/config.rs | 41 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/app.rs b/src/app.rs index 5cff20568..1ea6cf6b4 100644 --- a/src/app.rs +++ b/src/app.rs @@ -205,6 +205,10 @@ where self.services.extend(cfg.services); self.external.extend(cfg.external); self.extensions.extend(cfg.app_data); + if let Some(default) = cfg.default { + self.default = Some(default); + } + self } diff --git a/src/config.rs b/src/config.rs index b072ace16..01a750fdc 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,7 +3,7 @@ use std::rc::Rc; use actix_http::Extensions; use actix_router::ResourceDef; -use actix_service::{boxed, IntoServiceFactory, ServiceFactory}; +use actix_service::{boxed, IntoServiceFactory, ServiceFactory, ServiceFactoryExt}; use crate::data::Data; use crate::error::Error; @@ -185,6 +185,7 @@ pub struct ServiceConfig { pub(crate) services: Vec>, pub(crate) external: Vec, pub(crate) app_data: Extensions, + pub(crate) default: Option>, } impl ServiceConfig { @@ -193,6 +194,7 @@ impl ServiceConfig { services: Vec::new(), external: Vec::new(), app_data: Extensions::new(), + default: None, } } @@ -213,6 +215,28 @@ impl ServiceConfig { self } + /// Default service to be used if no matching resource could be found. + /// + /// Counterpart to [`App::default_service()`](crate::App::default_service). + pub fn default_service(&mut self, f: F) -> &mut Self + where + F: IntoServiceFactory, + U: ServiceFactory< + ServiceRequest, + Config = (), + Response = ServiceResponse, + Error = Error, + > + 'static, + U::InitError: std::fmt::Debug, + { + // create and configure default resource + self.default = Some(Rc::new(boxed::factory(f.into_factory().map_init_err( + |e| log::error!("Can not construct default service: {:?}", e), + )))); + + self + } + /// Configure route for a specific path. /// /// Counterpart to [`App::route()`](crate::App::route). @@ -341,6 +365,21 @@ mod tests { assert_eq!(body, Bytes::from_static(b"https://youtube.com/watch/12345")); } + #[actix_rt::test] + async fn test_default_service() { + let srv = init_service(App::new().configure(|cfg| { + cfg.default_service( + web::get().to(|| HttpResponse::NotFound().body("four oh four")), + ); + })) + .await; + let req = TestRequest::with_uri("/path/i/did/not-configure").to_request(); + let resp = call_service(&srv, req).await; + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + let body = read_body(resp).await; + assert_eq!(body, Bytes::from_static(b"four oh four")); + } + #[actix_rt::test] async fn test_service() { let srv = init_service(App::new().configure(|cfg| {