一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

[Rust]actix-web中的中間件說明

 godxiasad 2024-12-13 發(fā)布于北京

太長(zhǎng),反著大家也不看,點(diǎn)個(gè)贊意思下就行

actix_web的中間件middleware 

actix_web::middleware 在 Actix Web 框架中扮演著重要的角色,它允許開發(fā)者在處理 HTTP 請(qǐng)求和響應(yīng)的過程中插入自定義的邏輯。中間件可以在請(qǐng)求到達(dá)處理函數(shù)之前或響應(yīng)返回給客戶端之前執(zhí)行,從而實(shí)現(xiàn)日志記錄、身份驗(yàn)證、數(shù)據(jù)驗(yàn)證、錯(cuò)誤處理等功能。

為什么要有中間件?

代碼在處理邏輯的時(shí)候,通常只處理正常的業(yè)務(wù)邏輯,而在處理過程中,可能會(huì)遇到一些特殊的情況,比如:404錯(cuò)誤,這種錯(cuò)誤會(huì)讓客戶端的請(qǐng)求無法進(jìn)入到代碼功能中,這時(shí)候就需要中間件來處理這些特殊的情況。

另外,還可以起到Java里面的過濾器功能,在Java里面,過濾器可以在請(qǐng)求到達(dá)處理函數(shù)之前或響應(yīng)返回給客戶端之前執(zhí)行,從而實(shí)現(xiàn)對(duì)請(qǐng)求和響應(yīng)的預(yù)處理和后處理。

在Rust開發(fā)中,特別是在使用Actix Web框架時(shí),中間件(middleware)是一種非常有用的設(shè)計(jì)模式,它允許開發(fā)者在處理HTTP請(qǐng)求和響應(yīng)的過程中插入自定義的邏輯。以下是使用中間件的幾個(gè)主要原因:

  • 代碼模塊化和可重用性:中間件可以將通用的功能(如日志記錄、身份驗(yàn)證、數(shù)據(jù)驗(yàn)證等)封裝起來,使得這些功能可以被多個(gè)處理函數(shù)共享和重用。這樣可以減少代碼重復(fù),提高代碼的可讀性和可維護(hù)性。

  • 靈活性和可擴(kuò)展性:通過使用中間件,開發(fā)者可以靈活地組合和配置不同的功能,以滿足應(yīng)用程序的特定需求。例如,可以根據(jù)不同的路由或用戶角色應(yīng)用不同的中間件。

  • 請(qǐng)求和響應(yīng)的預(yù)處理和后處理:中間件可以在請(qǐng)求到達(dá)處理函數(shù)之前或響應(yīng)返回給客戶端之前執(zhí)行,從而實(shí)現(xiàn)對(duì)請(qǐng)求和響應(yīng)的預(yù)處理和后處理。例如,可以在請(qǐng)求到達(dá)之前記錄日志、驗(yàn)證用戶身份,或者在響應(yīng)返回之前壓縮數(shù)據(jù)、添加額外的頭部信息等。

  • 提高開發(fā)效率:使用中間件可以減少開發(fā)者編寫重復(fù)代碼的工作量,使得開發(fā)者可以更專注于業(yè)務(wù)邏輯的實(shí)現(xiàn)。同時(shí),中間件的模塊化設(shè)計(jì)也使得代碼的測(cè)試和調(diào)試更加容易。

  • 支持異步編程模型:Actix Web是一個(gè)基于異步IO的Web框架,中間件可以很好地適應(yīng)這種編程模型,允許開發(fā)者在不阻塞主線程的情況下執(zhí)行耗時(shí)的操作。

