From 5bd5651faab2b3b46b317124384290d33bff62b2 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sat, 13 Apr 2019 22:25:00 -0700 Subject: [PATCH] Allow to use any service as default service --- CHANGES.md | 2 ++ Cargo.toml | 1 + examples/basic.rs | 6 +++--- src/app.rs | 55 +++++++++++++++++++++++++++++++++++++++++------ src/resource.rs | 21 ++++++++++++------ src/scope.rs | 28 ++++++++++++++---------- src/service.rs | 4 ++-- 7 files changed, 87 insertions(+), 30 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 45ff6b38..eaceb97e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,8 @@ ### Changed +* Allow to use any service as default service. + * Remove generic type for request payload, always use default. * Removed `Decompress` middleware. Bytes, String, Json, Form extractors diff --git a/Cargo.toml b/Cargo.toml index 442914f0..1ce5c1dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -100,6 +100,7 @@ rustls = { version = "^0.15", optional = true } [dev-dependencies] actix-http = { version = "0.1.0-alpha.5", features=["ssl", "brotli", "flate2-zlib"] } actix-http-test = { version = "0.1.0-alpha.3", features=["ssl"] } +actix-files = { version = "0.1.0-alpha.4" } rand = "0.6" env_logger = "0.6" serde_derive = "1.0" diff --git a/examples/basic.rs b/examples/basic.rs index 91119657..46440d70 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -36,9 +36,9 @@ fn main() -> std::io::Result<()> { .wrap( middleware::DefaultHeaders::new().header("X-Version-R2", "0.3"), ) - .default_resource(|r| { - r.route(web::route().to(|| HttpResponse::MethodNotAllowed())) - }) + .default_service( + web::route().to(|| HttpResponse::MethodNotAllowed()), + ) .route(web::get().to_async(index_async)), ) .service(web::resource("/test1.html").to(|| "Test\r\n")) diff --git a/src/app.rs b/src/app.rs index 39c96cd9..6c34123d 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,4 +1,5 @@ use std::cell::RefCell; +use std::fmt; use std::marker::PhantomData; use std::rc::Rc; @@ -207,20 +208,56 @@ where self } - /// Default resource to be used if no matching resource could be found. - pub fn default_resource(mut self, f: F) -> Self + /// Default service to be used if no matching resource could be found. + /// + /// It is possible to use services like `Resource`, `Route`. + /// + /// ```rust + /// use actix_web::{web, App, HttpResponse}; + /// + /// fn index() -> &'static str { + /// "Welcome!" + /// } + /// + /// fn main() { + /// let app = App::new() + /// .service( + /// web::resource("/index.html").route(web::get().to(index))) + /// .default_service( + /// web::route().to(|| HttpResponse::NotFound())); + /// } + /// ``` + /// + /// It is also possible to use static files as default service. + /// + /// ```rust + /// use actix_files::Files; + /// use actix_web::{web, App, HttpResponse}; + /// + /// fn main() { + /// let app = App::new() + /// .service( + /// web::resource("/index.html").to(|| HttpResponse::Ok())) + /// .default_service( + /// Files::new("", "./static") + /// ); + /// } + /// ``` + pub fn default_service(mut self, f: F) -> Self where - F: FnOnce(Resource) -> Resource, + F: IntoNewService, U: NewService< Request = ServiceRequest, Response = ServiceResponse, Error = Error, - InitError = (), > + 'static, + U::InitError: fmt::Debug, { // create and configure default resource self.default = Some(Rc::new(boxed::new_service( - f(Resource::new("")).into_new_service().map_init_err(|_| ()), + f.into_new_service().map_init_err(|e| { + log::error!("Can not construct default service: {:?}", e) + }), ))); self @@ -420,10 +457,14 @@ mod tests { .service(web::resource("/test").to(|| HttpResponse::Ok())) .service( web::resource("/test2") - .default_resource(|r| r.to(|| HttpResponse::Created())) + .default_service(|r: ServiceRequest| { + r.into_response(HttpResponse::Created()) + }) .route(web::get().to(|| HttpResponse::Ok())), ) - .default_resource(|r| r.to(|| HttpResponse::MethodNotAllowed())), + .default_service(|r: ServiceRequest| { + r.into_response(HttpResponse::MethodNotAllowed()) + }), ); let req = TestRequest::with_uri("/blah").to_request(); diff --git a/src/resource.rs b/src/resource.rs index f0dea981..a8268302 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -1,4 +1,5 @@ use std::cell::RefCell; +use std::fmt; use std::rc::Rc; use actix_http::{Error, Response}; @@ -313,22 +314,24 @@ where self.wrap(mw) } - /// Default resource to be used if no matching route could be found. + /// Default service to be used if no matching route could be found. /// By default *405* response get returned. Resource does not use /// default handler from `App` or `Scope`. - pub fn default_resource(mut self, f: F) -> Self + pub fn default_service(mut self, f: F) -> Self where - F: FnOnce(Resource) -> R, - R: IntoNewService, + F: IntoNewService, U: NewService< Request = ServiceRequest, Response = ServiceResponse, Error = Error, > + 'static, + U::InitError: fmt::Debug, { // create and configure default resource self.default = Rc::new(RefCell::new(Some(Rc::new(boxed::new_service( - f(Resource::new("")).into_new_service().map_init_err(|_| ()), + f.into_new_service().map_init_err(|e| { + log::error!("Can not construct default service: {:?}", e) + }), ))))); self @@ -626,7 +629,9 @@ mod tests { .service( web::resource("/test").route(web::get().to(|| HttpResponse::Ok())), ) - .default_resource(|r| r.to(|| HttpResponse::BadRequest())), + .default_service(|r: ServiceRequest| { + r.into_response(HttpResponse::BadRequest()) + }), ); let req = TestRequest::with_uri("/test").to_request(); let resp = call_success(&mut srv, req); @@ -642,7 +647,9 @@ mod tests { App::new().service( web::resource("/test") .route(web::get().to(|| HttpResponse::Ok())) - .default_resource(|r| r.to(|| HttpResponse::BadRequest())), + .default_service(|r: ServiceRequest| { + r.into_response(HttpResponse::BadRequest()) + }), ), ); diff --git a/src/scope.rs b/src/scope.rs index 62badc86..5678158e 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -1,4 +1,5 @@ use std::cell::RefCell; +use std::fmt; use std::rc::Rc; use actix_http::Response; @@ -180,22 +181,24 @@ where ) } - /// Default resource to be used if no matching route could be found. + /// Default service to be used if no matching route could be found. /// /// If default resource is not registered, app's default resource is being used. - pub fn default_resource(mut self, f: F) -> Self + pub fn default_service(mut self, f: F) -> Self where - F: FnOnce(Resource) -> Resource, + F: IntoNewService, U: NewService< Request = ServiceRequest, Response = ServiceResponse, Error = Error, - InitError = (), > + 'static, + U::InitError: fmt::Debug, { // create and configure default resource self.default = Rc::new(RefCell::new(Some(Rc::new(boxed::new_service( - f(Resource::new("")).into_new_service().map_init_err(|_| ()), + f.into_new_service().map_init_err(|e| { + log::error!("Can not construct default service: {:?}", e) + }), ))))); self @@ -843,7 +846,9 @@ mod tests { App::new().service( web::scope("/app") .service(web::resource("/path1").to(|| HttpResponse::Ok())) - .default_resource(|r| r.to(|| HttpResponse::BadRequest())), + .default_service(|r: ServiceRequest| { + r.into_response(HttpResponse::BadRequest()) + }), ), ); @@ -860,12 +865,13 @@ mod tests { fn test_default_resource_propagation() { let mut srv = init_service( App::new() - .service( - web::scope("/app1") - .default_resource(|r| r.to(|| HttpResponse::BadRequest())), - ) + .service(web::scope("/app1").default_service( + web::resource("").to(|| HttpResponse::BadRequest()), + )) .service(web::scope("/app2")) - .default_resource(|r| r.to(|| HttpResponse::MethodNotAllowed())), + .default_service(|r: ServiceRequest| { + r.into_response(HttpResponse::MethodNotAllowed()) + }), ); let req = TestRequest::with_uri("/non-exist").to_request(); diff --git a/src/service.rs b/src/service.rs index 2817cc0b..e5b0896e 100644 --- a/src/service.rs +++ b/src/service.rs @@ -63,8 +63,8 @@ impl ServiceRequest { /// Create service response #[inline] - pub fn into_response(self, res: Response) -> ServiceResponse { - ServiceResponse::new(self.req, res) + pub fn into_response>>(self, res: R) -> ServiceResponse { + ServiceResponse::new(self.req, res.into()) } /// Create service response for error