Files
workspace/code/fms/.qoder/repowiki/zh/content/后端架构/API设计.md
T
2026-08-23 21:02:41 +08:00

18 KiB
Raw Blame History

API设计

**本文引用的文件** - [ApiResponse.java](file://fms-api/src/main/java/cn/g3soft/fmsapi/utils/ApiResponse.java) - [GlobalExceptionHandler.java](file://fms-api/src/main/java/cn/g3soft/fmsapi/exception/GlobalExceptionHandler.java) - [BusinessException.java](file://fms-api/src/main/java/cn/g3soft/fmsapi/exception/BusinessException.java) - [AuthenticationException.java](file://fms-api/src/main/java/cn/g3soft/fmsapi/exception/AuthenticationException.java) - [SaveObjectException.java](file://fms-api/src/main/java/cn/g3soft/fmsapi/exception/SaveObjectException.java) - [ParamUtils.java](file://fms-api/src/main/java/cn/g3soft/fmsapi/utils/ParamUtils.java) - [AuthController.java](file://fms-api/src/main/java/cn/g3soft/fmsapi/controller/AuthController.java) - [DataController.java](file://fms-api/src/main/java/cn/g3soft/fmsapi/controller/DataController.java) - [FileController.java](file://fms-api/src/main/java/cn/g3soft/fmsapi/controller/FileController.java)

目录

  1. 简介
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能考虑
  8. 故障排查指南
  9. 结论
  10. 附录

简介

本规范文档面向FMS API的RESTful接口设计与实现,聚焦以下目标:

  • 统一响应格式与数据封装:基于 ApiResponse 记录体提供一致的成功/失败返回结构。
  • 全局异常处理:通过 GlobalExceptionHandler 集中捕获业务异常、认证异常、保存异常及系统异常,并输出标准化错误响应。
  • 请求参数验证与类型转换:使用 ParamUtils 进行参数提取、必填校验与类型检查,避免控制器内重复逻辑。
  • 版本管理与向后兼容:在URL路径中引入版本前缀,结合字段级兼容性策略保障演进安全。
  • 最佳实践与常见问题:总结接口命名、状态码、错误信息、幂等性与分页等实践建议。

项目结构

FMS API采用分层组织:

  • controller:对外暴露REST端点,负责参数接收与响应封装。
  • service:业务逻辑(不在本文重点)。
  • exception:自定义异常与全局异常处理器。
  • utils:通用工具类,包括统一响应体 ApiResponse、参数工具 ParamUtils 等。
graph TB
Client["客户端"] --> Ctl_Auth["AuthController<br/>/auth/*"]
Client --> Ctl_Data["DataController<br/>/data/*"]
Client --> Ctl_File["FileController<br/>/file/*"]
Ctl_Auth --> Svc_Auth["AuthService"]
Ctl_Data --> Svc_Data["DataService / DataSaveService"]
Ctl_File --> Svc_File["FileService"]
Ctl_Auth --> Resp["ApiResponse"]
Ctl_Data --> Resp
Ctl_File --> Resp
Ctl_Auth --> Ex_Hdl["GlobalExceptionHandler"]
Ctl_Data --> Ex_Hdl
Ctl_File --> Ex_Hdl

图表来源

章节来源

核心组件

  • 统一响应体 ApiResponse:以记录体形式定义 code、message、data 三字段,并提供 success/fail 静态工厂方法;内置常用状态码常量,便于跨模块复用。
  • 参数工具 ParamUtils:提供 getString/getRequiredString/get/getList/getArray 等方法,支持空值与类型校验,并在不合法时抛出 BusinessException。
  • 全局异常处理器 GlobalExceptionHandler:集中处理认证异常、业务异常、保存异常、数据库异常与未知异常,统一返回 ApiResponse 并记录日志。

章节来源

架构总览

API层遵循“控制器薄、服务厚”的原则:

  • 控制器仅做参数解析、调用服务、封装响应。
  • 业务规则集中在服务层,必要时抛出自定义异常。
  • 全局异常处理器将异常转换为统一的 ApiResponse。
sequenceDiagram
participant C as "客户端"
participant A as "AuthController"
participant S as "AuthService"
participant E as "GlobalExceptionHandler"
participant R as "ApiResponse"
C->>A : POST /auth/login {orgid, userid, password}
A->>A : 参数校验(ParamUtils/StringUtils)
A->>S : login(orgId, userId, password, request)
alt 认证成功
S-->>A : Map<String,Object>
A-->>C : ApiResponse.success(data)
else 认证失败
A-->>C : ApiResponse.fail(AUTH_ERROR_CODE, message)
end
Note over A,S : 若发生未捕获异常,由E统一处理并返回系统错误

图表来源

详细组件分析

统一响应体 ApiResponse

  • 结构:code(数字)、message(字符串)、data(泛型)。
  • 成功构造:success(data)、success(message, data)。
  • 失败构造:fail(message)、fail(code, message)。
  • 状态码约定:
    • 0:成功
    • 401:认证错误
    • 40101:会话被顶替
    • 2001:保存失败
    • 1000:业务错误
    • 500:系统错误
classDiagram
class ApiResponse~T~ {
+int code
+String message
+T data
+static success(T)
+static success(String, T)
+static fail(String)
+static fail(int, String)
}

图表来源

章节来源

全局异常处理 GlobalExceptionHandler

  • 认证异常 AuthenticationException:返回 401 认证错误。
  • 业务异常 BusinessException:返回 1000 业务错误。
  • 保存异常 SaveObjectException:返回 2001 保存失败,并附带 table/action/index 详情。
  • 数据库异常 SQLException/DataAccessException:记录系统异常日志,返回 500 系统错误。
  • 其他异常 Exception:兜底捕获,记录日志并返回 500。
flowchart TD
Start(["进入异常处理"]) --> Type{"异常类型?"}
Type --> |AuthenticationException| AuthErr["返回 401 认证错误"]
Type --> |BusinessException| BizErr["返回 1000 业务错误"]
Type --> |SaveObjectException| SaveErr["返回 2001 保存失败<br/>附加table/action/index"]
Type --> |SQLException/DataAccessException| DBErr["记录系统异常日志<br/>返回 500"]
Type --> |其他Exception| SysErr["记录系统异常日志<br/>返回 500"]
AuthErr --> End(["结束"])
BizErr --> End
SaveErr --> End
DBErr --> End
SysErr --> End

图表来源

章节来源

请求参数验证与类型转换 ParamUtils

  • getString/getRequiredString:从Map中取值,必填项为空时抛出业务异常。
  • get:按类型取值并进行类型校验,类型不符抛出业务异常。
  • getList/getArray:集合与数组转换,类型不符抛出业务异常。
  • 适用场景:控制器中统一参数解析,减少样板代码与分支判断。
flowchart TD
In(["传入 params, field, type"]) --> GetVal["获取原始值"]
GetVal --> NullCheck{"值为空?"}
NullCheck --> |是| ReturnNull["返回 null"]
NullCheck --> |否| TypeCheck{"类型匹配?"}
TypeCheck --> |否| ThrowBiz["抛出业务异常"]
TypeCheck --> |是| Cast["类型转换并返回"]

图表来源

章节来源

控制器与API设计示例

认证接口 AuthController

  • 路径:POST /auth/login
  • 请求体:包含 orgid、userid、password
  • 行为:参数校验 -> 调用服务 -> 成功返回统一数据,失败返回认证错误码
  • 异常:缺失参数或认证失败会返回对应业务/认证错误
sequenceDiagram
participant C as "客户端"
participant A as "AuthController"
participant P as "ParamUtils"
participant S as "AuthService"
participant R as "ApiResponse"
C->>A : POST /auth/login
A->>P : getString("orgid"/"userid"/"password")
P-->>A : 参数值或抛出业务异常
A->>S : login(...)
alt 成功
S-->>A : Map
A-->>C : ApiResponse.success(Map)
else 失败
A-->>C : ApiResponse.fail(401, "认证失败")
end

图表来源

章节来源

数据接口 DataController

  • 路径:/data/loaddata、/data/loaddatabysql、/data/page、/data/saveobjt、/data/nextid、/data/nextcode、/data/describe
  • 行为:参数解析 -> 调用数据服务 -> 返回统一响应
  • 注意:saveobjt 可能触发 SaveObjectException,由全局处理器统一返回 2001
sequenceDiagram
participant C as "客户端"
participant D as "DataController"
participant DS as "DataService/DataSaveService"
participant R as "ApiResponse"
C->>D : POST /data/{endpoint}
D->>DS : 执行查询/保存/生成ID/描述等操作
DS-->>D : 结果或抛出异常
alt 正常
D-->>C : ApiResponse.success(...)
else 保存异常
D-->>C : 由全局处理器返回 2001
end

图表来源

章节来源

文件接口 FileController

  • 路径:/file/config、/file/upload、/file/upload-credential、/file/save-record、/file/delete、/file/static/{fileName:.+}
  • 行为:上传本地/OSS凭证、保存记录、删除文件、静态资源访问(含路径穿越防护)
  • 注意:静态资源访问需校验路径合法性,防止越权访问
sequenceDiagram
participant C as "客户端"
participant F as "FileController"
participant FS as "FileService"
participant R as "ApiResponse"
C->>F : POST /file/upload (files, father, moduleId, cateId?)
F->>FS : localUpload(...)
FS-->>F : 文件元数据列表
F-->>C : ApiResponse.success(列表)
C->>F : GET /file/static/{fileName}
F->>F : 路径穿越校验
alt 合法
F-->>C : 二进制流
else 非法
F-->>C : 404
end

图表来源

章节来源

依赖关系分析

  • Controller 依赖 Service 与 Utils(ParamUtils、ApiResponse)。
  • GlobalExceptionHandler 依赖 ApiResponse 与各自定义异常类型。
  • ParamUtils 依赖 BusinessException 进行参数校验失败时的异常抛出。
graph LR
AuthCtl["AuthController"] --> Resp["ApiResponse"]
DataCtl["DataController"] --> Resp
FileCtl["FileController"] --> Resp
AuthCtl --> Param["ParamUtils"]
DataCtl --> Param
FileCtl --> Param
GlobalHdl["GlobalExceptionHandler"] --> Resp
GlobalHdl --> BizEx["BusinessException"]
GlobalHdl --> AuthEx["AuthenticationException"]
GlobalHdl --> SaveEx["SaveObjectException"]

图表来源

章节来源

性能考虑

  • 统一响应体为轻量记录体,序列化开销低,适合高频接口。
  • 参数解析集中在 ParamUtils,减少控制器分支与重复校验,提升可维护性。
  • 全局异常处理器集中记录系统异常日志,便于定位问题与监控告警。
  • 文件静态资源访问直接读取磁盘,注意大文件传输与缓存策略(如条件请求、分片下载)。

[本节为通用指导,无需具体文件引用]

故障排查指南

  • 认证失败:检查 /auth/login 的请求体是否包含 orgid、userid、password,确认服务端认证逻辑。
  • 参数错误:关注 ParamUtils 抛出的业务异常,核对字段名与类型。
  • 保存失败:查看 SaveObjectException 中的 table、action、index 信息,定位具体操作与行号。
  • 数据库异常:检查 SQL 与连接配置,关注全局异常处理器记录的系统异常日志。
  • 文件访问异常:确认 /file/static 的路径拼接与权限控制,避免路径穿越。

章节来源

结论

本项目通过 ApiResponse 统一响应、ParamUtils 统一参数校验、GlobalExceptionHandler 统一异常处理,构建了清晰、稳定且易扩展的API基础能力。建议在后续开发中:

  • 严格遵循RESTful命名与HTTP语义。
  • 在控制器中保持薄逻辑,复杂规则下沉至服务层。
  • 使用 ParamUtils 进行参数校验,避免散落分支。
  • 通过全局异常处理器保证错误响应一致性。
  • 引入API版本管理,确保向后兼容。

[本节为总结性内容,无需具体文件引用]

附录

RESTful API设计规范

  • URL设计
    • 使用名词复数表示资源集合,例如 /data、/file。
    • 使用HTTP动词表达操作:GET读取、POST创建/提交、PUT更新、DELETE删除。
    • 如需过滤/分页,使用查询参数:?page=1&size=20。
  • 状态码
    • 业务状态码:0成功、1000业务错误、2001保存失败、401认证错误、500系统错误。
    • HTTP状态码:优先使用2xx/4xx/5xx,业务错误可在 ApiResponse.code 中体现。
  • 请求/响应
    • 请求体使用JSON,键名为小写下划线或驼峰(团队统一即可)。
    • 响应体统一为 ApiResponse{code,message,data}。
  • 版本管理
    • 在URL中加入版本前缀:/v1/auth/login、/v1/data/page。
    • 新增字段时保持向后兼容:新增可选字段、保留旧字段。
    • 废弃字段通过弃用标记与过渡期公告,逐步移除。
  • 幂等与安全
    • 读操作幂等;写操作尽量设计为幂等(如基于唯一键的upsert)。
    • 敏感信息不放入URL;使用HTTPS与鉴权机制。
    • 文件访问需校验路径,防止越权。

[本节为通用规范说明,无需具体文件引用]

常见问题与解决方案

  • 问题:前端无法解析响应
    • 解决:确保所有接口返回 ApiResponse 结构,避免直接返回原始对象。
  • 问题:参数类型不匹配
    • 解决:使用 ParamUtils.get 指定类型,或在控制器中进行显式转换。
  • 问题:保存失败难以定位
    • 解决:在服务层抛出 SaveObjectException,携带 table/action/index,由全局处理器统一返回。
  • 问题:系统异常无日志
    • 解决:依赖 GlobalExceptionHandler 的兜底异常处理,确保记录 method/uri 与堆栈。
  • 问题:文件越权访问
    • 解决:对静态资源路径进行规范化与白名单校验,拒绝越界路径。

[本节为通用问题解答,无需具体文件引用]