mirror of
https://github.com/actix/actix-web.git
synced 2024-11-22 09:31:10 +00:00
add Route::wrap
(#2725)
* add `Route::wrap` * add tests * fix clippy * fix doctests
This commit is contained in:
parent
8abcb94512
commit
45592b37b6
4 changed files with 108 additions and 34 deletions
|
@ -119,6 +119,7 @@ mod _original {
|
|||
let mut headers: [HeaderIndex; MAX_HEADERS] =
|
||||
unsafe { MaybeUninit::uninit().assume_init() };
|
||||
|
||||
#[allow(invalid_value)]
|
||||
let mut parsed: [httparse::Header<'_>; MAX_HEADERS] =
|
||||
unsafe { MaybeUninit::uninit().assume_init() };
|
||||
|
||||
|
|
|
@ -2,12 +2,14 @@
|
|||
|
||||
## Unreleased - 2021-xx-xx
|
||||
### Added
|
||||
- Add `ServiceRequest::extract` to make it easier to use extractors when writing middlewares. [#2647]
|
||||
- Add `ServiceRequest::extract()` to make it easier to use extractors when writing middlewares. [#2647]
|
||||
- Add `Route::wrap()` to allow individual routes to use middleware. [#2725]
|
||||
|
||||
### Fixed
|
||||
- Clear connection-level data on `HttpRequest` drop. [#2742]
|
||||
|
||||
[#2647]: https://github.com/actix/actix-web/pull/2647
|
||||
[#2725]: https://github.com/actix/actix-web/pull/2725
|
||||
[#2742]: https://github.com/actix/actix-web/pull/2742
|
||||
|
||||
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
use std::{mem, rc::Rc};
|
||||
|
||||
use actix_http::Method;
|
||||
use actix_http::{body::MessageBody, Method};
|
||||
use actix_service::{
|
||||
apply,
|
||||
boxed::{self, BoxService},
|
||||
fn_service, Service, ServiceFactory, ServiceFactoryExt,
|
||||
fn_service, Service, ServiceFactory, ServiceFactoryExt, Transform,
|
||||
};
|
||||
use futures_core::future::LocalBoxFuture;
|
||||
|
||||
use crate::{
|
||||
guard::{self, Guard},
|
||||
handler::{handler_service, Handler},
|
||||
middleware::Compat,
|
||||
service::{BoxedHttpServiceFactory, ServiceRequest, ServiceResponse},
|
||||
Error, FromRequest, HttpResponse, Responder,
|
||||
};
|
||||
|
@ -35,6 +37,31 @@ impl Route {
|
|||
}
|
||||
}
|
||||
|
||||
/// Registers a route middleware.
|
||||
///
|
||||
/// `mw` is a middleware component (type), that can modify the requests and responses handled by
|
||||
/// this `Route`.
|
||||
///
|
||||
/// See [`App::wrap`](crate::App::wrap) for more details.
|
||||
#[doc(alias = "middleware")]
|
||||
#[doc(alias = "use")] // nodejs terminology
|
||||
pub fn wrap<M, B>(self, mw: M) -> Route
|
||||
where
|
||||
M: Transform<
|
||||
BoxService<ServiceRequest, ServiceResponse, Error>,
|
||||
ServiceRequest,
|
||||
Response = ServiceResponse<B>,
|
||||
Error = Error,
|
||||
InitError = (),
|
||||
> + 'static,
|
||||
B: MessageBody + 'static,
|
||||
{
|
||||
Route {
|
||||
service: boxed::factory(apply(Compat::new(mw), self.service)),
|
||||
guards: self.guards,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn take_guards(&mut self) -> Vec<Box<dyn Guard>> {
|
||||
mem::take(Rc::get_mut(&mut self.guards).unwrap())
|
||||
}
|
||||
|
@ -246,11 +273,15 @@ mod tests {
|
|||
use futures_core::future::LocalBoxFuture;
|
||||
use serde::Serialize;
|
||||
|
||||
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};
|
||||
use crate::{
|
||||
dev::{always_ready, fn_factory, fn_service, Service},
|
||||
error,
|
||||
http::{header, Method, StatusCode},
|
||||
middleware::{DefaultHeaders, Logger},
|
||||
service::{ServiceRequest, ServiceResponse},
|
||||
test::{call_service, init_service, read_body, TestRequest},
|
||||
web, App, HttpResponse,
|
||||
};
|
||||
|
||||
#[derive(Serialize, PartialEq, Debug)]
|
||||
struct MyObject {
|
||||
|
@ -323,6 +354,44 @@ mod tests {
|
|||
assert_eq!(body, Bytes::from_static(b"{\"name\":\"test\"}"));
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn route_middleware() {
|
||||
let srv = init_service(
|
||||
App::new()
|
||||
.route("/", web::get().to(HttpResponse::Ok).wrap(Logger::default()))
|
||||
.service(
|
||||
web::resource("/test")
|
||||
.route(web::get().to(HttpResponse::Ok))
|
||||
.route(
|
||||
web::post()
|
||||
.to(HttpResponse::Created)
|
||||
.wrap(DefaultHeaders::new().add(("x-test", "x-posted"))),
|
||||
)
|
||||
.route(
|
||||
web::delete()
|
||||
.to(HttpResponse::Accepted)
|
||||
// logger changes body type, proving Compat is not needed
|
||||
.wrap(Logger::default()),
|
||||
),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
let req = TestRequest::get().uri("/test").to_request();
|
||||
let res = call_service(&srv, req).await;
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
assert!(!res.headers().contains_key("x-test"));
|
||||
|
||||
let req = TestRequest::post().uri("/test").to_request();
|
||||
let res = call_service(&srv, req).await;
|
||||
assert_eq!(res.status(), StatusCode::CREATED);
|
||||
assert_eq!(res.headers().get("x-test").unwrap(), "x-posted");
|
||||
|
||||
let req = TestRequest::delete().uri("/test").to_request();
|
||||
let res = call_service(&srv, req).await;
|
||||
assert_eq!(res.status(), StatusCode::ACCEPTED);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service_handler() {
|
||||
struct HelloWorld;
|
||||
|
|
|
@ -799,34 +799,36 @@ async fn test_server_cookies() {
|
|||
let res = req.send().await.unwrap();
|
||||
assert!(res.status().is_success());
|
||||
|
||||
let first_cookie = Cookie::build("first", "first_value")
|
||||
.http_only(true)
|
||||
.finish();
|
||||
let second_cookie = Cookie::new("second", "first_value");
|
||||
{
|
||||
let first_cookie = Cookie::build("first", "first_value")
|
||||
.http_only(true)
|
||||
.finish();
|
||||
let second_cookie = Cookie::new("second", "first_value");
|
||||
|
||||
let cookies = res.cookies().expect("To have cookies");
|
||||
assert_eq!(cookies.len(), 3);
|
||||
if cookies[0] == first_cookie {
|
||||
assert_eq!(cookies[1], second_cookie);
|
||||
} else {
|
||||
assert_eq!(cookies[0], second_cookie);
|
||||
assert_eq!(cookies[1], first_cookie);
|
||||
}
|
||||
let cookies = res.cookies().expect("To have cookies");
|
||||
assert_eq!(cookies.len(), 3);
|
||||
if cookies[0] == first_cookie {
|
||||
assert_eq!(cookies[1], second_cookie);
|
||||
} else {
|
||||
assert_eq!(cookies[0], second_cookie);
|
||||
assert_eq!(cookies[1], first_cookie);
|
||||
}
|
||||
|
||||
let first_cookie = first_cookie.to_string();
|
||||
let second_cookie = second_cookie.to_string();
|
||||
// Check that we have exactly two instances of raw cookie headers
|
||||
let cookies = res
|
||||
.headers()
|
||||
.get_all(http::header::SET_COOKIE)
|
||||
.map(|header| header.to_str().expect("To str").to_string())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(cookies.len(), 3);
|
||||
if cookies[0] == first_cookie {
|
||||
assert_eq!(cookies[1], second_cookie);
|
||||
} else {
|
||||
assert_eq!(cookies[0], second_cookie);
|
||||
assert_eq!(cookies[1], first_cookie);
|
||||
let first_cookie = first_cookie.to_string();
|
||||
let second_cookie = second_cookie.to_string();
|
||||
// Check that we have exactly two instances of raw cookie headers
|
||||
let cookies = res
|
||||
.headers()
|
||||
.get_all(http::header::SET_COOKIE)
|
||||
.map(|header| header.to_str().expect("To str").to_string())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(cookies.len(), 3);
|
||||
if cookies[0] == first_cookie {
|
||||
assert_eq!(cookies[1], second_cookie);
|
||||
} else {
|
||||
assert_eq!(cookies[0], second_cookie);
|
||||
assert_eq!(cookies[1], first_cookie);
|
||||
}
|
||||
}
|
||||
|
||||
srv.stop().await;
|
||||
|
|
Loading…
Reference in a new issue