当前位置: 首页 > news >正文

你的中间件一团糟-是时候修复它了-️

GitHub 主页

中间件(Middleware)。这是 Web 开发中最强大的概念之一,也是最容易被滥用的概念之一。理论上,这是一个美妙的想法:一个由可复用组件构成的管道,可以检查、转换或终止请求。但在实践中,在我多年来使用过的许多框架中,它变成了一团乱麻,函数调用函数,控制流难以追踪,错误处理简直是一场噩梦。而罪魁祸首,往往就是那个 next() 回调。

我花了无数个小时在 Express.js 中调试中间件链。模式总是一样的。你有一个接收 reqresnext 的函数。你做一些事情,然后你必须记住调用 next()。如果你忘了,请求就会挂起。如果你给它传递一个参数,它就会触发错误处理链,那又是一个完全不同的管道。如果你有一个异步操作,你必须确保在正确的地方调用 next(),这常常导致复杂的 Promise 链或 async/await 块,即便如此也仍然感觉笨拙。

让我们来看一个简单而又真实的 Express 中间件设置。我们想要一个日志记录器,一个身份验证检查,然后是实际的路由处理器。看起来很简单,但实现细节暴露了其中的裂痕。

// 1. 日志中间件
function logger(req, res, next) {console.log(`[${new Date().toISOString()}] ${req.method} ${req.path}`);next(); // 继续下一个
}// 2. 身份验证中间件
async function authenticate(req, res, next) {const apiKey = req.headers['x-api-key'];if (!apiKey) {// 在这里停止请求return res.status(401).send('Unauthorized');}// 想象一个异步的数据库调用来验证密钥const user = await db.findUserByApiKey(apiKey);if (user) {req.user = user; // 将用户附加到请求对象上next(); // 用户有效,继续} else {// 在这里停止请求res.status(401).send('Unauthorized');}
}// 3. 路由处理器
function getUserProfile(req, res) {// user 对象被神奇地附加到了 `req` 上res.json(req.user);
}// 4. 组装管道
app.use(logger);
app.get('/profile', authenticate, getUserProfile);

这段代码有太多隐式的规则。app.useapp.get 的顺序很重要。authenticate 函数可以通过发送响应来停止链条,也可以通过调用 next() 来继续。getUserProfile 函数只是假设 authenticate 已经成功运行,并向 req 附加了一个 user 对象。这就是我所说的“魔法”。这是一种“远距离作用”。当你看着 getUserProfile 时,你没有任何关于它之前运行了哪些中间件的明确信息。你只能凭空“知道”。这使得代码难以推理和重构。

这就是我所说的那团乱麻。它是一个仅由约定和一个可变请求对象链接起来的函数链。hyperlane 看待这个问题,并提供了一个根本不同的解决方案。它完全摆脱了 next() 回调。取而代之的是,它使用了一个钩子(hooks)和声明式宏的系统,这些钩子和宏直接附加到服务器或特定路由上。流程是明确的,逻辑与它所影响的代码是同地协作的。

hyperlane 中,针对请求生命周期的不同阶段,有不同类型的中间件和钩子。你有在路由处理器之前运行的 request_middleware,以及在之后运行的 response_middleware。你有用于连接建立时或发生 panic 时的钩子。它们不仅仅是一个单一、无定形的链条;它们是用于特定工作的特定工具。

让我们看看我们将如何用 hyperlane 实现同样的日志记录和身份验证逻辑。

use hyperlane::*
use hyperlane_macros::*// 1. 日志中间件
// 它只注册一次,并为每个请求运行。
#[request_middleware(order = 1)] // 明确的顺序
#[response_header(STEP => "request_middleware_logger")]
async fn logger_middleware(ctx: Context) {println!("接收到路径 {} 的请求", ctx.get_request_path().await);
}// 2. 身份验证中间件
// 它也为每个请求运行,但在日志记录器之后。
#[request_middleware(order = 2)]
#[response_header(STEP => "request_middleware_auth")]
#[request_header("X-API-KEY" => api_key_option)]
async fn auth_middleware(ctx: Context) {// 使用宏来提取请求头更清晰if let Some(api_key) = api_key_option {// 在真实应用中,我们会验证这个密钥。// 我们可以将数据附加到上下文中供以后使用。ctx.set_attribute("user_id", "user-123").await;} else {// 我们可以直接修改响应并停止处理。ctx.set_response_status_code(401).await;ctx.set_response_body("Unauthorized").await;// 通过不调用任何进一步的逻辑,我们实际上在这里结束了请求。// 框架会处理发送响应。ctx.stop_processing().await;}
}// 3. 路由处理器
#[get]
#[route("/profile")]
#[send] // 在结束时自动发送响应
#[attribute("user_id" => user_id_option: &str)]
async fn get_user_profile(ctx: Context) {// 我们可以明确地获取我们之前设置的属性。// 这是类型安全的,而不是“魔法”。if let Some(user_id) = user_id_option {let response = format!("{{\"user\":\"{}\"}}", user_id);ctx.set_response_body(response).await;} else {// 如果 auth_middleware 工作正常,这个分支不应该被触及,// 但我们可以防御性地处理它。ctx.set_response_status_code(500).await;ctx.set_response_body("Internal Server Error: User ID not found in context.").await;}
}#[hyperlane(server: Server)]
#[tokio::main]
async fn main() {// 服务器自动发现并注册中间件和路由。let server_hook: ServerHook = server.run().await.unwrap_or_default();server_hook.wait().await;
}

这是一种范式转变。中间件函数是独立的组件,由 #[request_middleware] 属性标识。它们的执行顺序由 order 参数明确定义,消除了任何歧义。auth_middleware 不需要 next() 回调;它有一个 Context 对象,可以用它来为下游处理器附加数据,或者停止处理并直接发送响应。

get_user_profile 函数也更加明确。它使用 #[attribute(...)] 宏来声明它期望上下文中存在一个 user_id。这是一个清晰的、编译时检查的依赖,而不是一个被神奇地附加到请求对象上的属性。它是自文档化的,而且安全得多。

这种基于钩子和声明式的方法提供了一种在基于 next() 的系统中根本不存在的清晰度和控制力。你可以看到一个请求的整个生命周期都展现在属性中。你可以推理操作的顺序。你可以编写更专注、更可复用、更易于测试的中间件。

多年来,我一直认为中间件必然会有点乱。这是我们为其强大功能付出的代价。hyperlane 证明我错了。它告诉我,你可以拥有一个强大、灵活的中间件系统,而无需牺牲清晰性、安全性或开发者的理智。你只需要愿意放开 next()

GitHub 主页

http://www.jsqmd.com/news/19080/

相关文章:

  • 自增自减,幂运算,逻辑运算符、短路运算,位运算,字符串连接符,三元运算符复习
  • 20232309 2025-2026-1 《网络与系统攻防技术》实验二实验报告
  • 2025年轻触开关/检测开关厂家推荐排行榜,轻触按键开关,防水轻触开关,贴片轻触开关,检测开关源头厂家精选指南
  • 2025年卫衣厂家推荐排行榜,男女款卫衣,春秋季卫衣,加绒卫衣,印花卫衣源头厂家精选推荐
  • 2025年护栏厂家推荐排行榜:市政护栏,道路护栏,桥梁护栏,小区护栏,锌钢护栏,阳台护栏公司精选
  • CentOS下安装部署Docker
  • 2025年铁氟龙高温线厂家权威推荐榜:极细铁氟龙/UL10064/UL1332/UL1867铁氟龙线材专业选购指南
  • 2025年发电机组厂家权威推荐榜:柴油发电机、静音发电机组源头企业综合实力与能效表现深度解析
  • 999
  • 2025年二手发电机厂家推荐排行榜,二手发电机回收,二手发电机买卖,二手发电机买卖回收公司专业推荐
  • 2025年角接触轴承厂家权威推荐榜单:高精度/高承载/高精密/机床主轴/汽车专用/定制/可替代进口/高转速/高刚性轴承全方位解析
  • 极大极小搜索
  • 2025年粘度计厂家权威推荐榜:旋转粘度计、落球粘度计、在线粘度计、便携式粘度计专业选购指南
  • 法语NER模型在可再生能源领域的应用
  • mochi-mqtt/server 实现一个mqtt bridge 功能
  • 2025年立式TYPE-C母座厂家推荐排行榜,TYPE-C接口,USB-C母座,立式贴片TYPE-C连接器,防水TYPE-C母座公司精选
  • CF2152F Triple Attack
  • 2025年定型机厂家权威推荐榜:拉幅定型机/门富士/节能/余热回收/废气回收/烟气回收/智能排风/双层定型机源头企业综合解析
  • 2025年真空钎焊炉厂家权威推荐榜单:工业级真空热处理设备,真空扩散焊炉,高温钎焊设备专业制造商深度解析
  • 2025年10月办公家具公司推荐:对比评测五强榜,聚焦恺 威家具品质标杆
  • 2025年防腐木凉亭厂家电话推荐:江西纳美工艺家俱有限公司实地探厂记
  • 2025年沈阳酒店电话推荐:北站西塔丽柏宠物友好市中心步行地铁口。
  • 2025年发电机厂家推荐排行榜,发电机组出租,柴油发电机出租,甲醇发电机组租赁,移动式发电机出租,维修保养服务公司推荐
  • 2025年沈阳酒店电话推荐:北站西塔丽柏宠物友好市中心步行地铁口
  • 表获取
  • 2025年陶瓷过滤板厂家权威推荐榜:白刚玉/棕刚玉/扇形/真空陶瓷过滤板,陶瓷滤膜,陶瓷过滤机配件及滤板专业选购指南
  • 2025年拖鞋机厂家权威推荐榜:酒店拖鞋生产线、全自动拖鞋机、一次性拖鞋机、酒店一次性拖鞋机器专业选购指南
  • 打卡测试
  • 2025年棒球帽源头厂家权威推荐榜:专业定制与潮流设计,运动棒球帽、时尚棒球帽、防晒棒球帽、品牌棒球帽公司精选!
  • 2025年栏杆护栏厂家权威推荐榜:不锈钢栏杆、桥梁防撞护栏、河道景观护栏最新选购指南与实力解析