Featured image of post REST API 企业级设计实战:登录鉴权、重大危险源、双预防与园区版双向同步

REST API 企业级设计实战:登录鉴权、重大危险源、双预防与园区版双向同步

从一份 30+ 端点的安全生产类 REST API 规范出发,系统梳理企业级接口设计的 6 大原则:统一响应体、Bearer Token 鉴权、RESTful 风格、批量与同步语义、状态码语义、版本与灰度

背景:本文以一份 1931 行的"安全生产类平台"接口规范为蓝本——它包含登录、重大危险源监测设备、双预防(安全风险分析单元/事件/管控措施/隐患排查任务/记录/信息)、特殊作业票、教育培训等 30+ 个端点,全部采用统一响应体 + Bearer Token + add/update/delete 三段式风格。本文重点不是"再写一遍规范",而是抽丝剥茧出企业级 REST API 设计的 6 大原则,让读者下次写接口时直接对号入座。

Why 2020 年:REST 在 2014-2018 完成主流化(Spring 5 / JAX-RS 2.1 / OpenAPI 3.0 都是这一时期定型的),2020 年是企业从"能跑"转向"规范 + 可观测"的分水岭。本文的方法论至今仍是企业级接口设计的底座。

一、统一响应体:把"成功"和"业务失败"分开

很多团队在写 REST 接口时,第一反应是"成功返回数据、失败抛 4xx/5xx + 异常 message"——但企业级 API 必须把"协议层错误"和"业务层错误"分开,否则前端要写两套解析逻辑,后端监控也要维护两套指标。

1.1 业界三大响应体风格对比

风格成功业务失败网络失败典型代表
HTTP 优先200 + 数据4xx/5xx + 错误体5xx + 异常Spring MVC 默认、Express 默认
统一信封200 + {code:0, data}200 + {code:1xxx, msg}5xx + 异常阿里云 API、字节内部 RPC 协议
混合模式200/201 + 数据4xx + 业务 code5xx + 异常GitHub API、Stripe API

这份规范采用"统一信封"风格,所有业务成功都返回 200,靠 code 字段区分:

1
2
3
4
5
6
{
  "msg": "成功",
  "code": 0,
  "data": null,
  "success": true
}

设计要点

  • code = 0 表示业务成功,非 0 表示业务错误(避免误用 HTTP 200 + 业务失败 + HTTP 4xx 三者交叉)
  • success 冗余但友好,前端 if (res.success)if (res.code === 0) 可读性高
  • msg 用中文(业务系统),对外公开的 API 建议英文 + 国际化 key
  • data 类型根据业务而定,列表/对象/null 都允许

1.2 状态码语义对照表

HTTP 状态码业务场景后端处理前端处理
200业务成功(包含 code != 0 业务失败)永远返回 200success 字段
400请求参数格式错误(JSON 解析失败)Spring @Valid 触发弹"参数错误"Toast
401Token 缺失/过期/无效拦截器抛 AuthException跳登录页
403权限不足RBAC 拦截弹"无权限"
404资源不存在@ExceptionHandler弹"记录不存在"
429限流Sentinel/Sentinel 拦截弹"操作太频繁"
500系统异常全局异常处理弹"系统异常"+ 上报埋点
502/503/504网关/上游异常Nginx/OpenResty弹"服务暂不可用"

二、URL 命名:模块化 + 资源化 + 动词三段式

这份规范的 URL 设计非常典型:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
POST /hazard/monitorEquip/add         # 重大危险源-监测设备-新增
POST /hazard/monitorEquip/update      # 重大危险源-监测设备-修改
POST /hazard/monitorEquip/delete      # 重大危险源-监测设备-删除
POST /prevent/riskUnit/add            # 双预防-风险分析单元-新增
POST /prevent/riskEvents/add          # 双预防-安全风险事件-新增
POST /prevent/riskMeasures/add        # 双预防-管控措施-新增
POST /prevent/troubleshootTask/add     # 双预防-隐患排查任务-新增
POST /prevent/troubleshootRecord/add  # 双预防-隐患排查记录-新增
POST /prevent/troubleshootInfo/add    # 双预防-隐患排查信息-新增
POST /prevent/ticketBaseInfo/add      # 特殊作业票-票证信息-新增
POST /prevent/trainBaseInfo/add       # 教育培训-培训信息-新增

