中间件系统[20260718145758]

0 阅读1分钟

中间件系统

项目代码:github.com/hyperlane-d…

简介

中间件是 Hyperlane 最强大的功能之一。它允许你在请求生命周期的各个阶段拦截和处理请求与响应。无论你需要身份验证、日志记录、跨域支持还是自定义请求转换,Hyperlane 的中间件系统都提供了一种干净灵活的方式来实现这些横切关注点。

理解中间件架构

Hyperlane 的中间件系统围绕 ServerHook trait 构建。中间件是任何实现了 ServerHook 的类型,它提供了两个方法:

  1. new():在新连接建立时调用。用于初始化。
  2. handle():对连接上的每个请求调用。这是中间件逻辑的核心所在。

handle() 方法返回一个 Status 枚举,用于控制请求流:

  • Status::Continue:请求继续传递到下一个中间件或路由处理器。
  • Status::Reject:请求被拒绝,连接可能被关闭。

请求中间件

请求中间件在路由处理器处理请求之前运行。它非常适合用于身份验证、请求验证和设置响应默认值等任务。

以下是一个设置默认响应头部的请求中间件:

struct RequestMiddleware;

impl ServerHook for RequestMiddleware {
    async fn new(stream: &mut Stream, _: &mut Context) -> Self {
        Self
    }

    async fn handle(self, stream: &mut Stream, ctx: &mut Context) -> Status {
        ctx.get_mut_response()
            .set_version(HttpVersion::Http1_1)
            .set_status_code(200);
        Status::Continue
    }
}

server.request_middleware::<RequestMiddleware>();