以下是一些常見的中間件及其作用:

  • 日志記錄:記錄每個(gè)請(qǐng)求的詳細(xì)信息,如請(qǐng)求方法、路徑、時(shí)間戳等,有助于調(diào)試和監(jiān)控。

  • 身份驗(yàn)證:驗(yàn)證用戶的身份,確保只有經(jīng)過授權(quán)的用戶才能訪問特定的資源或執(zhí)行特定的操作。

  • 數(shù)據(jù)驗(yàn)證:在請(qǐng)求到達(dá)處理函數(shù)之前,驗(yàn)證請(qǐng)求數(shù)據(jù)的格式和內(nèi)容是否符合預(yù)期,防止無效或惡意數(shù)據(jù)進(jìn)入系統(tǒng)。

  • 錯(cuò)誤處理:統(tǒng)一處理應(yīng)用程序中的錯(cuò)誤,提供友好的錯(cuò)誤信息給客戶端,同時(shí)記錄錯(cuò)誤日志以便后續(xù)分析。

  • 性能監(jiān)控:測(cè)量每個(gè)請(qǐng)求的處理時(shí)間,幫助開發(fā)者識(shí)別性能瓶頸并進(jìn)行優(yōu)化。

  • 跨域資源共享(CORS):處理跨域請(qǐng)求,允許或拒絕來自不同域的請(qǐng)求,確保安全的跨域數(shù)據(jù)交互。

  • 使用中間件可以使代碼更加模塊化和可重用,開發(fā)者可以根據(jù)需要組合和配置不同的中間件,以滿足應(yīng)用程序的特定需求。在 Actix Web 中,中間件通常通過 App::wrap 方法進(jìn)行注冊(cè)和應(yīng)用。

自定義中間件的過程:

利用閉包實(shí)現(xiàn)簡(jiǎn)單的中間件

例如我要定義一個(gè)預(yù)先獲取請(qǐng)求中header里面的token,如果沒有這個(gè)token則直接就返回錯(cuò)誤。代碼可以這樣寫:

use actix_web::dev::Service;
use actix_web::error;
use actix_web::{middleware::{self, Logger}, web, App, HttpServer};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(move || {
App::new()
.wrap(middleware::Logger::default())
.wrap_fn(|req, srv| {
let header = req.headers().to_owned();
let fut = srv.call(req);
async move {
let res = fut.await;
match header.get("token"){
Some(token) => {
println!("token:{}", token.to_str().unwrap());
},
None => {
println!("token is None");
return Err(error::ErrorUnauthorized("Unauthorized"));
},
}
res
}
})
.route("/", web::get().to(|| async { "Hello, World!" }))
})
.bind("127.0.0.1:8080")?
.run()
.await
}

這里的核心,是使用wrap_fn + 一個(gè)閉包來實(shí)現(xiàn)中間件。

在示例代碼中,wrap_fn 被用來創(chuàng)建一個(gè)中間件,這個(gè)中間件檢查請(qǐng)求頭中是否包含 token。如果沒有 token,它會(huì)返回一個(gè)未經(jīng)授權(quán)的錯(cuò)誤。如果有 token,它會(huì)調(diào)用下一個(gè)服務(wù)并返回其結(jié)果。

warp_fn的源碼如下(看不懂也沒關(guān)系,不用懂,我寫這里是為了你以后能看懂時(shí)候回來查資料用的)

pub fn wrap_fn<F, Fut>(f: F) -> impl Transform<ServiceRequest, Response = ServiceResponse<Fut::Item>, Error = Fut::Error, InitError = ()>
where
F: Fn(ServiceRequest, &mut dyn Service<ServiceRequest, Response = ServiceResponse<Fut::Item>, Error = Fut::Error, Future = Fut>) -> Fut,
Fut: Future<Output = Result<ServiceResponse<Fut::Item>, Fut::Error>> + 'static,