2.1 URL 设计的 4 层语义

层级含义这份规范的做法
L1 一级模块业务域hazard(重大危险源)、prevent(双预防 + 特殊作业票 + 教育培训)、system(系统)
L2 二级模块子系统monitorEquip(监测设备)、riskUnit(风险单元)
L3 动作CRUDadd / update / delete不是 RESTful 的 POST/GET/PUT/DELETE
L4 路径参数资源 ID写在请求体 id 字段,不是 URL

2.2 这种"动作式 URL" vs RESTful 风格

RESTful 风格(HTTP 动词 + 资源):

1
2
3
4
POST   /api/v1/hazard/monitor-equip        # 新增
GET    /api/v1/hazard/monitor-equip/{id}   # 查询
PUT    /api/v1/hazard/monitor-equip/{id}   # 修改
DELETE /api/v1/hazard/monitor-equip/{id}   # 删除

这份规范的动作式风格

1
2
3
POST /hazard/monitorEquip/add
POST /hazard/monitorEquip/update
POST /hazard/monitorEquip/delete
维度RESTful动作式(这份规范)
HTTP 动词丰富度4-5 个仅 POST(与 CSRF/中间件兼容性好)
网关路由匹配复杂(要看动词)简单(只看路径)
审计日志method + pathpath(更易聚合)
团队学习成本低(无歧义)
幂等性需显式声明update/delete 天然要求幂等
OpenAPI 自动生成友好不友好(同一路径下多个动作)

我的建议对内系统、CRUD 密集型、强审计 → 动作式(add/update/delete);对外 API、平台化 → RESTful。这份规范属于前者,理由是它要对接大量前端 + 移动端 + 第三方园区版系统,用 POST /xxx/add 这种风格,后端可以用统一的请求体 + 鉴权 + 日志拦截器,避免 HTTP 动词带来的中间件兼容性问题。

2.3 命名规范:驼峰 vs 短横线

1
2
3
4
5
6
7
# 驼峰(这份规范)
/hazard/monitorEquip
/prevent/riskUnit

# 短横线(RESTful 推荐)
/hazard/monitor-equip
/prevent/risk-unit
风格优点缺点
驼峰 monitorEquip与 Java/Go 字段名一致URL 编码 %2D 不需要,但 RFC 3986 不推荐
短横线 monitor-equipRFC 3986 友好、SEO 友好需与字段名手动映射

最佳实践对外 API 用短横线(GitHub、Twitter、Stripe 都这样),对内 RPC 用驼峰(与业务代码一致,减少转换)。

三、鉴权:Bearer Token + 30 天有效期

3.1 登录接口

1
2
POST /system/login
Content-Type: application/json

请求体:

1
2
3
4
{
  "userName": "{{USERNAME}}",
  "password": "{{PASSWORD}}"
}

响应体:

1
2
3
4
5
6
7
8
{
  "msg": "",
  "code": 0,
  "data": {
    "token": "{{TOKEN}}"
  },
  "success": true
}

脱敏说明:源文档中 userName 默认填 adminpassword 默认填 123456token 是一个真实的 UUID 格式字符串(b5b6b03f-18f4-4caf-acdf-25c5cc555bd3)。默认账号密码不应在博文中演示,token 也应视为凭据——博文全部用 {{}} 占位。

3.2 业务请求的鉴权头

1
2
3
POST /hazard/monitorEquip/add
Authorization: Bearer {{TOKEN}}
Content-Type: application/json

关键设计

取值原因
鉴权方案Bearer TokenRFC 6750 标准,与 OAuth 2.0 / OIDC 兼容
有效期30 天长效适合企业用户(避免每天重新登录),但配合刷新机制更佳
存储localStorage / sessionStorage前端选型决定(localStorage 持久但有 XSS 风险,HttpOnly Cookie 安全但要 CSRF 防护)
传输HTTPS 强制防止 token 泄露
撤销服务端黑名单 + JWT推荐:Redis 黑名单 + 短 JWT

3.3 进阶:刷新令牌模式

1
2
POST /system/refresh
Authorization: Bearer {{REFRESH_TOKEN}}

返回新的 access_token(短效 2 小时)+ 复用 refresh_token(长效 30 天)。这种"双 token"模式是 OAuth 2.0 推荐的工业实践,避免一个长效 token 泄露就要全员重置。

