Skip to content

Commit b1c85ba

Browse files
authored
Add ServiceConfig::default_service (#2743)
* Add `ServiceConfig::default_service` based on #2338 * update changelog
1 parent 9aab911 commit b1c85ba

File tree

4 files changed

+94
-25
lines changed

4 files changed

+94
-25
lines changed

actix-web/CHANGES.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44
### Added
55
- Add `ServiceRequest::extract()` to make it easier to use extractors when writing middlewares. [#2647]
66
- Add `Route::wrap()` to allow individual routes to use middleware. [#2725]
7+
- Add `ServiceConfig::default_service()`. [#2338] [#2743]
78

89
### Fixed
910
- Clear connection-level data on `HttpRequest` drop. [#2742]
1011

12+
[#2338]: https://github.com/actix/actix-web/pull/2338
1113
[#2647]: https://github.com/actix/actix-web/pull/2647
1214
[#2725]: https://github.com/actix/actix-web/pull/2725
1315
[#2742]: https://github.com/actix/actix-web/pull/2742
16+
[#2743]: https://github.com/actix/actix-web/pull/2743
1417

1518

1619
## 4.0.1 - 2022-02-25
@@ -726,7 +729,7 @@
726729
### Removed
727730
- Public modules `middleware::{normalize, err_handlers}`. All necessary middleware types are now
728731
exposed directly by the `middleware` module.
729-
- Remove `actix-threadpool` as dependency. `actix_threadpool::BlockingError` error type can be imported
732+
- Remove `actix-threadpool` as dependency. `actix_threadpool::BlockingError` error type can be imported
730733
from `actix_web::error` module. [#1878]
731734

732735
[#1812]: https://github.com/actix/actix-web/pull/1812
@@ -828,7 +831,7 @@
828831

829832
## 3.0.0-beta.4 - 2020-09-09
830833
### Added
831-
- `middleware::NormalizePath` now has configurable behavior for either always having a trailing
834+
- `middleware::NormalizePath` now has configurable behavior for either always having a trailing
832835
slash, or as the new addition, always trimming trailing slashes. [#1639]
833836

834837
### Changed

actix-web/src/app.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,10 +185,17 @@ where
185185
F: FnOnce(&mut ServiceConfig),
186186
{
187187
let mut cfg = ServiceConfig::new();
188+
188189
f(&mut cfg);
190+
189191
self.services.extend(cfg.services);
190192
self.external.extend(cfg.external);
191193
self.extensions.extend(cfg.app_data);
194+
195+
if let Some(default) = cfg.default {
196+
self.default = Some(default);
197+
}
198+
192199
self
193200
}
194201

@@ -267,7 +274,6 @@ where
267274
{
268275
let svc = svc
269276
.into_factory()
270-
.map(|res| res.map_into_boxed_body())
271277
.map_init_err(|e| log::error!("Can not construct default service: {:?}", e));
272278

273279
self.default = Some(Rc::new(boxed::factory(svc)));

actix-web/src/config.rs

Lines changed: 78 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,40 @@
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

1919
type Guards = Vec<Box<dyn Guard>>;
20-
type HttpNewService = boxed::BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>;
2120

2221
/// Application configuration
2322
pub 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

3635
impl 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

192192
impl 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| {

actix-web/src/scope.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,10 @@ where
198198
.get_or_insert_with(Extensions::new)
199199
.extend(cfg.app_data);
200200

201+
if let Some(default) = cfg.default {
202+
self.default = Some(default);
203+
}
204+
201205
self
202206
}
203207

0 commit comments

Comments
 (0)