1- use std:: net:: SocketAddr ;
2- use std :: rc :: Rc ;
3-
4- use actix_http :: Extensions ;
5- use actix_router :: ResourceDef ;
6- use actix_service :: { boxed , IntoServiceFactory , ServiceFactory } ;
7-
8- use crate :: data :: Data ;
9- use crate :: error :: Error ;
10- use crate :: guard :: Guard ;
11- use crate :: resource :: Resource ;
12- use crate :: rmap :: ResourceMap ;
13- use crate :: route :: Route ;
14- use crate :: service :: {
15- AppServiceFactory , HttpServiceFactory , ServiceFactoryWrapper , ServiceRequest ,
16- ServiceResponse ,
1+ use std:: { net:: SocketAddr , rc :: Rc } ;
2+
3+ use actix_service :: { boxed , IntoServiceFactory , ServiceFactory , ServiceFactoryExt as _ } ;
4+
5+ use crate :: {
6+ data :: Data ,
7+ dev :: { Extensions , ResourceDef } ,
8+ error :: Error ,
9+ guard :: Guard ,
10+ resource :: Resource ,
11+ rmap :: ResourceMap ,
12+ route :: Route ,
13+ service :: {
14+ AppServiceFactory , BoxedHttpServiceFactory , HttpServiceFactory , ServiceFactoryWrapper ,
15+ ServiceRequest , ServiceResponse ,
16+ } ,
1717} ;
1818
1919type Guards = Vec < Box < dyn Guard > > ;
20- type HttpNewService = boxed:: BoxServiceFactory < ( ) , ServiceRequest , ServiceResponse , Error , ( ) > ;
2120
2221/// Application configuration
2322pub struct AppService {
2423 config : AppConfig ,
2524 root : bool ,
26- default : Rc < HttpNewService > ,
25+ default : Rc < BoxedHttpServiceFactory > ,
2726 #[ allow( clippy:: type_complexity) ]
2827 services : Vec < (
2928 ResourceDef ,
30- HttpNewService ,
29+ BoxedHttpServiceFactory ,
3130 Option < Guards > ,
3231 Option < Rc < ResourceMap > > ,
3332 ) > ,
3433}
3534
3635impl AppService {
3736 /// Crate server settings instance.
38- pub ( crate ) fn new ( config : AppConfig , default : Rc < HttpNewService > ) -> Self {
37+ pub ( crate ) fn new ( config : AppConfig , default : Rc < BoxedHttpServiceFactory > ) -> Self {
3938 AppService {
4039 config,
4140 default,
@@ -56,7 +55,7 @@ impl AppService {
5655 AppConfig ,
5756 Vec < (
5857 ResourceDef ,
59- HttpNewService ,
58+ BoxedHttpServiceFactory ,
6059 Option < Guards > ,
6160 Option < Rc < ResourceMap > > ,
6261 ) > ,
@@ -81,7 +80,7 @@ impl AppService {
8180 }
8281
8382 /// Returns default handler factory.
84- pub fn default_service ( & self ) -> Rc < HttpNewService > {
83+ pub fn default_service ( & self ) -> Rc < BoxedHttpServiceFactory > {
8584 self . default . clone ( )
8685 }
8786
@@ -187,6 +186,7 @@ pub struct ServiceConfig {
187186 pub ( crate ) services : Vec < Box < dyn AppServiceFactory > > ,
188187 pub ( crate ) external : Vec < ResourceDef > ,
189188 pub ( crate ) app_data : Extensions ,
189+ pub ( crate ) default : Option < Rc < BoxedHttpServiceFactory > > ,
190190}
191191
192192impl ServiceConfig {
@@ -195,6 +195,7 @@ impl ServiceConfig {
195195 services : Vec :: new ( ) ,
196196 external : Vec :: new ( ) ,
197197 app_data : Extensions :: new ( ) ,
198+ default : None ,
198199 }
199200 }
200201
@@ -215,6 +216,29 @@ impl ServiceConfig {
215216 self
216217 }
217218
219+ /// Default service to be used if no matching resource could be found.
220+ ///
221+ /// Counterpart to [`App::default_service()`](crate::App::default_service).
222+ pub fn default_service < F , U > ( & mut self , f : F ) -> & mut Self
223+ where
224+ F : IntoServiceFactory < U , ServiceRequest > ,
225+ U : ServiceFactory <
226+ ServiceRequest ,
227+ Config = ( ) ,
228+ Response = ServiceResponse ,
229+ Error = Error ,
230+ > + ' static ,
231+ U :: InitError : std:: fmt:: Debug ,
232+ {
233+ let svc = f
234+ . into_factory ( )
235+ . map_init_err ( |err| log:: error!( "Can not construct default service: {:?}" , err) ) ;
236+
237+ self . default = Some ( Rc :: new ( boxed:: factory ( svc) ) ) ;
238+
239+ self
240+ }
241+
218242 /// Run external configuration as part of the application building process
219243 ///
220244 /// Counterpart to [`App::configure()`](crate::App::configure) that allows for easy nesting.
@@ -322,6 +346,38 @@ mod tests {
322346 assert_eq ! ( body, Bytes :: from_static( b"https://youtube.com/watch/12345" ) ) ;
323347 }
324348
349+ #[ actix_rt:: test]
350+ async fn registers_default_service ( ) {
351+ let srv = init_service (
352+ App :: new ( )
353+ . configure ( |cfg| {
354+ cfg. default_service (
355+ web:: get ( ) . to ( || HttpResponse :: NotFound ( ) . body ( "four oh four" ) ) ,
356+ ) ;
357+ } )
358+ . service ( web:: scope ( "/scoped" ) . configure ( |cfg| {
359+ cfg. default_service (
360+ web:: get ( ) . to ( || HttpResponse :: NotFound ( ) . body ( "scoped four oh four" ) ) ,
361+ ) ;
362+ } ) ) ,
363+ )
364+ . await ;
365+
366+ // app registers default service
367+ let req = TestRequest :: with_uri ( "/path/i/did/not-configure" ) . to_request ( ) ;
368+ let resp = call_service ( & srv, req) . await ;
369+ assert_eq ! ( resp. status( ) , StatusCode :: NOT_FOUND ) ;
370+ let body = read_body ( resp) . await ;
371+ assert_eq ! ( body, Bytes :: from_static( b"four oh four" ) ) ;
372+
373+ // scope registers default service
374+ let req = TestRequest :: with_uri ( "/scoped/path/i/did/not-configure" ) . to_request ( ) ;
375+ let resp = call_service ( & srv, req) . await ;
376+ assert_eq ! ( resp. status( ) , StatusCode :: NOT_FOUND ) ;
377+ let body = read_body ( resp) . await ;
378+ assert_eq ! ( body, Bytes :: from_static( b"scoped four oh four" ) ) ;
379+ }
380+
325381 #[ actix_rt:: test]
326382 async fn test_service ( ) {
327383 let srv = init_service ( App :: new ( ) . configure ( |cfg| {
0 commit comments