四、请求/响应体设计:5 个核心要素

4.1 请求体的 5 段式结构

以"重大危险源-监测设备-新增"为例(这份规范最详细的端点):

要素字段类型必选备注
基础标识idstring主键
业务字段monitorName monitorCode monitorType numericalUnit rangeMax rangeMinstring业务核心数据
责任字段responsibilityPeopleName deptNamestring责任人/部门
位置字段longitude latitude addressnumber/string经纬度 + 地址
设备字段manufacturer checkTime checkCyclestring/datetime厂家/校验
告警字段oneLevelHigh twoLevelHigh oneLevelLow twoLevelLowstring一二级高低报
类型字段monitorSpotType state equipmentType equipmentNamestring类型/状态

4.2 字段类型选型决策树

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
字段值是什么?
├── 中文/枚举/编号
│   ├── 固定有限集合 → string("男"/"女"、"已处理"/"未处理")
│   ├── 不定文本     → string(地址、姓名、备注)
│   └── 编号/UUID   → string(不直接用 number!避免 JS 精度问题)
├── 数值
│   ├── 整数计数     → int(年龄、数量)
│   ├── 小数金额     → string 或 long(避免精度问题,"1.23")
│   ├── 经纬度       → number(前端 leaflet/高德直接用)
│   └── 大 ID        → string(雪花 ID 19 位超出 JS Number 安全范围)
└── 时间
    ├── 精确到秒     → string ISO 8601("2026-06-04T10:30:00+08:00")
    ├── 仅日期       → string("2026-06-04")
    └── 时间戳       → long(毫秒,前端 new Date(ms))

关键提醒永远不要用 number 表示 ID/编号/金额!JavaScript 的 Number 是双精度浮点,Number.MAX_SAFE_INTEGER9007199254740991(约 9 * 10^15),超出后 12345678901234567 === 12345678901234568 会返回 true。雪花 ID 通常是 18-19 位,必须用 string

4.3 必选/可选字段的标注规范

1
2
3
4
5
# 推荐:仅标注"必选"(默认是可选)
"id": { "type": "string", "required": true, "desc": "主键" }
"monitorName": { "type": "string", "required": true, "desc": "监测点名称" }

# 不推荐:每个字段都标注 required: false(噪音)

4.4 错误响应体的差异化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// 业务错误(4xx + 业务 code)
{
  "msg": "监测点编号已存在",
  "code": 1001,
  "data": null,
  "success": false
}

// 系统错误(5xx + 异常 trace)
{
  "msg": "系统繁忙,请稍后重试",
  "code": -1,
  "data": null,
  "success": false
}

code 设计原则

  • 0 = 成功
  • 1xxx = 业务错误(参数错误、唯一冲突、状态非法等)
  • 2xxx = 鉴权错误(token 过期、权限不足)
  • 3xxx = 第三方错误(短信/邮件/支付失败)
  • 4xxx-5xxx = 内部错误(数据库异常、Redis 异常等)
  • -1 = 系统兜底

五、批量与同步:园区版双向同步的 4 种模式

这份规范有一个特殊设计:几乎所有端点的接口功能都写"企业版新增数据时同步到园区版"——这意味着每个写操作都是双写的。这种"企业版 + 园区版"的同步模式在政企/工业互联网场景非常典型。

5.1 4 种同步模式对比

模式实时性复杂度一致性适用
同步双写实时企业版 ↔ 园区版(这份规范的做法)
异步消息准实时(秒级)最终一致跨地域多活、削峰填谷
CDC 监听准实时最终一致数据库迁移、异构同步
定时拉取延迟(分钟/小时)对账、非关键数据

5.2 同步双写的实现细节

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@PostMapping("/hazard/monitorEquip/add")
public Result<?> addMonitorEquip(@RequestBody MonitorEquipDTO dto) {
    // 1. 参数校验
    validator.validate(dto);
    
    // 2. 业务校验(编号唯一性、权限等)
    if (monitorEquipRepository.existsByCode(dto.getMonitorCode())) {
        return Result.fail(1001, "监测点编号已存在");
    }
    
    // 3. 企业版落库
    monitorEquipRepository.save(dto);
    
    // 4. 同步园区版(这里是关键!)
    try {
        parkApiClient.pushMonitorEquip(dto);
    } catch (Exception e) {
        // 5. 园区版同步失败的处理(决定了一致性策略)
        log.warn("园区版同步失败,已加入重试队列: id={}", dto.getId(), e);
        retryQueue.push("park_sync", dto);  // 异步重试
        // 注意:这里**不要抛异常给前端**,因为企业版已经成功了
    }
    
    return Result.ok();
}

