replace services with 500 error when data_factory construction failed

This commit is contained in:
fakeshadow 2021-01-25 09:08:33 +08:00
parent c201c15f8c
commit bb01b691b9
2 changed files with 99 additions and 73 deletions

View File

@ -11,11 +11,10 @@ use actix_service::{
apply, apply_fn_factory, IntoServiceFactory, ServiceFactory, ServiceFactoryExt,
Transform,
};
use futures_util::future::FutureExt;
use crate::app_service::{AppEntry, AppInit, AppRoutingFactory};
use crate::config::ServiceConfig;
use crate::data::{Data, DataFactory, FnDataFactory};
use crate::data::{Data, FnDataFactory};
use crate::dev::ResourceDef;
use crate::error::Error;
use crate::resource::Resource;
@ -114,22 +113,19 @@ where
E: std::fmt::Debug,
{
self.data_factories.push(Box::new(move || {
{
let fut = data();
async move {
match fut.await {
Err(e) => {
log::error!("Can not construct data instance: {:?}", e);
Err(())
}
Ok(data) => {
let data: Box<dyn DataFactory> = Box::new(Data::new(data));
Ok(data)
}
let fut = data();
Box::pin(async move {
match fut.await {
Err(e) => {
log::error!("Can not construct data instance: {:?}", e);
Err(())
}
Ok(data) => {
let data = Box::new(Data::new(data)) as _;
Ok(data)
}
}
}
.boxed_local()
})
}));
self
}
@ -453,7 +449,7 @@ where
fn into_factory(self) -> AppInit<T, B> {
AppInit {
async_data_factories: self.data_factories.into_boxed_slice().into(),
endpoint: self.endpoint,
endpoint: Rc::new(self.endpoint),
services: Rc::new(RefCell::new(self.services)),
external: RefCell::new(self.external),
default: self.default,
@ -473,9 +469,7 @@ mod tests {
use crate::http::{header, HeaderValue, Method, StatusCode};
use crate::middleware::DefaultHeaders;
use crate::service::ServiceRequest;
use crate::test::{
call_service, init_service, read_body, try_init_service, TestRequest,
};
use crate::test::{call_service, init_service, read_body, TestRequest};
use crate::{web, HttpRequest, HttpResponse};
#[actix_rt::test]
@ -546,13 +540,21 @@ mod tests {
#[actix_rt::test]
async fn test_data_factory_errors() {
let srv =
try_init_service(App::new().data_factory(|| err::<u32, _>(())).service(
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
))
.await;
let mut srv = init_service(
App::new().data_factory(|| err::<u32, _>(())).service(
web::resource("/")
.route(web::get().to(|_: web::Data<usize>| HttpResponse::Ok())),
),
)
.await;
assert!(srv.is_err());
let req = TestRequest::get().uri("/").to_request();
let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
let req = TestRequest::post().uri("/test").to_request();
let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
}
#[actix_rt::test]

View File

@ -26,14 +26,14 @@ type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Err
pub struct AppInit<T, B>
where
T: ServiceFactory<
ServiceRequest,
Config = (),
Response = ServiceResponse<B>,
Error = Error,
InitError = (),
>,
ServiceRequest,
Config = (),
Response = ServiceResponse<B>,
Error = Error,
InitError = (),
> + 'static,
{
pub(crate) endpoint: T,
pub(crate) endpoint: Rc<T>,
pub(crate) extensions: RefCell<Option<Extensions>>,
pub(crate) async_data_factories: Rc<[FnDataFactory]>,
pub(crate) services: Rc<RefCell<Vec<Box<dyn AppServiceFactory>>>>,
@ -45,12 +45,12 @@ where
impl<T, B> ServiceFactory<Request> for AppInit<T, B>
where
T: ServiceFactory<
ServiceRequest,
Config = (),
Response = ServiceResponse<B>,
Error = Error,
InitError = (),
>,
ServiceRequest,
Config = (),
Response = ServiceResponse<B>,
Error = Error,
InitError = (),
> + 'static,
T::Future: 'static,
{
type Response = ServiceResponse<B>;
@ -77,38 +77,21 @@ where
.into_iter()
.for_each(|mut srv| srv.register(&mut config));
let mut rmap = ResourceMap::new(ResourceDef::new(""));
let (config, services) = config.into_services();
// complete pipeline creation.
*self.factory_ref.borrow_mut() = Some(AppRoutingFactory {
default,
services: services
.into_iter()
.map(|(mut rdef, srv, guards, nested)| {
rmap.add(&mut rdef, nested);
(rdef, srv, RefCell::new(guards))
})
.collect::<Vec<_>>()
.into_boxed_slice()
.into(),
});
// external resources
for mut rdef in std::mem::take(&mut *self.external.borrow_mut()) {
rmap.add(&mut rdef, None);
}
// complete ResourceMap tree creation
let rmap = Rc::new(rmap);
rmap.finish(rmap.clone());
let (config, mut services) = config.into_services();
// construct all async data factory futures
let factory_futs = join_all(self.async_data_factories.iter().map(|f| f()));
// construct app service and middleware service factory future.
let endpoint_fut = self.endpoint.new_service(());
// clone factory_ref and endpoint to use them in async block.
let factory_ref = self.factory_ref.clone();
let endpoint = self.endpoint.clone();
// construct resource map. it would move to async block afterwards.
let mut rmap = ResourceMap::new(ResourceDef::new(""));
// add external resources
for mut rdef in std::mem::take(&mut *self.external.borrow_mut()) {
rmap.add(&mut rdef, None);
}
// take extensions or create new one as app data container.
let mut app_data = self
@ -118,15 +101,56 @@ where
.unwrap_or_else(Extensions::new);
Box::pin(async move {
// async data factories
let async_data_factories = factory_futs
// construct async data factories.
let res = factory_futs
.await
.into_iter()
.collect::<Result<Vec<_>, _>>()
.map_err(|_| ())?;
.collect::<Result<Vec<_>, _>>();
// app service and middleware
let service = endpoint_fut.await?;
let async_data_factories = match res {
Ok(async_data_factories) => async_data_factories,
Err(_) => {
// error occur producing app_data.
// replace services with 500 error response
services = services
.into_iter()
.map(|(rdef, _, guards, nested)| {
let srv = boxed::factory(fn_service(
|req: ServiceRequest| async {
Ok(req.into_response(
Response::InternalServerError().finish(),
))
},
));
(rdef, srv, guards, nested)
})
.collect();
// throw away async_data.
Vec::new()
}
};
// complete pipeline creation.
*factory_ref.borrow_mut() = Some(AppRoutingFactory {
default,
services: services
.into_iter()
.map(|(mut rdef, srv, guards, nested)| {
rmap.add(&mut rdef, nested);
(rdef, srv, RefCell::new(guards))
})
.collect::<Vec<_>>()
.into_boxed_slice()
.into(),
});
// complete ResourceMap tree creation
let rmap = Rc::new(rmap);
rmap.finish(rmap.clone());
// construct app service and middleware service.
let service = endpoint.new_service(()).await?;
// populate app data container from (async) data factories.
async_data_factories.iter().for_each(|factory| {