# API设计规范 ## 概述 本规范基于项目现有代码实现,定义了API接口的设计原则和标准。所有API必须遵循RESTful设计理念,确保接口的一致性、可维护性和易用性。 ## 基本原则 1. **RESTful设计**:使用RESTful风格的URL路径,资源导向的设计理念。 2. **统一响应格式**:所有API响应必须使用统一的`R`格式。 3. **状态码规范**:使用HTTP状态码和业务状态码相结合的方式。 4. **认证授权**:采用Token-based认证机制。 5. **错误处理**:统一的异常处理机制。 ## URL路径设计 1. **路径结构**: - 使用小写字母和连字符(-)分隔单词 - 路径格式:`/{resource}/{action}` 或 `/{resource}/{id}/{action}` - 示例:`/geo/nearest`, `/get_openid`, `/user_info` 2. **控制器映射**: - 使用`@RequestMapping("/{module}")`定义模块路径 - 子路径使用`@GetMapping`, `@PostMapping`等注解 ## HTTP方法 - **GET**:查询操作,用于获取资源 - **POST**:创建或执行操作,用于提交数据或执行动作 - **PUT**:更新操作(项目中暂未使用) - **DELETE**:删除操作(项目中暂未使用) ## 请求格式 1. **Content-Type**: - JSON请求:`application/json` - 使用`@RequestBody`注解接收JSON数据 2. **参数传递**: - 查询参数:使用`@RequestParam` - 请求体:使用`@RequestBody` - 路径参数:使用`@PathVariable`(项目中暂未使用) ## 响应格式 1. **统一响应类**:`R` ```java { "code": Integer, // 状态码 "message": String, // 响应消息 "data": T // 响应数据,可为空 } ``` 2. **成功响应**: - 状态码:200 - 使用`R.success(code, message, data)`构造 3. **失败响应**: - 状态码:400(参数错误)、500(服务器错误) - 使用`R.fail(code, message)`构造 ## 状态码规范 1. **成功状态码**: - 200:操作成功 - 具体业务成功码定义在`SuccessResultCode`枚举中 2. **失败状态码**: - 400:参数校验异常、请求参数错误 - 500:服务器内部错误 - 具体业务失败码定义在`ErrorCode`枚举中 3. **异常状态码**: - 定义在`ErrorCode`枚举中 - 包括各种业务相关的错误码,如参数错误、资源不存在等 ## 认证与授权 1. **认证方式**: - 优先使用标准 Authorization header:`Authorization: Bearer `(AuthInterceptor 优先解析该方式) - 向后兼容支持:`X-Token` 或 `token` header - 注意:`AuthInterceptor` 当前实现通过请求头解析 token(Authorization、X-Token、token),并不直接读取请求 body。若需要从 body 中提取 token,请在 Controller 层明确定义并处理。 2. **拦截器配置**: - `AuthInterceptor` 负责 Token 校验并在校验通过后把 `currentUserId`/`currentUserRole` 放入 request attribute - 排除路径:`/`、`/get_openid`、`/v3/api-docs/**`, `/swagger-ui/**`, `/doc.html`, `/webjars/**`, `/favicon.ico` 等(参见 `WebMvcConfig` 的 `excludePathPatterns`) 3. **Token管理**: - 使用`TokenService`生成和校验Token - 校验通过后将`userId`放入request属性 ## 错误处理 1. **全局异常处理器**:`CustomExceptionHandler` - 处理`BindException`:参数绑定异常 - 处理`ConstraintViolationException`:约束违反异常 - 处理`CustomException`:自定义业务异常 - 处理`Exception`:未知异常 2. **异常响应**: - 统一返回`R.fail(code, message)`格式 - 记录错误日志 ## 分页处理 1. **分页类**:`Page` ```java { "pageNum": int, // 当前页码 "pageSize": int, // 每页大小 "total": long, // 总记录数 "list": List // 数据列表 } ``` 2. **分页插件**:使用`pagehelper`实现分页 - 配置在`application.yml`中 - 数据库方言:MySQL ## 跨域配置 1. **CORS配置**:`CorsConfig` - 实际实现:`CorsConfig.addCorsMappings` 当前对所有路径 `/**` 开放跨域(本地开发场景),并允许源 `http://localhost:3000`。 - 允许方法:GET, POST, PUT, DELETE, OPTIONS - 允许头:`*`(允许所有请求头)并暴露头 `X-Custom-Header`, `X-Token`, `Authorization` 给前端 - 允许凭证:true ## API文档 1. **文档工具**:使用 Knife4j / springdoc-openapi(基于 OpenAPI 3) - `Knife4jConfig` 中配置了 `OpenAPI` 的基本信息(title、description、version)并通过 `GroupedOpenApi` 扫描包 `work.baiyun.chronicdiseaseapp`。 - 默认本地服务器地址配置为 `http://localhost:8080`,版本号在 `OpenAPI` info 中设置为 `v1`。 - 访问路径:`/doc.html`(Knife4j UI)或 springdoc 的 `/v3/api-docs`。 2. **字段描述**: - 使用`@Schema`注解描述请求/响应字段 - 示例: ```java @Schema(description = "更新用户信息请求") public class UpdateUserInfoRequest { @Schema(description = "头像(可选)") private String avatar; } ``` ## 安全考虑 1. **输入验证**:使用Bean Validation注解 2. **SQL注入防护**:使用MyBatis-Plus预编译 3. **XSS防护**:前端负责,API层不额外处理 4. **敏感信息**:不在日志中记录Token等敏感信息 ## 版本控制 - 当前API版本:v0 - 版本信息在Swagger配置中定义 - 未来版本升级时,可通过URL路径前缀区分(如`/v1/`、`/v2/`) ## 性能优化 1. **连接池**:使用HikariCP数据库连接池 2. **缓存**:根据业务需要添加缓存机制(项目中暂未实现) 3. **异步处理**:对于耗时操作可考虑异步处理(项目中暂未使用) ## 测试规范 - API接口应编写单元测试和集成测试 - 测试应覆盖正常流程和异常情况 - 使用Mock框架模拟依赖服务 ## 关于长整型 ID 的传输策略(补充) 说明:项目使用雪花算法(Snowflake)生成的主键/业务 ID 为 64 位长整型(BIGINT / Long),在前端 JavaScript 中直接使用 Number 类型可能会发生精度丢失。为避免前端显示与处理错误,API 层对外响应时应把此类 ID 以字符串形式返回。 要点: - 在对外响应的 DTO/VO 中,将 id 字段定义为 `String`(响应侧)。请求参数可继续使用 Long/BigInt(后端解析/校验)。 - 在时序/批量返回场景(List/分页)中,同样应保证 id 字段为字符串类型。 - 在 OpenAPI/Swagger 注释中显式标注字段类型为 `string`,并在说明中写明其实际在数据库/业务中的原始类型为 Long/BIGINT。 示例(响应 VO 与转换): ```java public class PhysicalDataResponse { @Schema(description = "记录ID,数据库类型为BIGINT,JSON返回为字符串") private String id; // 对外返回 String // ... 其他字段 } // 服务层转换示例 PhysicalDataResponse r = new PhysicalDataResponse(); BeanUtils.copyProperties(record, r); if (record.getId() != null) { r.setId(record.getId().toString()); } ``` 兼容性与说明: - 该约定属于接口契约变更(响应字段类型从 number -> string),在发布到生产前请在 API 文档、变更日志与前端团队进行告知并做好契约验证(可用契约测试)。 - 若需要向下兼容旧客户端,可考虑在文档中说明旧客户端如何兼容(例如前端接收到字符串后显式转换为 BigInt 或按字符串处理)。