错误处理与响应映射
这页解决的不是“有哪些错误码”,而是更靠近接入落地的问题:
- 这些错误在接口层应该怎么返回
- 哪些错误应该映射成
400 / 403 / 404 / 409 / 500 - 响应体结构应该长什么样
- 哪些错误可以直接暴露给调用方,哪些不应该原样透出
如果你已经看过 错误码,但还是不确定在中间件、管理 API 或后台接口里该怎么处理,这页更适合继续往下读。
一、先区分两层概念
permission-core 抛出的错误,和你最终返回给 HTTP 调用方的响应,不是同一层东西。
更合理的拆法是:
- 权限内核:负责抛出稳定错误码
- 接口层:负责把错误码映射成对外响应
这样做的好处是:
- 内核语义稳定
- HTTP 层可以按框架或业务风格自由映射
- 不会把底层错误对象直接暴露出去
二、推荐响应体结构
如果你要把错误返回给前端或外部调用方,推荐至少保持下面这几个字段:
更复杂的场景里,可以再补一个 details:
推荐约束:
code:稳定错误码,前后端都可以依赖message:更适合展示或日志搜索的短消息requestId:便于排查链路details:只放安全且必要的额外信息
三、常见错误到 HTTP 的映射建议
这张表的核心不是“唯一正确答案”,而是帮助你保持一个稳定口径:
- 参数错了,用
400 - 权限不够,用
403 - 资源不存在,用
404 - 状态冲突,用
409 - 服务端内部状态或存储坏了,用
500
四、中间件层怎么处理更稳
中间件层最常见的是处理 assert() 抛出来的错误。
这一步的重点不是拿到“实际 URL”,而是拿到稳定的接口资源。只要框架能暴露命中的模板路由,就优先用模板路径;拿不到时再退回普通 path。
统一错误处理中,更推荐这样映射:
五、管理 API 层更容易遇到什么错误
如果你在做角色和用户绑定管理接口,更常遇到的不是 PERMISSION_DENIED,而是:
ROLE_ALREADY_EXISTSROLE_NOT_FOUNDCIRCULAR_INHERITANCEINVALID_ARGUMENT
例如:
- 创建了一个已存在的角色 →
409 - 绑定了一个不存在的角色 →
404 - 把角色 parent 改成形成环 →
409 - 请求体缺字段或字段格式不合法 →
400
也就是说,管理 API 层通常更需要“业务错误映射”,而不是只处理权限失败。
六、前端怎么理解这些响应
前端或调用方最容易踩的坑,是把不同错误都当成“没权限”。
更推荐这样区分:
403:用户当前无权访问或操作400:调用方式错了,前端或调用方参数有问题404:依赖资源不存在409:当前状态冲突,通常需要先修正数据关系500:后端内部状态或初始化有问题
这样前端就不会把:
- “系统还没初始化”
- “角色根本不存在”
- “你真的没权限”
这三种完全不同的问题混成一个提示。
七、最常见的错误处理误区
- 把
NOT_INITIALIZED当成普通无权限处理 - 把
STORAGE_ERROR的原始堆栈直接返回给调用方 - 所有错误统一返回
500 - 把
getResources()当成最终权限判断,而不是菜单/按钮级参考结果
八、和 getResources() 的关系
这件事单独提醒一次:
getResources() 返回的不是错误,也不是最终放行结果,而是“某个 action 下可先参考的资源列表”。
所以如果前端页面是:
- 先用
getResources()做菜单显隐 - 真正提交时再调用接口
那接口层仍然必须保留 can() 或 assert(),不要把资源列表误当成最终授权结果。
下一步看什么
- 想回到错误码本身:看 错误码
- 想看接口层真实接法:看 Express 接入
- 想看管理能力入口:看 RoleManager 和 UserRoleManager
- 想把角色页、用户页和缓存失效串成一条链路:看 管理后台接入