里面參數(shù)解釋如下:

  • 輸入?yún)?shù):

    • F: 這是一個(gè)函數(shù)類型,它接受兩個(gè)參數(shù):ServiceRequest 和一個(gè)可變引用 &mut dyn Service<...>。這個(gè)函數(shù)將返回一個(gè) Fut 類型的 Future。

    • Fut: 這是一個(gè) Future 類型,它的輸出是 Result<ServiceResponseFut::Item, Fut::Error>。Fut::Item 是響應(yīng)體的類型,F(xiàn)ut::Error 是錯(cuò)誤類型。

  • 返回值:

    • impl Transform<ServiceRequest, Response = ServiceResponseFut::Item, Error = Fut::Error, InitError = ()>: 這是一個(gè)實(shí)現(xiàn)了 Transform trait 的類型,它可以轉(zhuǎn)換 ServiceRequest 到 ServiceResponseFut::Item,并且可以處理 Fut::Error 類型的錯(cuò)誤。

執(zhí)行結(jié)果如下:

如果是一些建議的邏輯,我們用wrap_fn + 閉包就可以了,但是如果是一些復(fù)雜的邏輯,就需要自己實(shí)現(xiàn)Transform trait了。

重新實(shí)現(xiàn)Transform trait 來實(shí)現(xiàn)自定義的中間件

  1. 中間件初始化:在這個(gè)階段,中間件工廠函數(shù)被調(diào)用,它接收鏈中的下一個(gè)服務(wù)作為參數(shù)。這允許中間件在實(shí)際處理請(qǐng)求之前進(jìn)行任何必要的設(shè)置或配置。

  • 中間件工廠類是 Transform trait的。

    • S - 后續(xù)服務(wù)的類型:S 代表鏈中下一個(gè)服務(wù)的類型,即當(dāng)前中間件將請(qǐng)求傳遞給哪個(gè)服務(wù)。

    • B - 響應(yīng)體的類型:B 代表響應(yīng)體(response)的類型,指定了從服務(wù)返回的響應(yīng)內(nèi)容的格式。

//中間的工廠類
pub struct Auth;


impl<S, B> Transform<S, ServiceRequest> for Auth
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse<B>;
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 }))
}
}

  1. 編寫中間件的具體實(shí)現(xiàn),核心是中間件的 call 方法調(diào)用:一旦中間件初始化完成,每當(dāng)有新的請(qǐng)求到來時(shí),中間件的 call 方法就會(huì)被調(diào)用,并接收這個(gè)普通請(qǐng)求作為參數(shù)。此時(shí),中間件可以對(duì)請(qǐng)求進(jìn)行處理


// 中間件的具體實(shí)現(xiàn),里面需要接受工廠類里面過來的service
pub struct AuthMiddleware<S> {
service: S,
}

//具體實(shí)現(xiàn)
//核心是兩個(gè)方法:
// call 具體實(shí)現(xiàn)
// poll_ready
impl<S, B> Service<ServiceRequest> for AuthMiddleware<S>
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse<B>;
type Error = Error;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

// 實(shí)現(xiàn) poll_ready 方法,用于檢查服務(wù)是否準(zhǔn)備好處理請(qǐng)求
//這里用的是forward_ready!宏
forward_ready!(service);

// 實(shí)現(xiàn) call 方法,用于處理實(shí)際的請(qǐng)求
fn call(&self, req: ServiceRequest) -> Self::Future {
// 進(jìn)行鑒權(quán)操作,判斷是否有權(quán)限
if has_permission(&req) {
// 有權(quán)限,繼續(xù)執(zhí)行后續(xù)中間件
let fut = self.service.call(req);
Box::pin(async move {
let res = fut.await?;
Ok(res)
})
} else {
// 沒有權(quán)限,立即返回響應(yīng)
Box::pin(async move {
// 鑒權(quán)失敗,返回未授權(quán)的響應(yīng),停止后續(xù)中間件的調(diào)用
Err(error::ErrorUnauthorized("Unauthorized"))
})
}
}
}

fn has_permission(req: &ServiceRequest) -> bool {
// 實(shí)現(xiàn)你的鑒權(quán)邏輯,根據(jù)需求判斷是否有權(quán)限
// 返回 true 表示有權(quán)限,返回 false 表示沒有權(quán)限
// unimplemented!()
let value = HeaderValue::from_str("").unwrap();
match req.path().to_ascii_lowercase().as_str(){
"/login" => true,
_ => {
let token = req.headers().get("token").unwrap_or(&value);
if token.len() <=0{
false
}else{
println!("驗(yàn)證一下token,看看是否合法");
true
}
}
}
}

