diff --git a/CHANGES.md b/CHANGES.md
index 31fa4690..876e1c03 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -4,7 +4,8 @@
 ### Added
 * Add `ServiceRequest::parts_mut`. [#2177]
 * Add extractors for `Uri` and `Method`. [#2263]
-* Add extractor for `ConnectionInfo` and `PeerAddr`. [#2263]
+* Add extractors for `ConnectionInfo` and `PeerAddr`. [#2263]
+* Add `Route::service` for using hand-written services as handlers. [#2262]
 
 ### Changed
 * Change compression algorithm features flags. [#2250]
@@ -13,6 +14,7 @@
 [#2177]: https://github.com/actix/actix-web/pull/2177
 [#2250]: https://github.com/actix/actix-web/pull/2250
 [#2271]: https://github.com/actix/actix-web/pull/2271
+[#2262]: https://github.com/actix/actix-web/pull/2262
 [#2263]: https://github.com/actix/actix-web/pull/2263
 
 
diff --git a/Cargo.toml b/Cargo.toml
index 320751c6..779b5225 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -113,7 +113,6 @@ flate2 = "1.0.13"
 zstd = "0.7"
 rand = "0.8"
 rcgen = "0.8"
-serde_derive = "1.0"
 tls-openssl = { package = "openssl", version = "0.10.9" }
 tls-rustls = { package = "rustls", version = "0.19.0" }
 
diff --git a/src/extract.rs b/src/extract.rs
index d7b67cd9..592f7ab8 100644
--- a/src/extract.rs
+++ b/src/extract.rs
@@ -54,7 +54,7 @@ pub trait FromRequest: Sized {
 /// use actix_web::{web, dev, App, Error, HttpRequest, FromRequest};
 /// use actix_web::error::ErrorBadRequest;
 /// use futures_util::future::{ok, err, Ready};
-/// use serde_derive::Deserialize;
+/// use serde::Deserialize;
 /// use rand;
 ///
 /// #[derive(Debug, Deserialize)]
@@ -145,7 +145,7 @@ where
 /// use actix_web::{web, dev, App, Result, Error, HttpRequest, FromRequest};
 /// use actix_web::error::ErrorBadRequest;
 /// use futures_util::future::{ok, err, Ready};
-/// use serde_derive::Deserialize;
+/// use serde::Deserialize;
 /// use rand;
 ///
 /// #[derive(Debug, Deserialize)]
@@ -265,7 +265,7 @@ impl FromRequest for Method {
 #[doc(hidden)]
 impl FromRequest for () {
     type Error = Infallible;
-    type Future = Ready<Result<(), Infallible>>;
+    type Future = Ready<Result<Self, Self::Error>>;
     type Config = ();
 
     fn from_request(_: &HttpRequest, _: &mut Payload) -> Self::Future {
@@ -376,7 +376,7 @@ mod m {
 mod tests {
     use actix_http::http::header;
     use bytes::Bytes;
-    use serde_derive::Deserialize;
+    use serde::Deserialize;
 
     use super::*;
     use crate::test::TestRequest;
diff --git a/src/lib.rs b/src/lib.rs
index 9d8bf62e..920abccb 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -149,7 +149,9 @@ pub mod dev {
     pub use actix_http::{Extensions, Payload, PayloadStream, RequestHead, ResponseHead};
     pub use actix_router::{Path, ResourceDef, ResourcePath, Url};
     pub use actix_server::Server;
-    pub use actix_service::{always_ready, forward_ready, Service, Transform};
+    pub use actix_service::{
+        always_ready, fn_factory, fn_service, forward_ready, Service, Transform,
+    };
 
     pub(crate) fn insert_slash(mut patterns: Vec<String>) -> Vec<String> {
         for path in &mut patterns {
diff --git a/src/request.rs b/src/request.rs
index bff66f08..5c5c43d2 100644
--- a/src/request.rs
+++ b/src/request.rs
@@ -347,7 +347,7 @@ impl Drop for HttpRequest {
 /// # Examples
 /// ```
 /// use actix_web::{web, App, HttpRequest};
-/// use serde_derive::Deserialize;
+/// use serde::Deserialize;
 ///
 /// /// extract `Thing` from request
 /// async fn index(req: HttpRequest) -> String {
diff --git a/src/route.rs b/src/route.rs
index 44f7e30b..d85b940b 100644
--- a/src/route.rs
+++ b/src/route.rs
@@ -5,7 +5,7 @@ use std::{future::Future, rc::Rc};
 use actix_http::http::Method;
 use actix_service::{
     boxed::{self, BoxService, BoxServiceFactory},
-    Service, ServiceFactory,
+    Service, ServiceFactory, ServiceFactoryExt,
 };
 use futures_core::future::LocalBoxFuture;
 
@@ -128,9 +128,10 @@ impl Route {
 
     /// Set handler function, use request extractors for parameters.
     ///
+    /// # Examples
     /// ```
     /// use actix_web::{web, http, App};
-    /// use serde_derive::Deserialize;
+    /// use serde::Deserialize;
     ///
     /// #[derive(Deserialize)]
     /// struct Info {
@@ -154,7 +155,7 @@ impl Route {
     ///
     /// ```
     /// # use std::collections::HashMap;
-    /// # use serde_derive::Deserialize;
+    /// # use serde::Deserialize;
     /// use actix_web::{web, App};
     ///
     /// #[derive(Deserialize)]
@@ -184,6 +185,53 @@ impl Route {
         self.service = boxed::factory(HandlerService::new(handler));
         self
     }
+
+    /// Set raw service to be constructed and called as the request handler.
+    ///
+    /// # Examples
+    /// ```
+    /// # use std::convert::Infallible;
+    /// # use futures_util::future::LocalBoxFuture;
+    /// # use actix_web::{*, dev::*, http::header};
+    /// struct HelloWorld;
+    ///
+    /// impl Service<ServiceRequest> for HelloWorld {
+    ///     type Response = ServiceResponse;
+    ///     type Error = Infallible;
+    ///     type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
+    ///
+    ///     always_ready!();
+    ///
+    ///     fn call(&self, req: ServiceRequest) -> Self::Future {
+    ///         let (req, _) = req.into_parts();
+    ///
+    ///         let res = HttpResponse::Ok()
+    ///             .insert_header(header::ContentType::plaintext())
+    ///             .body("Hello world!");
+    ///
+    ///         Box::pin(async move { Ok(ServiceResponse::new(req, res)) })
+    ///     }
+    /// }
+    ///
+    /// App::new().route(
+    ///     "/",
+    ///     web::get().service(fn_factory(|| async { Ok(HelloWorld) })),
+    /// );
+    /// ```
+    pub fn service<S, E>(mut self, service_factory: S) -> Self
+    where
+        S: ServiceFactory<
+                ServiceRequest,
+                Response = ServiceResponse,
+                Error = E,
+                InitError = (),
+                Config = (),
+            > + 'static,
+        E: Into<Error> + 'static,
+    {
+        self.service = boxed::factory(service_factory.map_err(Into::into));
+        self
+    }
 }
 
 #[cfg(test)]
@@ -192,9 +240,12 @@ mod tests {
 
     use actix_rt::time::sleep;
     use bytes::Bytes;
-    use serde_derive::Serialize;
+    use futures_core::future::LocalBoxFuture;
+    use serde::Serialize;
 
-    use crate::http::{Method, StatusCode};
+    use crate::dev::{always_ready, fn_factory, fn_service, Service};
+    use crate::http::{header, Method, StatusCode};
+    use crate::service::{ServiceRequest, ServiceResponse};
     use crate::test::{call_service, init_service, read_body, TestRequest};
     use crate::{error, web, App, HttpResponse};
 
@@ -268,4 +319,65 @@ mod tests {
         let body = read_body(resp).await;
         assert_eq!(body, Bytes::from_static(b"{\"name\":\"test\"}"));
     }
+
+    #[actix_rt::test]
+    async fn test_service_handler() {
+        struct HelloWorld;
+
+        impl Service<ServiceRequest> for HelloWorld {
+            type Response = ServiceResponse;
+            type Error = crate::Error;
+            type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
+
+            always_ready!();
+
+            fn call(&self, req: ServiceRequest) -> Self::Future {
+                let (req, _) = req.into_parts();
+
+                let res = HttpResponse::Ok()
+                    .insert_header(header::ContentType::plaintext())
+                    .body("Hello world!");
+
+                Box::pin(async move { Ok(ServiceResponse::new(req, res)) })
+            }
+        }
+
+        let srv = init_service(
+            App::new()
+                .route(
+                    "/hello",
+                    web::get().service(fn_factory(|| async { Ok(HelloWorld) })),
+                )
+                .route(
+                    "/bye",
+                    web::get().service(fn_factory(|| async {
+                        Ok::<_, ()>(fn_service(|req: ServiceRequest| async {
+                            let (req, _) = req.into_parts();
+
+                            let res = HttpResponse::Ok()
+                                .insert_header(header::ContentType::plaintext())
+                                .body("Goodbye, and thanks for all the fish!");
+
+                            Ok::<_, Infallible>(ServiceResponse::new(req, res))
+                        }))
+                    })),
+                ),
+        )
+        .await;
+
+        let req = TestRequest::get().uri("/hello").to_request();
+        let resp = call_service(&srv, req).await;
+        assert_eq!(resp.status(), StatusCode::OK);
+        let body = read_body(resp).await;
+        assert_eq!(body, Bytes::from_static(b"Hello world!"));
+
+        let req = TestRequest::get().uri("/bye").to_request();
+        let resp = call_service(&srv, req).await;
+        assert_eq!(resp.status(), StatusCode::OK);
+        let body = read_body(resp).await;
+        assert_eq!(
+            body,
+            Bytes::from_static(b"Goodbye, and thanks for all the fish!")
+        );
+    }
 }