1
0
mirror of https://github.com/actix/examples synced 2024-11-27 16:02:57 +01:00

Return HttpResponse from middleware.

This commit is contained in:
zonblade 2023-02-13 14:14:11 +07:00
parent 2df944c5e5
commit 123cd8fa95
4 changed files with 178 additions and 0 deletions

View File

@ -0,0 +1,12 @@
[package]
name = "middleware-return-httpresponse"
version = "1.0.0"
edition = "2021"
[dependencies]
actix-web = "4"
env_logger = "0.9"
futures-util = { version = "0.3.17", default-features = false, features = ["std"] }
log = "0.4"
pin-project = "1"
serde = { version = "*", features = ["derive"] }

View File

@ -0,0 +1,35 @@
## Middleware : Return HttpResponse from Middleware
```rs
cd middleware-return-httpresponse
cargo run
# Started http server: 127.0.0.1:8080
```
## What is this?
A Middleware example which returning HttpResponse.
## How to test
### success case
```sh
curl http://127.0.0.1:8080/ -H 'Authorization:ok' | json_pp -json_opt pretty,canonical
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 42 100 42 0 0 42000 0 --:--:-- --:--:-- --:--:-- 42000
{
"data" : "Hello this is success response!"
}
```
### failed case
```sh
curl http://127.0.0.1:8080/ | json_pp -json_opt pretty,canonical
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 102 100 102 0 0 99k 0 --:--:-- --:--:-- --:--:-- 99k
{
"data" : "Hello this is default error message! you need to set Authorization header to get thru this."
}
```

View File

@ -0,0 +1,40 @@
use actix_web::{web, App, HttpServer, HttpResponse};
mod simple;
// You can move this struct to a separate file.
// this struct below just for example.
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct HttpData {
pub data: String,
}
// this implementation is optional
impl Default for HttpData {
fn default() -> Self {
Self {
data: "Hello this is success response!".to_string(),
}
}
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
log::info!("starting HTTP server at http://localhost:8080");
HttpServer::new(|| {
App::new()
.wrap(simple::ReturnHttpResponse)
.service(
web::resource("/").to(|| async {
HttpResponse::Ok().json(HttpData::default())
}),
)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}

View File

@ -0,0 +1,91 @@
use std::{
future::{ready, Ready},
rc::Rc
};
use actix_web::{
dev::{self, Service, ServiceRequest, ServiceResponse, Transform},
Error,
http::{header, StatusCode}, HttpResponseBuilder
};
use futures_util::future::LocalBoxFuture;
// You can move this struct to a separate file.
// this struct below just for example.
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct HttpData {
pub data: String,
}
// this implementation is optional
impl Default for HttpData {
fn default() -> Self {
Self {
data: "Hello this is default error message! you need to set Authorization header to get thru this.".to_string(),
}
}
}
pub struct ReturnHttpResponse;
impl<S: 'static> Transform<S, ServiceRequest> for ReturnHttpResponse
where
S: Service<ServiceRequest, Response = ServiceResponse, Error = Error>,
S::Future: 'static
{
type Response = ServiceResponse;
type Error = Error;
type InitError = ();
type Transform = AuthMiddleware<S>;
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(AuthMiddleware {
service: Rc::new(service),
}))
}
}
pub struct AuthMiddleware<S> {
// This is special: We need this to avoid lifetime issues.
service: Rc<S>,
}
impl<S> Service<ServiceRequest> for AuthMiddleware<S>
where
S: Service<ServiceRequest, Response = ServiceResponse, Error = Error> + 'static,
S::Future: 'static
{
type Response = ServiceResponse;
type Error = Error;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
dev::forward_ready!(service);
fn call(&self, req: ServiceRequest) -> Self::Future {
let svc = self.service.clone();
Box::pin(async move {
let headers = req.headers();
let _ = match headers.get("Authorization") {
Some(e) => e,
None => {
let new_response = HttpResponseBuilder::new(StatusCode::BAD_REQUEST)
.insert_header((header::CONTENT_TYPE, "application/json"))
.json(HttpData::default());
return Ok(ServiceResponse::new(
req.request().to_owned(), /* or req.request().clone() */
new_response
))
}
};
let res = svc.call(req).await?;
Ok(res)
})
}
}