核心方法說明:

  • poll_ready: 這個(gè)方法用于檢查服務(wù)是否準(zhǔn)備好處理請(qǐng)求。它返回一個(gè)Poll類型的結(jié)果,表示服務(wù)是否就緒。如果服務(wù)已經(jīng)就緒,返回Poll::Ready(Ok(()))。如果服務(wù)尚未就緒,返回Poll::Pending,表示需要等待一段時(shí)間后再次檢查。在middleware中,poll_ready方法通常用于確保在處理請(qǐng)求之前,所有依賴的資源或服務(wù)都已經(jīng)準(zhǔn)備就緒。

  • call: 這個(gè)方法用于處理實(shí)際的請(qǐng)求。它接收一個(gè)ServiceRequest類型的參數(shù),并返回一個(gè)ServiceResponse類型的結(jié)果。在middleware中,call方法通常用于對(duì)請(qǐng)求進(jìn)行預(yù)處理或后處理,例如添加日志記錄、驗(yàn)證請(qǐng)求、修改響應(yīng)等。call方法的返回值是一個(gè)Future,表示異步處理的結(jié)果。

所以也可以自己實(shí)現(xiàn)poll_ready

fn poll_ready(&self, ctx: &mut core::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
if self.service.poll_ready(ctx).is_pending() {
// 如果服務(wù)尚未準(zhǔn)備好,返回Pending
return std::task::Poll::Pending;
}

// 如果服務(wù)已準(zhǔn)備好,返回Ready(Ok(()))
std::task::Poll::Ready(Ok(()))
}

使用中間件

#[actix_web::main]
async fn main() -> std::io::Result<()> {
//中間件的順序是從下到上的,最后注冊(cè)的中間件會(huì)最先執(zhí)行
HttpServer::new(move || {
App::new()
.wrap(middleware::Logger::default())
.wrap(Auth::Auth)
// 注冊(cè)其他路由和處理函數(shù)
.route("/", web::get().to(|| async { "Hello, World!" }))
.route("/login", web::get().to(|| async { "Hello, login" }))
})
.bind("127.0.0.1:8080")?
.run()
.await
}

執(zhí)行結(jié)果如下:

    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    国产成人综合亚洲欧美日韩| 精品国产亚洲av久一区二区三区| 高潮少妇高潮久久精品99| 婷婷伊人综合中文字幕| 91人妻丝袜一区二区三区| 四季精品人妻av一区二区三区| 亚洲中文字幕人妻系列| 欧美一级内射一色桃子| 日本深夜福利视频在线| 都市激情小说在线一区二区三区| 日本午夜免费福利视频 | 91麻豆精品欧美一区| 国产日韩欧美一区二区| 麻豆看片麻豆免费视频| 美女黄色三级深夜福利| 91福利免费一区二区三区| 亚洲av成人一区二区三区在线| 成人免费在线视频大香蕉| 国产免费一区二区三区av大片| 成人精品亚洲欧美日韩| 少妇淫真视频一区二区| 午夜精品国产精品久久久| 精品al亚洲麻豆一区| 国产又粗又长又大的视频| 中字幕一区二区三区久久蜜桃| 不卡一区二区在线视频| 国产精品亚洲欧美一区麻豆| 在线日本不卡一区二区| 国产传媒一区二区三区| 成年男女午夜久久久精品| 丰满少妇高潮一区二区| 精品人妻一区二区四区| 91午夜少妇极品福利| 国内午夜精品视频在线观看| 欧美老太太性生活大片| 日韩成人午夜福利免费视频| 欧美加勒比一区二区三区| 中文久久乱码一区二区| 国产乱久久亚洲国产精品| 欧美视频在线观看一区| 亚洲国产精品国自产拍社区|