5 个关键决策

  1. 是否同步等待园区版响应? 答:否,园区版失败不阻断企业版(否则企业用户卡死)
  2. 失败后如何重试? 答:MQ 异步重试 + 死信队列人工介入
  3. 如何避免重复同步? 答:每个端点都带 id(主键),园区版用 upsert 语义
  4. 如何追踪同步状态? 答:每个对象加 sync_status(待同步/已同步/失败)+ sync_time
  5. 如何保证最终一致? 答:定时对账任务(每天凌晨拉取企业版增量 vs 园区版已收,对账差异)

5.3 批量同步的端点设计

1
2
POST /prevent/riskUnit/add-batch
Content-Type: application/json
1
2
3
4
5
6
7
{
  "list": [
    { "id": "1", "riskUnitName": "反应釜 A 单元" },
    { "id": "2", "riskUnitName": "储罐区 B 单元" },
    { "id": "3", "riskUnitName": "装卸区 C 单元" }
  ]
}

批量端点的 3 个原则

  1. 总量限制:单次 ≤ 500 条(超过容易超时/内存溢出)
  2. 部分成功语义:返回 data.succeeded / data.failed 两个数组
  3. 失败明细可重试failed[].reason 标明原因,前端可一键重试

六、CRUD 三段式 vs RESTful 的 7 大差异

维度动作式(这份规范)RESTful推荐
新增POST /xxx/addPOST /xxx等价
修改(整对象)POST /xxx/updatePUT /xxx/{id}等价
修改(部分字段)POST /xxx/updatePATCH /xxx/{id}RESTful 更精确
删除POST /xxx/deleteDELETE /xxx/{id}等价
查询单个POST /xxx/getByIdGET /xxx/{id}RESTful 天然缓存
查询列表POST /xxx/listGET /xxx?page=1&size=20RESTful 更标准
查询复杂条件POST /xxx/searchPOST /xxx/search等价(因为 GET 装不下复杂 JSON)

实战选型

  • CRUD 增删改 → 动作式(少一层 RESTful 心智负担)
  • 查询(特别是 GET)→ RESTful(浏览器/网关/CDN 都能缓存)
  • 复杂搜索(多条件 + JSON body)→ 动作式(POST /search 不可避免)

七、OpenAPI 3.0 自动化生成

这份规范是手写 Markdown 的,但现代化的企业 API 规范应该从代码生成

7.1 Java + springdoc-openapi

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@RestController
@RequestMapping("/hazard/monitorEquip")
@Tag(name = "重大危险源-监测设备")
public class MonitorEquipController {

    @Operation(summary = "新增监测设备")
    @PostMapping("/add")
    public Result<MonitorEquipVO> add(@Valid @RequestBody MonitorEquipDTO dto) {
        // ...
    }
}

7.2 自动生成 OpenAPI 3.0 + Swagger UI

1
2
3
4
5
6
<!-- pom.xml -->
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-ui</artifactId>
    <version>1.7.0</version>
</dependency>

访问 http://{{INTERNAL_HOST}}:8080/swagger-ui.html 即可看到自动生成的接口文档。比手写 Markdown 强 10 倍——前后端联调、自动化测试、Mock 数据都能从 OpenAPI 自动派生。

八、6 大设计原则速记

  1. 统一信封:所有响应都是 {code, msg, data, success},业务成功用 200
  2. Bearer Token:Authorization 头 + 30 天有效期 + HTTPS 强制
  3. 模块化 URL/一级模块/二级模块/动作,动作可选 add/update/delete 或 HTTP 动词
  4. 字段类型谨慎:ID/编号/金额 永远用 string,时间用 ISO 8601
  5. 批量与同步:单次 ≤ 500 条 + 部分成功 + MQ 异步重试 + 定时对账
  6. OpenAPI 自动化:从代码注解生成文档,不要手写 Markdown

