predicate 是返回布尔值的无副作用函数,用于中间件中安全、可组合、隔离的请求准入控制,支持可观测、可降级与环境可控的白名单校验链。

理解 Predicate 在中间件中的核心作用
Predicate(谓词)本质上是一个返回布尔值的函数,它不执行副作用,只做“判断”。在中间件开发中,它天然适合做请求准入控制——比如判断某个请求是否来自白名单域名、IP 或用户身份。关键在于:Predicate 本身不修改任何状态,也不触发业务逻辑,这为“安全隔离”提供了基础保障。
用 Predicate 构建可组合的白名单校验链
避免把所有白名单规则硬编码在一个 if 判断里。应将每条规则拆成独立、可测试的 Predicate:
- domainInWhitelist:解析 Host 头,比对预置域名列表(支持通配符如 *.example.com)
- ipInRange:将 X-Forwarded-For 首地址转为 IPv4/IPv6,检查是否落在 CIDR 段内
- headerTokenValid:验证自定义白名单 Token(非认证 Token,仅用于内部服务标识)
再用逻辑组合子(如 and()、or())按需拼装,例如:allowIf(and(domainInWhitelist, ipInRange)) —— 只有域名和 IP 同时合规才放行。
隔离全局环境:Predicate 必须无状态、无依赖
所谓“安全隔离”,首要防止 Predicate 意外读取或污染运行时上下文。做到这点的关键是:
- 所有白名单数据(如域名列表、CIDR 规则)在中间件初始化时加载为不可变结构(如
Set、ImmutableList),不从process.env或配置中心实时读取 - Predicate 函数参数仅限当前请求对象(如
req)或其明确提取的字段(req.hostname、req.ip),绝不访问global、require.cache或其他模块私有变量 - 禁止在 Predicate 内部调用异步操作(如数据库查询、HTTP 请求)——若需动态白名单,应提前同步拉取并缓存,Predicate 仍保持同步纯函数
实战建议:让 Predicate 可观测、可降级、可绕过
生产环境中,Predicate 不仅要正确,还要可控:
- 每条 Predicate 执行前后打结构化日志,记录匹配结果、耗时、触发的规则名,便于审计和问题回溯
- 提供运行时开关(如通过 Redis Flag 或本地原子布尔量),允许紧急关闭某条 Predicate 而不影响整个中间件流程
- 在开发/测试环境,支持通过请求头(如
X-Bypass-Whitelist: true)临时跳过校验——但该头必须在 Predicate 外层统一识别,且仅当环境变量NODE_ENV !== 'production'时生效











