Skip to content

Commit ac38d9d

Browse files
zonbladeJohnTitor
authored andcommitted
Return HttpResponse from middleware.
1 parent a507cf2 commit ac38d9d

4 files changed

Lines changed: 178 additions & 0 deletions

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "middleware-return-httpresponse"
3+
version = "1.0.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
actix-web = "4"
8+
env_logger = "0.9"
9+
futures-util = { version = "0.3.17", default-features = false, features = ["std"] }
10+
log = "0.4"
11+
pin-project = "1"
12+
serde = { version = "*", features = ["derive"] }
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
## Middleware : Return HttpResponse from Middleware
2+
3+
```rs
4+
cd middleware-return-httpresponse
5+
cargo run
6+
# Started http server: 127.0.0.1:8080
7+
```
8+
9+
## What is this?
10+
11+
A Middleware example which returning HttpResponse.
12+
13+
## How to test
14+
15+
### success case
16+
```sh
17+
curl http://127.0.0.1:8080/ -H 'Authorization:ok' | json_pp -json_opt pretty,canonical
18+
% Total % Received % Xferd Average Speed Time Time Time Current
19+
Dload Upload Total Spent Left Speed
20+
100 42 100 42 0 0 42000 0 --:--:-- --:--:-- --:--:-- 42000
21+
{
22+
"data" : "Hello this is success response!"
23+
}
24+
```
25+
26+
### failed case
27+
```sh
28+
curl http://127.0.0.1:8080/ | json_pp -json_opt pretty,canonical
29+
% Total % Received % Xferd Average Speed Time Time Time Current
30+
Dload Upload Total Spent Left Speed
31+
100 102 100 102 0 0 99k 0 --:--:-- --:--:-- --:--:-- 99k
32+
{
33+
"data" : "Hello this is default error message! you need to set Authorization header to get thru this."
34+
}
35+
```
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use actix_web::{web, App, HttpServer, HttpResponse};
2+
3+
mod simple;
4+
5+
// You can move this struct to a separate file.
6+
// this struct below just for example.
7+
use serde::{Deserialize, Serialize};
8+
9+
#[derive(Debug, Serialize, Deserialize)]
10+
pub struct HttpData {
11+
pub data: String,
12+
}
13+
// this implementation is optional
14+
impl Default for HttpData {
15+
fn default() -> Self {
16+
Self {
17+
data: "Hello this is success response!".to_string(),
18+
}
19+
}
20+
}
21+
22+
#[actix_web::main]
23+
async fn main() -> std::io::Result<()> {
24+
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
25+
26+
log::info!("starting HTTP server at http://localhost:8080");
27+
28+
HttpServer::new(|| {
29+
App::new()
30+
.wrap(simple::ReturnHttpResponse)
31+
.service(
32+
web::resource("/").to(|| async {
33+
HttpResponse::Ok().json(HttpData::default())
34+
}),
35+
)
36+
})
37+
.bind(("127.0.0.1", 8080))?
38+
.run()
39+
.await
40+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use std::{
2+
future::{ready, Ready},
3+
rc::Rc
4+
};
5+
6+
use actix_web::{
7+
dev::{self, Service, ServiceRequest, ServiceResponse, Transform},
8+
Error,
9+
http::{header, StatusCode}, HttpResponseBuilder
10+
};
11+
use futures_util::future::LocalBoxFuture;
12+
13+
14+
// You can move this struct to a separate file.
15+
// this struct below just for example.
16+
use serde::{Deserialize, Serialize};
17+
18+
#[derive(Debug, Serialize, Deserialize)]
19+
pub struct HttpData {
20+
pub data: String,
21+
}
22+
// this implementation is optional
23+
impl Default for HttpData {
24+
fn default() -> Self {
25+
Self {
26+
data: "Hello this is default error message! you need to set Authorization header to get thru this.".to_string(),
27+
}
28+
}
29+
}
30+
31+
32+
pub struct ReturnHttpResponse;
33+
34+
impl<S: 'static> Transform<S, ServiceRequest> for ReturnHttpResponse
35+
where
36+
S: Service<ServiceRequest, Response = ServiceResponse, Error = Error>,
37+
S::Future: 'static
38+
{
39+
type Response = ServiceResponse;
40+
type Error = Error;
41+
type InitError = ();
42+
type Transform = AuthMiddleware<S>;
43+
type Future = Ready<Result<Self::Transform, Self::InitError>>;
44+
45+
fn new_transform(&self, service: S) -> Self::Future {
46+
ready(Ok(AuthMiddleware {
47+
service: Rc::new(service),
48+
}))
49+
}
50+
}
51+
52+
pub struct AuthMiddleware<S> {
53+
// This is special: We need this to avoid lifetime issues.
54+
service: Rc<S>,
55+
}
56+
57+
impl<S> Service<ServiceRequest> for AuthMiddleware<S>
58+
where
59+
S: Service<ServiceRequest, Response = ServiceResponse, Error = Error> + 'static,
60+
S::Future: 'static
61+
{
62+
type Response = ServiceResponse;
63+
type Error = Error;
64+
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
65+
66+
dev::forward_ready!(service);
67+
68+
fn call(&self, req: ServiceRequest) -> Self::Future {
69+
let svc = self.service.clone();
70+
71+
Box::pin(async move {
72+
73+
let headers = req.headers();
74+
let _ = match headers.get("Authorization") {
75+
Some(e) => e,
76+
None => {
77+
let new_response = HttpResponseBuilder::new(StatusCode::BAD_REQUEST)
78+
.insert_header((header::CONTENT_TYPE, "application/json"))
79+
.json(HttpData::default());
80+
return Ok(ServiceResponse::new(
81+
req.request().to_owned(), /* or req.request().clone() */
82+
new_response
83+
))
84+
}
85+
};
86+
87+
let res = svc.call(req).await?;
88+
Ok(res)
89+
})
90+
}
91+
}

0 commit comments

Comments
 (0)