在这个示例中,中间件:

  1. 在连接建立时创建一个新实例(new()
  2. 为每个请求设置 HTTP 版本为 1.1 和状态码为 200(handle()
  3. 返回 Status::Continue 让请求继续处理

响应中间件

响应中间件在路由处理器处理完请求之后运行。它非常适合用于发送响应、添加响应头部和日志记录等任务。

以下是一个发送已构建响应的响应中间件:

struct ResponseMiddleware;

impl ServerHook for ResponseMiddleware {
    async fn new(_: &mut Stream, _: &mut Context) -> Self {
        Self
    }

    async fn handle(self, stream: &mut Stream, ctx: &mut Context) -> Status {
        let data: Vec<u8> = ctx.get_mut_response().build();
        if stream.try_send(data).await.is_err() {
            stream.set_closed(true);
            return Status::Reject;
        }
        Status::Continue
    }
}

server.response_middleware::<ResponseMiddleware>();

这个中间件:

  1. 使用 ctx.get_mut_response().build() 将响应构建为字节
  2. 通过流发送响应数据
  3. 如果发送失败,标记流为已关闭并拒绝请求

执行顺序

中间件的注册顺序很重要。请求中间件按注册顺序运行,响应中间件按相反顺序运行。你可以使用属性宏语法控制执行顺序:

#[request_middleware(1)]
struct RequestMiddleware1;

#[request_middleware(2)]
struct RequestMiddleware2;

#[response_middleware]
struct ResponseMiddleware;

#[request_middleware(N)] 中的数字参数指定执行优先级。数字越小越先运行。在这个示例中:

  1. RequestMiddleware1(优先级 1)首先运行
  2. RequestMiddleware2(优先级 2)其次运行
  3. 路由处理器处理请求
  4. ResponseMiddleware 最后运行

实用中间件示例

身份验证中间件

最常见的中间件用例之一是身份验证。以下是一个检查 Authorization 头部的示例:

impl ServerHook for AuthMiddleware {
    async fn handle(self, stream: &mut Stream, ctx: &mut Context) -> Status {
        let auth_str: String = ctx
            .get_request()
            .try_get_header_back(AUTHORIZATION)
            .unwrap_or_default();

        if auth_str.is_empty() {
            let data: Vec<u8> = ctx
                .get_mut_response()
                .set_status_code(401)
                .set_body("Unauthorized")
                .build();
            if stream.try_send(data).await.is_err() {
                stream.set_closed(true);
            }
            return Status::Reject;
        }

        Status::Continue
    }
}

这个中间件:

  1. 从请求中提取 Authorization 头部
  2. 如果头部缺失或为空,发送 401 Unauthorized 响应
  3. 返回 Status::Reject 停止后续处理
  4. 如果头部存在,返回 Status::Continue 继续处理

跨域中间件

跨域资源共享(CORS)对于服务于不同来源客户端的 API 至关重要:

ctx.get_mut_response()
    .set_header(ACCESS_CONTROL_ALLOW_ORIGIN, WILDCARD_ANY)
    .set_header(ACCESS_CONTROL_ALLOW_METHODS, ALL_METHODS)
    .set_header(ACCESS_CONTROL_ALLOW_HEADERS, WILDCARD_ANY);

这设置了标准的 CORS 头部:

  • Access-Control-Allow-Origin: * — 允许来自任何来源的请求
  • Access-Control-Allow-Methods: * — 允许所有 HTTP 方法
  • Access-Control-Allow-Headers: * — 允许所有请求头部

静态资源中间件

对于提供静态文件服务,可以创建一个从磁盘读取文件的中间件:

let file_path: String = format!("{static_path}{path}");
let file_extension: String = FileExtension::get_extension_name(&file_path);
let content_type: &'static str = FileExtension::parse(&file_extension).get_content_type();
let file_data: String = tokio::fs::read_to_string(&file_path).await.unwrap();
ctx.get_mut_response().set_body(file_data);

这个中间件:

  1. 从静态目录和请求路径构建文件路径
  2. 确定文件扩展名
  3. 将扩展名映射到内容类型
  4. 读取文件内容并将其设置为响应体

超时中间件

超时中间件确保长时间运行的请求不会阻塞服务器:

spawn(async move {
    timeout(
        Duration::from_millis(100),
        async move {
            new_ctx.get_mut_response()
                .set_status_code(504)
                .set_body("timeout");
        }
    ).await.unwrap();
});

这会生成一个等待 100 毫秒的任务,然后设置 504 Gateway Timeout 响应。

错误处理中间件

Hyperlane 提供了专门用于处理错误和恐慌的中间件:

请求错误处理器

struct RequestErrorHook;

impl ServerHook for RequestErrorHook {
    async fn new(_: &mut Stream, ctx: &mut Context) -> Self {
        let request_error: RequestError = ctx
            .try_get_request_error_data()
            .unwrap_or_default();
        Self
    }

    async fn handle(self, stream: &mut Stream, ctx: &mut Context) -> Status {
        let data: Vec<u8> = ctx
            .get_mut_response()
            .set_status_code(500)
            .set_body("error")
            .build();
        stream.try_send(data).await;
        Status::Continue
    }
}

server.request_error::<RequestErrorHook>();

任务恐慌处理器

struct TaskPanicHook;

impl ServerHook for TaskPanicHook {
    async fn new(_: &mut Stream, ctx: &mut Context) -> Self {
        let error: PanicData = ctx
            .try_get_task_panic_data()
            .unwrap_or_default();
        Self
    }

    async fn handle(self, stream: &mut Stream, ctx: &mut Context) -> Status {
        let data: Vec<u8> = ctx
            .get_mut_response()
            .set_status_code(500)
            .set_body("panic")
            .build();
        stream.try_send(data).await;
        Status::Continue
    }
}

server.task_panic::<TaskPanicHook>();

这些钩子在请求处理期间捕获错误和恐慌,并返回适当的错误响应,而不是让服务器崩溃。

属性宏中间件注册

除了编程式 API,你还可以使用属性宏注册中间件:

#[request_middleware(1)]
struct RequestMiddleware1;

#[request_middleware(2)]
struct RequestMiddleware2;

#[response_middleware]
struct ResponseMiddleware;

这种声明式方法更清晰、更易维护,特别是当你有很多中间件组件时。宏会自动处理注册。

中间件最佳实践

  1. 保持中间件聚焦:每个中间件应该处理一个关注点。不要试图在单个中间件中做所有事情。

  2. 顺序很重要:按正确的顺序注册中间件。身份验证应该在授权之前,授权应该在日志记录之前。

  3. 优雅地处理错误:始终处理发送响应失败的情况,检查 stream.try_send() 的结果。

  4. 明智使用 Status::Reject:只有在已经发送了适当的错误响应时才拒绝请求。

  5. 利用 new() 方法:在 new() 方法中进行连接级初始化,而不是在 handle() 中为每个请求都做。

总结

Hyperlane 的中间件系统提供了一种强大而灵活的方式来实现 Web 应用中的横切关注点。ServerHook trait 让你完全控制请求和响应处理,而属性宏系统则提供了一种更声明式的方法。通过组合请求中间件、响应中间件和错误处理器,你可以构建健壮且可维护的 Web 服务。

在下一篇文章中,我们将探讨 Hyperlane 的路由系统,了解传入请求如何匹配到对应的处理器。


项目代码:github.com/hyperlane-d…