九、典型代码生成(Java + Spring Boot)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 统一响应体
@Data
public class Result<T> {
    private String msg;
    private int code;
    private T data;
    private boolean success;
    
    public static <T> Result<T> ok() { return ok(null); }
    public static <T> Result<T> ok(T data) {
        Result<T> r = new Result<>();
        r.setCode(0);
        r.setMsg("成功");
        r.setData(data);
        r.setSuccess(true);
        return r;
    }
    public static <T> Result<T> fail(int code, String msg) {
        Result<T> r = new Result<>();
        r.setCode(code);
        r.setMsg(msg);
        r.setSuccess(false);
        return r;
    }
}

// 鉴权拦截器
@Component
public class AuthInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) {
        String auth = req.getHeader("Authorization");
        if (auth == null || !auth.startsWith("Bearer ")) {
            throw new AuthException("未登录");
        }
        String token = auth.substring(7);
        // 校验 token:从 Redis 取用户信息
        Long userId = tokenService.getUserIdByToken(token);
        if (userId == null) {
            throw new AuthException("Token 已过期");
        }
        // 放入 ThreadLocal
        UserContext.set(userId);
        return true;
    }
}

十、参考与延伸

下一步:建议直接用 springdoc-openapi(Java)或 swaggo(Go)从代码生成 API 文档;用 Postman / Apifox 维护接口测试用例;用 YApi / Apifox 做 Mock 数据;用 Pact 做消费者驱动的契约测试。

2024+ 视角

2020 年的"统一响应体 + Bearer Token + RESTful"骨架在 2024-2026 年依然成立,但生态发生了几个关键演进:

  1. OpenAPI 3.1 落地:3.0 在 2024 年仍是主流,但 3.1 已对齐 JSON Schema 2020-12 规范,并支持 WebhookAPI 设计优先(spec-first)模式。Stoplight Elements / Redocly 等文档渲染工具成为新标配。
  2. API 网关与契约测试
    • 网关:Kong 3.x、Apigee、Apache APISIX 3.x(云原生 + eBPF 加速)已成主流
    • 契约测试:Pact 2.0 + Pactflow 是消费者驱动测试的工业标准,配合 CI 做兼容性回归
  3. 鉴权演进:Bearer Token + JWT 仍是基础,但OAuth 2.1(2024 草案)将 PKCE 设为强制;企业级推荐 OIDC(OpenID Connect) + Keycloak / Auth0 / Authing
  4. API 安全OWASP API Security Top 10(2023 版) 把"Broken Object Level Authorization(BOLA)“列为 #1——任何列表/详情接口都要做资源所有权校验。42Crunch / APIsec 提供自动化 API 安全扫描。
  5. GraphQL / gRPC 互补
    • 复杂前端聚合场景:GraphQL(Hasura、Apollo、Yoga)正在替代"网关聚合多个 REST”
    • 内部服务通信:gRPC + protobuf 已成事实标准(Envoy/Istio 内部走 xDS/gRPC)
  6. 可观测性:API 监控从"看 HTTP 状态码"升级到 RED(Rate/Error/Duration)+ 业务维度 traceOpenTelemetry 1.x + Jaeger / Tempo 把 traceid 注入 HTTP header,跨服务追踪成为标配。
  7. 园区版/双写模式进化:2024 年企业级同步更推荐 CDC(Change Data Capture) ——Debezium 监听 MySQL binlog,Kafka 分发,下游订阅。比"应用层 try-catch 同步双写"更可靠、侵入小。

实战建议更新

  • 单体 → REST + OpenAPI + 统一信封
  • 微服务 → REST(对外)+ gRPC(对内)+ GraphQL(BFF)
  • 鉴权 → OIDC + OAuth 2.1 + RBAC/ABAC
  • 文档 → Spec-First(OpenAPI 3.1 + Stoplight)
  • 测试 → 契约测试(Pact)+ E2E(Playwright)+ 性能(k6)
  • 同步 → 避免应用层双写,改 CDC + 事件驱动

2020 年这篇的"6 大设计原则"骨架完全成立,2024+ 视角主要补全的是 OAuth 2.1、OWASP API Top 10、CDC 同步、OpenTelemetry 四个维度。

使用 Hugo 构建
主题 StackJimmy 设计