Skip to content

Commit be28a0b

Browse files
authored
feat: add from_fn middleware (#3447)
1 parent a431b73 commit be28a0b

File tree

4 files changed

+521
-17
lines changed

4 files changed

+521
-17
lines changed

actix-web/CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Added
66

7+
- Add `middleware::from_fn()` helper.
78
- Add `web::ThinData` extractor.
89

910
## 4.8.0
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
//! Shows a couple of ways to use the `from_fn` middleware.
2+
3+
use std::{collections::HashMap, io, rc::Rc, time::Duration};
4+
5+
use actix_web::{
6+
body::MessageBody,
7+
dev::{Service, ServiceRequest, ServiceResponse, Transform},
8+
http::header::{self, HeaderValue, Range},
9+
middleware::{from_fn, Logger, Next},
10+
web::{self, Header, Query},
11+
App, Error, HttpResponse, HttpServer,
12+
};
13+
14+
async fn noop<B>(req: ServiceRequest, next: Next<B>) -> Result<ServiceResponse<B>, Error> {
15+
next.call(req).await
16+
}
17+
18+
async fn print_range_header<B>(
19+
range_header: Option<Header<Range>>,
20+
req: ServiceRequest,
21+
next: Next<B>,
22+
) -> Result<ServiceResponse<B>, Error> {
23+
if let Some(Header(range)) = range_header {
24+
println!("Range: {range}");
25+
} else {
26+
println!("No Range header");
27+
}
28+
29+
next.call(req).await
30+
}
31+
32+
async fn mutate_body_type(
33+
req: ServiceRequest,
34+
next: Next<impl MessageBody + 'static>,
35+
) -> Result<ServiceResponse<impl MessageBody>, Error> {
36+
let res = next.call(req).await?;
37+
Ok(res.map_into_left_body::<()>())
38+
}
39+
40+
async fn mutate_body_type_with_extractors(
41+
string_body: String,
42+
query: Query<HashMap<String, String>>,
43+
req: ServiceRequest,
44+
next: Next<impl MessageBody + 'static>,
45+
) -> Result<ServiceResponse<impl MessageBody>, Error> {
46+
println!("body is: {string_body}");
47+
println!("query string: {query:?}");
48+
49+
let res = next.call(req).await?;
50+
51+
Ok(res.map_body(move |_, _| string_body))
52+
}
53+
54+
async fn timeout_10secs(
55+
req: ServiceRequest,
56+
next: Next<impl MessageBody + 'static>,
57+
) -> Result<ServiceResponse<impl MessageBody>, Error> {
58+
match tokio::time::timeout(Duration::from_secs(10), next.call(req)).await {
59+
Ok(res) => res,
60+
Err(_err) => Err(actix_web::error::ErrorRequestTimeout("")),
61+
}
62+
}
63+
64+
struct MyMw(bool);
65+
66+
impl MyMw {
67+
async fn mw_cb(
68+
&self,
69+
req: ServiceRequest,
70+
next: Next<impl MessageBody + 'static>,
71+
) -> Result<ServiceResponse<impl MessageBody>, Error> {
72+
let mut res = match self.0 {
73+
true => req.into_response("short-circuited").map_into_right_body(),
74+
false => next.call(req).await?.map_into_left_body(),
75+
};
76+
77+
res.headers_mut()
78+
.insert(header::WARNING, HeaderValue::from_static("42"));
79+
80+
Ok(res)
81+
}
82+
83+
pub fn into_middleware<S, B>(
84+
self,
85+
) -> impl Transform<
86+
S,
87+
ServiceRequest,
88+
Response = ServiceResponse<impl MessageBody>,
89+
Error = Error,
90+
InitError = (),
91+
>
92+
where
93+
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
94+
B: MessageBody + 'static,
95+
{
96+
let this = Rc::new(self);
97+
from_fn(move |req, next| {
98+
let this = Rc::clone(&this);
99+
async move { Self::mw_cb(&this, req, next).await }
100+
})
101+
}
102+
}
103+
104+
#[actix_web::main]
105+
async fn main() -> io::Result<()> {
106+
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
107+
108+
let bind = ("127.0.0.1", 8080);
109+
log::info!("staring server at http://{}:{}", &bind.0, &bind.1);
110+
111+
HttpServer::new(|| {
112+
App::new()
113+
.wrap(from_fn(noop))
114+
.wrap(from_fn(print_range_header))
115+
.wrap(from_fn(mutate_body_type))
116+
.wrap(from_fn(mutate_body_type_with_extractors))
117+
.wrap(from_fn(timeout_10secs))
118+
// switch bool to true to observe early response
119+
.wrap(MyMw(false).into_middleware())
120+
.wrap(Logger::default())
121+
.default_service(web::to(HttpResponse::Ok))
122+
})
123+
.workers(1)
124+
.bind(bind)?
125+
.run()
126+
.await
127+
}

0 commit comments

Comments
 (0)