1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-06-11 01:39:33 +00:00

Route::service (#2262)

Co-authored-by: Rob Ede <robjtede@icloud.com>
This commit is contained in:
Ibraheem Ahmed 2021-06-23 16:30:06 -04:00 committed by GitHub
parent ed0516d724
commit 083ee05d50
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 128 additions and 13 deletions

View file

@ -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

View file

@ -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" }

View file

@ -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;

View file

@ -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 {

View file

@ -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 {

View file

@ -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!")
);
}
}