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

View File

@ -31,9 +31,9 @@ where
Response = ServiceResponse<B>, Response = ServiceResponse<B>,
Error = Error, Error = Error,
InitError = (), InitError = (),
>, > + 'static,
{ {
pub(crate) endpoint: T, pub(crate) endpoint: Rc<T>,
pub(crate) extensions: RefCell<Option<Extensions>>, pub(crate) extensions: RefCell<Option<Extensions>>,
pub(crate) async_data_factories: Rc<[FnDataFactory]>, pub(crate) async_data_factories: Rc<[FnDataFactory]>,
pub(crate) services: Rc<RefCell<Vec<Box<dyn AppServiceFactory>>>>, pub(crate) services: Rc<RefCell<Vec<Box<dyn AppServiceFactory>>>>,
@ -50,7 +50,7 @@ where
Response = ServiceResponse<B>, Response = ServiceResponse<B>,
Error = Error, Error = Error,
InitError = (), InitError = (),
>, > + 'static,
T::Future: 'static, T::Future: 'static,
{ {
type Response = ServiceResponse<B>; type Response = ServiceResponse<B>;
@ -77,12 +77,62 @@ where
.into_iter() .into_iter()
.for_each(|mut srv| srv.register(&mut config)); .for_each(|mut srv| srv.register(&mut config));
let mut rmap = ResourceMap::new(ResourceDef::new("")); let (config, mut services) = config.into_services();
let (config, services) = config.into_services(); // construct all async data factory futures
let factory_futs = join_all(self.async_data_factories.iter().map(|f| f()));
// 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
.extensions
.borrow_mut()
.take()
.unwrap_or_else(Extensions::new);
Box::pin(async move {
// construct async data factories.
let res = factory_futs
.await
.into_iter()
.collect::<Result<Vec<_>, _>>();
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. // complete pipeline creation.
*self.factory_ref.borrow_mut() = Some(AppRoutingFactory { *factory_ref.borrow_mut() = Some(AppRoutingFactory {
default, default,
services: services services: services
.into_iter() .into_iter()
@ -95,38 +145,12 @@ where
.into(), .into(),
}); });
// external resources
for mut rdef in std::mem::take(&mut *self.external.borrow_mut()) {
rmap.add(&mut rdef, None);
}
// complete ResourceMap tree creation // complete ResourceMap tree creation
let rmap = Rc::new(rmap); let rmap = Rc::new(rmap);
rmap.finish(rmap.clone()); rmap.finish(rmap.clone());
// construct all async data factory futures // construct app service and middleware service.
let factory_futs = join_all(self.async_data_factories.iter().map(|f| f())); let service = endpoint.new_service(()).await?;
// construct app service and middleware service factory future.
let endpoint_fut = self.endpoint.new_service(());
// take extensions or create new one as app data container.
let mut app_data = self
.extensions
.borrow_mut()
.take()
.unwrap_or_else(Extensions::new);
Box::pin(async move {
// async data factories
let async_data_factories = factory_futs
.await
.into_iter()
.collect::<Result<Vec<_>, _>>()
.map_err(|_| ())?;
// app service and middleware
let service = endpoint_fut.await?;
// populate app data container from (async) data factories. // populate app data container from (async) data factories.
async_data_factories.iter().for_each(|factory| { async_data_factories.iter().for_each(|factory| {