From 123cd8fa957a27d11f0d61a2e50cf8def081dd35 Mon Sep 17 00:00:00 2001 From: zonblade Date: Mon, 13 Feb 2023 14:14:11 +0700 Subject: [PATCH] Return HttpResponse from middleware. --- .../middleware-return-httpresponse/Cargo.toml | 12 +++ .../middleware-return-httpresponse/README.md | 35 +++++++ .../src/main.rs | 40 ++++++++ .../src/simple.rs | 91 +++++++++++++++++++ 4 files changed, 178 insertions(+) create mode 100644 middleware/middleware-return-httpresponse/Cargo.toml create mode 100644 middleware/middleware-return-httpresponse/README.md create mode 100644 middleware/middleware-return-httpresponse/src/main.rs create mode 100644 middleware/middleware-return-httpresponse/src/simple.rs diff --git a/middleware/middleware-return-httpresponse/Cargo.toml b/middleware/middleware-return-httpresponse/Cargo.toml new file mode 100644 index 0000000..bd88388 --- /dev/null +++ b/middleware/middleware-return-httpresponse/Cargo.toml @@ -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"] } diff --git a/middleware/middleware-return-httpresponse/README.md b/middleware/middleware-return-httpresponse/README.md new file mode 100644 index 0000000..0550cc0 --- /dev/null +++ b/middleware/middleware-return-httpresponse/README.md @@ -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." +} +``` \ No newline at end of file diff --git a/middleware/middleware-return-httpresponse/src/main.rs b/middleware/middleware-return-httpresponse/src/main.rs new file mode 100644 index 0000000..823e19f --- /dev/null +++ b/middleware/middleware-return-httpresponse/src/main.rs @@ -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 +} diff --git a/middleware/middleware-return-httpresponse/src/simple.rs b/middleware/middleware-return-httpresponse/src/simple.rs new file mode 100644 index 0000000..54dbfa8 --- /dev/null +++ b/middleware/middleware-return-httpresponse/src/simple.rs @@ -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 Transform for ReturnHttpResponse +where + S: Service, + S::Future: 'static +{ + type Response = ServiceResponse; + type Error = Error; + type InitError = (); + type Transform = AuthMiddleware; + type Future = Ready>; + + fn new_transform(&self, service: S) -> Self::Future { + ready(Ok(AuthMiddleware { + service: Rc::new(service), + })) + } +} + +pub struct AuthMiddleware { + // This is special: We need this to avoid lifetime issues. + service: Rc, +} + +impl Service for AuthMiddleware +where + S: Service + 'static, + S::Future: 'static +{ + type Response = ServiceResponse; + type Error = Error; + type Future = LocalBoxFuture<'static, Result>; + + 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) + }) + } +} \ No newline at end of file