# 模块11:API接口设计规范 --- ## 🎨 UI设计系统概览 > **完整设计系统文档请参考:** `UI设计系统.md` ### 核心设计原则 - **现代简约**:界面清晰,层次分明 - **专业高效**:减少操作步骤,提升工作效率 - **一致性**:统一的视觉语言和交互模式 - **可访问性**:符合WCAG 2.1 AA标准 ### 关键设计令牌 **颜色系统:** - 主色:`#0d6efd`(操作按钮、选中状态) - 成功:`#198754`(连接成功状态) - 危险:`#dc3545`(删除操作、错误提示) - 深灰:`#212529`(导航栏背景) - 浅灰:`#e9ecef`(工具栏背景) **字体系统:** - 字体族:系统字体栈(-apple-system, Segoe UI, Roboto等) - 正文:14px,行高1.5 - 标题:20-32px,行高1.2-1.4 - 小号文字:12px(文件大小、日期等) **间距系统:** - 基础单位:8px - 标准间距:16px(1rem) - 组件内边距:8px-16px **组件规范:** - 导航栏:高度48px,深色背景 - 工具栏:浅灰背景,按钮间距8px - 文件项:最小高度44px,悬停效果150ms - 按钮:圆角4px,过渡150ms **交互规范:** - 悬停效果:150ms过渡 - 触摸目标:最小44x44px - 键盘导航:Tab、Enter、Delete、F2、F5、Esc - 焦点状态:2px蓝色轮廓 **响应式断点:** - 移动端:< 768px(双面板垂直排列) - 平板:768px - 1024px - 桌面:> 1024px(标准布局) --- ## 11.1 RESTful设计原则 ### 11.1.1 URL设计规范 **设计原则:** 1. 使用名词复数形式(/api/files) 2. 使用HTTP方法表示操作类型 3. 使用查询参数传递可选参数 4. 使用路径参数传递必需参数 **命名规范:** - URL使用小写字母 - 单词之间使用连字符(-)分隔 - 资源名称使用复数形式 - 层级关系使用斜杠(/)分隔 **示例:** ``` ✅ 正确: GET /api/files/list POST /api/files/upload DELETE /api/files/{id} ❌ 错误: GET /api/getFiles POST /api/files/create DELETE /api/deleteFile/123 ``` ### 11.1.2 HTTP方法映射 | 方法 | 操作 | 是否幂等 | 说明 | 示例 | |------|------|---------|------|------| | GET | 查询 | 是 | 获取资源列表或详情 | GET /api/files/list | | POST | 创建 | 否 | 创建新资源 | POST /api/files/mkdir | | PUT | 更新 | 是 | 完整更新资源 | PUT /api/files/rename | | DELETE | 删除 | 是 | 删除资源 | DELETE /api/files/delete | ### 11.1.3 统一响应格式 **成功响应:** ```json { "success": true, "message": "操作成功", "data": {} } ``` **失败响应:** ```json { "success": false, "message": "操作失败", "error": "错误详细信息" } ``` **列表响应:** ```json { "success": true, "message": "查询成功", "data": [ {}, {}, {} ], "total": 100 } ``` **分页响应:** ```json { "success": true, "message": "查询成功", "data": [], "pagination": { "page": 1, "pageSize": 20, "total": 100, "totalPages": 5 } } ``` ## 11.2 完整API列表 ### 11.2.1 连接管理API #### 建立SFTP连接 **请求:** ``` POST /api/connection/connect Content-Type: application/json { "name": "测试服务器", "host": "192.168.1.100", "port": 22, "username": "root", "password": "123456", "privateKeyPath": "/path/to/private/key", "passPhrase": "key_password", "rootPath": "/home/user" } ``` **响应:** ```json { "success": true, "message": "连接成功", "data": { "sessionId": "sftp-12345678-1234-1234-1234-123456789abc" } } ``` **错误响应:** ```json { "success": false, "message": "连接失败", "error": "认证失败:用户名或密码错误" } ``` #### 断开连接 **请求:** ``` POST /api/connection/disconnect Content-Type: application/json { "sessionId": "sftp-12345678-1234-1234-1234-123456789abc" } ``` **响应:** ```json { "success": true, "message": "断开成功", "data": null } ``` #### 保存连接配置 **请求:** ``` POST /api/connection/save Content-Type: application/json { "id": 1, "name": "测试服务器", "host": "192.168.1.100", "port": 22, "username": "root", "password": "encrypted_password", "privateKeyPath": "/path/to/private/key", "passPhrase": "encrypted_key_password", "rootPath": "/home/user", "connectTimeout": 10000 } ``` **响应:** ```json { "success": true, "message": "保存成功", "data": { "id": 1, "name": "测试服务器", "host": "192.168.1.100", "port": 22, "username": "root", "createdAt": "2024-02-02T10:00:00", "updatedAt": "2024-02-02T10:00:00" } } ``` #### 获取所有保存的连接 **请求:** ``` GET /api/connection/list ``` **响应:** ```json { "success": true, "message": "查询成功", "data": [ { "id": 1, "name": "测试服务器", "host": "192.168.1.100", "port": 22, "username": "root", "createdAt": "2024-02-02T10:00:00", "updatedAt": "2024-02-02T10:00:00" } ] } ``` #### 获取指定连接 **请求:** ``` GET /api/connection/{id} ``` **响应:** ```json { "success": true, "message": "查询成功", "data": { "id": 1, "name": "测试服务器", "host": "192.168.1.100", "port": 22, "username": "root", "createdAt": "2024-02-02T10:00:00", "updatedAt": "2024-02-02T10:00:00" } } ``` **错误响应(连接不存在):** ```json { "success": false, "message": "连接不存在", "error": "ID为1的连接不存在" } ``` #### 删除连接配置 **请求:** ``` DELETE /api/connection/{id} ``` **响应:** ```json { "success": true, "message": "删除成功", "data": null } ``` #### 获取活跃连接列表 **请求:** ``` GET /api/connection/active ``` **响应:** ```json { "success": true, "message": "查询成功", "data": { "sftp-12345678-1234-1234-1234-123456789abc": { "id": 1, "name": "测试服务器", "host": "192.168.1.100", "port": 22, "username": "root" }, "sftp-87654321-4321-4321-4321-cba987654321": { "id": 2, "name": "生产服务器", "host": "192.168.1.200", "port": 22, "username": "admin" } } } ``` ### 11.2.2 文件操作API #### 列出文件 **请求:** ``` POST /api/files/list Content-Type: application/json { "sessionId": "sftp-12345678-1234-1234-1234-123456789abc", "path": "/home/user" } ``` **响应:** ```json { "success": true, "message": "查询成功", "data": [ { "name": "Documents", "path": "/home/user/Documents", "size": 0, "isDirectory": true, "modifiedTime": "2024-02-02T10:00:00", "permissions": "drwxr-xr-x" }, { "name": "test.txt", "path": "/home/user/test.txt", "size": 1024, "isDirectory": false, "modifiedTime": "2024-02-02T10:00:00", "permissions": "-rw-r--r--" } ] } ``` #### 获取文件信息 **请求:** ``` POST /api/files/info Content-Type: application/json { "sessionId": "sftp-12345678-1234-1234-1234-123456789abc", "path": "/home/user/test.txt" } ``` **响应:** ```json { "success": true, "message": "查询成功", "data": { "name": "test.txt", "path": "/home/user/test.txt", "size": 1024, "isDirectory": false, "modifiedTime": "2024-02-02T10:00:00", "permissions": "-rw-r--r--" } } ``` #### 获取当前路径 **请求:** ``` GET /api/files/path?sessionId=sftp-12345678-1234-1234-1234-123456789abc ``` **响应:** ```json { "success": true, "message": "查询成功", "data": { "path": "/home/user" } } ``` #### 上传文件 **请求:** ``` POST /api/files/upload Content-Type: multipart/form-data file: [文件内容] targetSessionId: sftp-12345678-1234-1234-1234-123456789abc targetPath: /home/user/ ``` **响应:** ```json { "success": true, "message": "上传成功", "data": null } ``` **错误响应:** ```json { "success": false, "message": "上传失败", "error": "目标目录不存在" } ``` #### 下载文件 **请求:** ``` GET /api/files/download?sessionId=sftp-12345678-1234-1234-1234-123456789abc&path=/home/user/test.txt ``` **响应:** ``` [文件流] ``` **响应头:** ``` Content-Type: application/octet-stream Content-Disposition: attachment; filename=test.txt Content-Length: 1024 ``` #### 服务器间传输 **请求:** ``` POST /api/files/transfer Content-Type: application/json { "sourceSessionId": "sftp-12345678-1234-1234-1234-123456789abc", "sourcePath": "/home/user/test.txt", "targetSessionId": "sftp-87654321-4321-4321-4321-cba987654321", "targetPath": "/home/target/" } ``` **响应:** ```json { "success": true, "message": "传输成功", "data": null } ``` #### 删除文件 **请求:** ``` DELETE /api/files/delete?sessionId=sftp-12345678-1234-1234-1234-123456789abc&path=/home/user/test.txt ``` **响应:** ```json { "success": true, "message": "删除成功", "data": null } ``` #### 批量删除 **请求:** ``` POST /api/files/batch-delete Content-Type: application/json { "sessionId": "local", "paths": [ "C:/test/file1.txt", "C:/test/file2.txt", "C:/test/file3.txt" ] } ``` **响应:** ```json { "success": true, "message": "删除完成", "data": { "successCount": 2, "failCount": 1, "failedFiles": [ "C:/test/file3.txt - 没有删除权限" ] } } ``` #### 重命名 **请求:** ``` POST /api/files/rename Content-Type: application/json { "sessionId": "sftp-12345678-1234-1234-1234-123456789abc", "oldPath": "/home/user/old.txt", "newPath": "/home/user/new.txt" } ``` **响应:** ```json { "success": true, "message": "重命名成功", "data": null } ``` #### 新建文件夹 **请求:** ``` POST /api/files/mkdir Content-Type: application/json { "sessionId": "local", "path": "C:/test/newfolder" } ``` **响应:** ```json { "success": true, "message": "创建成功", "data": null } ``` ## 11.3 错误码定义 ### 11.3.1 HTTP状态码 | 状态码 | 说明 | 使用场景 | |--------|------|---------| | 200 | OK | 请求成功 | | 201 | Created | 资源创建成功 | | 400 | Bad Request | 请求参数错误 | | 401 | Unauthorized | 未授权(连接失败) | | 403 | Forbidden | 权限不足 | | 404 | Not Found | 资源不存在 | | 500 | Internal Server Error | 服务器内部错误 | ### 11.3.2 业务错误码 ```java public enum ErrorCode { SUCCESS(0, "成功"), INVALID_PARAMETER(1001, "参数错误"), CONNECTION_NOT_FOUND(2001, "连接不存在"), CONNECTION_FAILED(2002, "连接失败"), FILE_NOT_FOUND(3001, "文件不存在"), FILE_ALREADY_EXISTS(3002, "文件已存在"), INSUFFICIENT_PERMISSION(3003, "权限不足"), DISK_SPACE_FULL(3004, "磁盘空间不足"), OPERATION_FAILED(5000, "操作失败"); private final int code; private final String message; ErrorCode(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public String getMessage() { return message; } } ``` ### 11.3.3 错误响应格式 ```json { "success": false, "message": "操作失败", "error": { "code": 3001, "message": "文件不存在", "details": "文件 /home/user/test.txt 不存在" } } ``` ## 11.4 API版本管理 ### 11.4.1 URL版本控制 ``` /api/v1/connection/connect /api/v2/connection/connect ``` ### 11.4.2 请求头版本控制 ``` GET /api/connection/list Accept: application/vnd.sftp-manager.v1+json ``` ### 11.4.3 查询参数版本控制 ``` GET /api/connection/list?version=1 ``` ## 11.5 API文档生成 ### 11.5.1 Swagger集成 ```xml org.springdoc springdoc-openapi-ui 1.6.14 ``` ### 11.5.2 Swagger配置 ```java @Configuration public class SwaggerConfig { @Bean public OpenAPI springShopOpenAPI() { return new OpenAPI() .info(new Info().title("SFTP文件管理系统API") .description("SFTP文件管理系统REST API文档") .version("v1.0.0")); } } ``` ### 11.5.3 注解示例 ```java @RestController @RequestMapping("/api/connection") @Tag(name = "连接管理", description = "SFTP连接相关接口") public class ConnectionController { @Operation(summary = "建立SFTP连接", description = "根据连接参数建立SFTP连接") @ApiResponses({ @ApiResponse(responseCode = "200", description = "连接成功"), @ApiResponse(responseCode = "400", description = "参数错误"), @ApiResponse(responseCode = "500", description = "连接失败") }) @PostMapping("/connect") public ApiResponse connect( @Parameter(description = "连接参数", required = true) @RequestBody ConnectionRequest request) { // 实现 } } ``` ## 11.6 API测试 ### 11.6.1 Postman测试 导入以下JSON到Postman: ```json { "info": { "name": "SFTP Manager API", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "item": [ { "name": "连接管理", "item": [ { "name": "建立SFTP连接", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json" } ], "url": { "raw": "{{baseUrl}}/api/connection/connect", "host": ["{{baseUrl}}"], "path": ["api", "connection", "connect"] }, "body": { "mode": "raw", "raw": "{\n \"name\": \"测试服务器\",\n \"host\": \"192.168.1.100\",\n \"port\": 22,\n \"username\": \"root\",\n \"password\": \"123456\"\n}" } } } ] } ] } ``` ### 11.6.2 curl测试 ```bash # 测试连接API curl -X POST http://localhost:8080/sftp-manager/api/connection/connect \ -H "Content-Type: application/json" \ -d '{"name":"测试","host":"192.168.1.100","port":22,"username":"root","password":"123456"}' # 测试文件列表API curl -X POST http://localhost:8080/sftp-manager/api/files/list \ -H "Content-Type: application/json" \ -d '{"sessionId":"local","path":"C:/Users"}' ``` ## 11.7 安全规范 ### 11.7.1 认证与会话 - **Session 管理**:连接建立后服务端维护 `sessionId`,前端在后续请求中携带 - **敏感字段**:密码、密钥口令等不入库明文,保存连接配置时使用加密存储 - **请求头**:可在 Header 中传递 `X-Session-Id` 作为会话标识(与 body 中的 sessionId 二选一或并存校验) ### 11.7.2 CORS 配置 ```java @Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOriginPatterns("http://localhost:*", "https://your-domain.com") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } } ``` ### 11.7.3 敏感数据脱敏 - 列表/详情接口返回连接信息时,不返回 `password`、`passPhrase` - 日志中禁止打印密码、密钥内容;错误信息可返回“认证失败”等笼统描述 --- ## 11.8 限流与性能建议 ### 11.8.1 建议限流策略 | 接口类型 | 建议限流 | |----------------|-----------------------------| | 连接/断开 | 每 IP 每分钟 10 次 | | 文件列表/信息 | 每 session 每秒 20 次 | | 上传/下载 | 按连接数 + 单文件大小限制 | | 删除/重命名 | 每 session 每秒 10 次 | ### 11.8.2 大文件与流式传输 - **上传**:使用 `multipart/form-data`,服务端流式写入,避免整文件进内存 - **下载**:使用 `InputStreamResource` 或 `StreamingResponseBody` 流式输出 - **服务器间传输**:服务端到服务端流式转发,不落本地盘(或使用临时缓冲区流式处理) --- ## 11.9 前端调用示例(Vue 3 + Axios) ### 11.9.1 封装 baseURL 与响应处理 ```typescript // api/client.ts import axios, { type AxiosInstance } from 'axios'; const api: AxiosInstance = axios.create({ baseURL: '/sftp-manager/api', timeout: 30000, headers: { 'Content-Type': 'application/json' }, }); api.interceptors.response.use( (res) => { const { success, data, message } = res.data ?? {}; if (!success) return Promise.reject(new Error(message || '请求失败')); return res.data; }, (err) => Promise.reject(err.response?.data?.message || err.message) ); export default api; ``` ### 11.9.2 连接管理示例 ```typescript // api/connection.ts import api from './client'; export interface ConnectParams { name: string; host: string; port: number; username: string; password?: string; privateKeyPath?: string; passPhrase?: string; rootPath?: string; } export function connect(params: ConnectParams) { return api.post<{ data: { sessionId: string } }>('/connection/connect', params); } export function disconnect(sessionId: string) { return api.post('/connection/disconnect', { sessionId }); } export function getConnectionList() { return api.get<{ data: Array<{ id: number; name: string; host: string; port: number; username: string }> }>('/connection/list'); } ``` ### 11.9.3 文件操作示例 ```typescript // api/files.ts import api from './client'; export interface FileItem { name: string; path: string; size: number; isDirectory: boolean; modifiedTime: string; permissions?: string; } export function listFiles(sessionId: string, path: string) { return api.post<{ data: Array }>('/files/list', { sessionId, path }); } export function uploadFile(sessionId: string, targetPath: string, file: File) { const form = new FormData(); form.append('file', file); form.append('targetSessionId', sessionId); form.append('targetPath', targetPath); return api.post('/files/upload', form, { headers: { 'Content-Type': 'multipart/form-data' }, }); } export function downloadFile(sessionId: string, path: string) { return api.get('/files/download', { params: { sessionId, path }, responseType: 'blob', }); } export function deleteFile(sessionId: string, path: string) { return api.delete('/files/delete', { params: { sessionId, path } }); } ``` --- ## 11.10 健康检查与就绪探针(可选) 便于容器化部署与负载均衡探测: | 路径 | 方法 | 说明 | |-------------------|------|----------------| | `/actuator/health` | GET | 应用存活检查 | | `/actuator/ready` | GET | 就绪(依赖就绪)| --- ## 注意事项 1. **统一响应格式**:所有API使用统一的响应格式 2. **错误处理**:提供详细的错误信息 3. **参数验证**:严格验证请求参数 4. **安全性**:敏感数据加密传输 5. **性能**:大文件使用流式传输 ## 下一步 完成模块11后,继续模块12:错误处理与日志