18 KiB
18 KiB
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)目录
简介
本规范文档面向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
图表来源
- AuthController.java:19-57
- DataController.java:19-96
- FileController.java:28-113
- GlobalExceptionHandler.java:15-85
- ApiResponse.java:1-32
章节来源
- AuthController.java:19-57
- DataController.java:19-96
- FileController.java:28-113
- GlobalExceptionHandler.java:15-85
- ApiResponse.java:1-32
核心组件
- 统一响应体 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
图表来源
章节来源
- GlobalExceptionHandler.java:15-85
- BusinessException.java:1-9
- AuthenticationException.java:1-9
- SaveObjectException.java:1-33
请求参数验证与类型转换 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"]
图表来源
- AuthController.java:19-57
- DataController.java:19-96
- FileController.java:28-113
- GlobalExceptionHandler.java:15-85
- ParamUtils.java:1-60
章节来源
性能考虑
- 统一响应体为轻量记录体,序列化开销低,适合高频接口。
- 参数解析集中在 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 与堆栈。
- 问题:文件越权访问
- 解决:对静态资源路径进行规范化与白名单校验,拒绝越界路径。
[本节为通用问题解答,无需具体文件引用]