2025-2-6-后端FAQ

2025-2-6-后端FAQ

FAQ[常见问题]

Middleware

使用Rust开发 Actix 需要使用中间件

中间件处理有两个步骤。 1.中间件初始化,中间件工厂被调用 链中的下一个服务作为参数。 中间件的调用方法通过正常请求被调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
use std::future::{ready, Ready};

use actix_web::{
dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
Error,
};
use futures_util::future::LocalBoxFuture;

// 导入库

pub struct SayHi;

// SayHi 将作为一个中间件处理 ServiceRequest 类型的请求

impl<S, B> Transform<S, ServiceRequest> for SayHi

// S - 下一个服务的类型

// B - 响应体的类型

// 对 SayHi 结构体实现了 Transform trait

// Transform 是 actix-web 中的一个 trait,用来定义如何转换和处理请求

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 = SayHiMiddleware<S>;
type Future = Ready<Result<Self::Transform, Self::InitError>>;

fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(SayHiMiddleware { service }))
}
}

这里出现了一些比较高级的技巧

middleware factory 中间件工厂

首先是

1
2
3
4
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,

这个 where 字句实现了类型约束:

S 必须是一个实现了 Service trait 的类型,且该服务处理的是 ServiceRequest 类型的请求,返回的是 ServiceResponse<B> 类型的响应,错误类型是 Error

这是一个生命周期约束,表明 S::Future 的生命周期是 'static,即返回的 Future 不会持有任何短生命周期的引用。

这是一个生命周期约束,表明响应体的类型 B 必须是 'static,即 B 的生命周期是静态的,不依赖于任何局部变量或临时引用。

下面代码继续,定义了类型关联。(太高级了)

1
2
3
4
5
type Response = ServiceResponse<B>;
type Error = Error;
type InitError = ();
type Transform = SayHiMiddleware<S>;
type Future = Ready<Result<Self::Transform, Self::InitError>>;

以上内容

Response 类型指定了中间件工厂将会返回的响应类型

  • ServiceResponse<B>。它表示的是中间件最终生成的响应

Error 类型指定了服务中可能出现的错误类型:通用类型

InitError 类型表示初始化中间件时可能发生的错误类型

  • 这里使用 () 作为初始化错误类型,表示初始化时没有错误

Transform 类型指定了中间件的类型

  • SayHiMiddleware<S>。即这个中间件会处理请求,类型参数 S 表示它会接收一个服务,并且是 ServiceRequest 类型的请求和 ServiceResponse<B> 类型的响应。

Future 类型表示中间件工厂的异步执行结果

  • Ready,意味着这个返回值是一个立即完成的 Future,它包装了一个 Result<Self::Transform, Self::InitError>。这表示中间件的创建过程是同步的,不会进行复杂的异步操作。

new_transform 方法是 Transform trait 的一部分,用于创建一个新的中间件实例

1
2
3
fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(SayHiMiddleware { service }))
}

new_transform 方法是 Transform trait 的一部分,用于创建一个新的中间件实例。

参数解析:S 类型的 service 参数,这个 service 是下游的服务,即处理请求的实际服务。

返回类型解析:该方法返回一个 Self::Future 类型的 Future,即一个 Ready 类型的 Future,它表示异步操作完成并立即返回

ready(Ok(SayHiMiddleware { service }))

  • ready 是一个将结果包装成已完成的 Future 的辅助函数。在这里,我们返回了一个 SayHiMiddleware<S> 中间件,它包含了下游服务 service,并且包装在一个 Ok 结果中,表示没有错误。
1
2
3
pub struct SayHiMiddleware<S> {
service: S,
}

SayHiMiddleware<S> 是一个结构体,接收 S 类型的参数。(S 为下游服务类型)

1
impl<S, B> Service<ServiceRequest> for SayHiMiddleware<S>

这是 SayHiMiddleware<S>Service<ServiceRequest> trait 的实现。

定义了如何将请求传递给下游服务。具体来说,Service 是一个 trait,它定义了如何处理 ServiceRequest,并返回一个 ServiceResponse<B>

1
2
3
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(cx)
}

这是 Service<ServiceRequest> trait 中的方法

作用是让中间件知道下游服务是否可以处理请求,用来检查服务是否准备好接收请求。

方法会调用下游服务 self.service.poll_ready(cx),这表示如果下游服务准备好了(也就是说可以开始处理请求)

那么 SayHiMiddleware 也可以开始处理请求。

第二个方法

1
2
3
4
fn call(&mut self, req: ServiceRequest) -> Self::Future {
println!("Hi! Handling a request!");
self.service.call(req)
}

call 是处理请求的核心方法,它会将一个 ServiceRequest 请求交给中间件来处理

req 是传入的请求,它是一个 ServiceRequest 类型的值,表示需要处理的 HTTP 请求

println!("Hi! Handling a request!") 这一行会在每次请求被中间件处理时输出 “Hi! Handling a request!”,表示中间件正在处理请求。这个可以作为日志记录,帮助你跟踪请求的流转。

最后,self.service.call(req) 调用下游服务的 call 方法,把请求传递给下游服务进行处理。下游服务通常会返回一个 ServiceResponse 类型的响应

1
2
3
4
5
6
7
8
9
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>>;

1
2
3
4
5
6
7
8
9
10
11
12
13
fn call(&self, req: ServiceRequest) -> Self::Future {
println!("Hi from start. You requested: {}", req.path());

let fut = self.service.call(req);

Box::pin(async move {
let res = fut.await?;

println!("Hi from response");
Ok(res)
})
}


简易的中间件

适用于小型任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
async fn my_middleware(
req: ServiceRequest,
// req 是一个 ServiceRequest 类型的参数,表示传入的 HTTP 请求
// ServiceRequest 包含请求的详细信息,像是头部、路径、查询参数等。
next: Next<impl MessageBody>,
// next 是一个函数,表示下一个中间件或请求处理器。Next 类型包含一个闭包,
// 调用这个闭包可以传递请求到下一个中间件或最终的请求处理器

) -> Result<ServiceResponse<impl MessageBody>, Error> {
// 这是函数的返回类型。它返回一个 Result,表示异步调用可能会成功或者失败

// 写 预处理代码


next.call(req).await // 异步调用
// 即将请求传递到下一个中间件或最终的处理函数。如果这是最后一个中间件,最终的请求处理函数(例如控制器)将会执行。


// post-processing

// 会等待请求的处理结果,即调用下一个中间件或者最终的请求处理器,并等待它返回一个响应。
}