不能直接对 await 表达式做 try-catch 置换错误码,而应将 await 所在异步逻辑封装进防腐调用器,在 catch 中依据错误特征动态映射统一业务错误码,并保留原始错误可追溯性。

在自定义统一防腐网关中,不能直接对 await 表达式本身做 try-catch 动态置换错误码——因为 await 不是函数调用,而是语法结构,它抛出的错误来自 Promise rejection。真正可操作的是 **包裹 await 所在的异步逻辑块**,并在 catch 中根据原始错误特征(如类型、消息、堆栈、HTTP 状态码等)动态映射为统一业务错误码。
1. 在网关层封装异步调用入口
防腐网关通常负责转发请求、校验、熔断、降级等。所有下游服务调用应走统一的“防腐调用器”,而非裸写 await fetch(...) 或 await axios(...):
- 将下游请求封装为一个返回 Promise 的函数(如
invokeUpstream(options)) - 该函数内部用 try-catch 包裹 await,并捕获 rejection
- 根据 error 实例属性(
error.response?.status、error.code、error.message.includes('timeout')等)匹配预设规则 - 构造标准化错误对象:
{ code: 'GATEWAY_TIMEOUT', message: '上游服务响应超时', traceId }
2. 建立动态错误码映射表
避免 if-else 堆砌,推荐用配置驱动方式管理错误映射逻辑:
- 定义规则数组:
const ERROR_MAPPING = [ { match: err => err.code === 'ECONNABORTED', code: 'GATEWAY_REQUEST_TIMEOUT' }, { match: err => err.response?.status === 503, code: 'UPSTREAM_UNAVAILABLE' } ] - 在 catch 中遍历规则,首个匹配项即生效;未匹配则 fallback 到默认码(如
SYSTEM_GATEWAY_ERROR) - 支持运行时热更新规则(例如从配置中心拉取),实现错误码策略动态切换
3. 注意错误来源的多样性
await 报错不只来自 HTTP 层,需覆盖全链路异常场景:
-
网络层:DNS 失败、连接拒绝、超时(Axios 的
ERR_CONNECTION_REFUSED)、TLS 握手失败 - 协议层:HTTP 状态码非 2xx(4xx/5xx)、空响应体、非法 JSON
- 网关自身:参数校验失败、路由找不到、限流触发、JWT 解析异常
- 每类错误应有独立映射路径,例如网络错误走
NETWORK_*前缀,业务错误走BIZ_*前缀
4. 保持原始错误可追溯
动态置换错误码 ≠ 吞掉原始信息,必须保留诊断线索:
- 新错误对象中保留
originalError字段(注意循环引用,可用serializeError(err)提取关键字段) - 记录完整日志时打印原始 error.stack 和 response headers
- 对外返回时仅暴露 code + message + requestId,不泄露内部细节(如文件路径、数据库名)
本质上,这不是“给 await 加 try-catch”,而是把每个需要 await 的外部依赖调用,都纳入受控的防腐执行容器中。错误码置换发生在容器的 catch 分支,且必须结合上下文(调用方、目标服务、SLA 要求)做语义化翻译。不复杂但容易忽略。

















