# BaseController 使用指南 `BaseController` 是项目的基础控制器类,提供了一套完整的 Web 开发常用功能,包括错误处理、参数验证、分页、权限检查等。所有控制器都应该继承此类以保持代码的一致性和可维护性。 ## 特性概览 - 🛡️ **统一异常处理** - 自动捕获和格式化错误响应 - ✅ **参数验证** - 内置常用参数验证规则 - 📄 **分页支持** - 标准化分页参数处理 - 🔐 **权限控制** - 用户权限和资源所有权检查 - 📁 **文件上传** - 文件上传处理助手 - 🎨 **响应格式化** - 统一的 JSON 和视图响应 ## 基本用法 ### 1. 继承 BaseController ```javascript import BaseController from \"@/base/BaseController.js\" import Router from \"utils/router.js\" import YourService from \"services/YourService.js\" class YourController extends BaseController { constructor() { super() // 必须调用 super() this.yourService = new YourService() } // 控制器方法 async yourMethod(ctx) { // 业务逻辑 } // 路由定义 static createRoutes() { const controller = new YourController() const router = new Router({ prefix: '/api/your-resource' }) router.get('/', controller.handleRequest(controller.yourMethod)) return router } } ``` ### 2. 使用异常处理装饰器 **推荐方式**:使用 `handleRequest` 包装控制器方法,自动处理异常: ```javascript // 路由注册时使用 handleRequest router.get('/', controller.handleRequest(controller.yourMethod)) // 控制器方法正常编写,无需 try-catch async yourMethod(ctx) { const data = await this.yourService.getData() return this.success(ctx, data, \"获取数据成功\") } ``` **手动方式**:如果需要自定义异常处理: ```javascript async yourMethod(ctx) { try { const data = await this.yourService.getData() return this.success(ctx, data, \"获取数据成功\") } catch (error) { if (error instanceof CommonError) { return this.error(ctx, error.message, null, 400) } return this.error(ctx, \"系统内部错误\", null, 500) } } ``` ## 核心方法详解 ### 响应方法 #### `success(ctx, data, message, statusCode)` 生成成功响应 ```javascript return this.success(ctx, { id: 1, name: 'test' }, \"操作成功\", 200) // 响应: { success: true, data: {...}, error: \"操作成功\" } ``` #### `error(ctx, message, data, statusCode)` 生成错误响应 ```javascript return this.error(ctx, \"数据不存在\", null, 404) // 响应: { success: false, data: null, error: \"数据不存在\" } ``` #### `paginated(ctx, paginationResult, message)` 生成分页响应 ```javascript const result = await this.service.getDataWithPagination(page, limit) return this.paginated(ctx, result, \"获取列表成功\") // 响应: { success: true, data: { list: [...], pagination: {...} } } ``` ### 参数处理 #### `validateParams(ctx, rules)` 验证请求参数(支持 body、query、params) ```javascript const data = this.validateParams(ctx, { title: { required: true, minLength: 1, maxLength: 100, label: '标题' }, email: { required: false, type: 'email', label: '邮箱' }, age: { required: true, type: 'number', label: '年龄' } }) ``` **验证规则说明**: - `required`: 是否必填 - `type`: 数据类型('number', 'email') - `minLength`/`maxLength`: 字符串长度限制 - `label`: 字段显示名称(用于错误消息) #### `getPaginationParams(ctx, defaults)` 获取分页参数 ```javascript const params = this.getPaginationParams(ctx, { page: 1, limit: 20, orderBy: 'created_at', order: 'desc' }) // 返回: { page: 1, limit: 20, orderBy: 'created_at', order: 'desc' } ``` #### `getSearchParams(ctx)` 获取搜索参数 ```javascript const params = this.getSearchParams(ctx) // 从 query 中提取: keyword, status, category, author ``` ### 权限控制 #### `getCurrentUser(ctx)` 获取当前登录用户 ```javascript const user = this.getCurrentUser(ctx) if (!user) { throw new CommonError(\"用户未登录\") } ``` #### `checkPermission(ctx, permission)` 检查用户权限(需要根据业务实现权限逻辑) ```javascript this.checkPermission(ctx, 'article.create') ``` #### `checkOwnership(ctx, resource, ownerField)` 检查资源所有权 ```javascript const article = await this.articleService.getById(id) this.checkOwnership(ctx, article, 'author') // 检查 article.author 是否为当前用户 ``` ### 视图和文件处理 #### `render(ctx, template, data, options)` 渲染视图模板 ```javascript return this.render(ctx, 'articles/detail', { article, title: article.title }, { includeSite: true, includeUser: true }) ``` #### `redirect(ctx, url, message)` 页面重定向 ```javascript this.redirect(ctx, '/articles', '文章创建成功') ``` #### `getUploadedFile(ctx, fieldName)` 处理文件上传 ```javascript const file = this.getUploadedFile(ctx, 'avatar') if (file) { console.log('文件名:', file.name) console.log('文件大小:', file.size) console.log('文件类型:', file.type) console.log('文件路径:', file.path) } ``` ## 完整示例 ```javascript import BaseController from \"@/base/BaseController.js\" import Router from \"utils/router.js\" import ArticleService from \"services/ArticleService.js\" class ArticleController extends BaseController { constructor() { super() this.articleService = new ArticleService() } // 获取文章列表(支持分页和搜索) async getArticles(ctx) { const searchParams = this.getSearchParams(ctx) const paginationParams = this.getPaginationParams(ctx) const result = await this.articleService.getArticlesWithPagination( paginationParams.page, paginationParams.limit, searchParams.status ) return this.paginated(ctx, result, \"获取文章列表成功\") } // 创建文章 async createArticle(ctx) { // 权限检查 this.checkPermission(ctx, 'article.create') // 参数验证 const data = this.validateParams(ctx, { title: { required: true, minLength: 1, maxLength: 200, label: '标题' }, content: { required: true, minLength: 10, label: '内容' } }) // 添加作者信息 const user = this.getCurrentUser(ctx) data.author = user.id const article = await this.articleService.createArticle(data) return this.success(ctx, article, \"创建文章成功\", 201) } // 更新文章 async updateArticle(ctx) { const { id } = this.validateParams(ctx, { id: { required: true, type: 'number', label: '文章ID' } }) // 检查文章是否存在和所有权 const article = await this.articleService.getArticleById(id) this.checkOwnership(ctx, article) // 验证更新数据 const updateData = this.validateParams(ctx, { title: { required: false, minLength: 1, maxLength: 200, label: '标题' }, content: { required: false, minLength: 10, label: '内容' } }) const updatedArticle = await this.articleService.updateArticle(id, updateData) return this.success(ctx, updatedArticle, \"更新文章成功\") } // 路由定义 static createRoutes() { const controller = new ArticleController() const router = new Router({ prefix: '/api/articles' }) router.get('/', controller.handleRequest(controller.getArticles), { auth: false }) router.post('/', controller.handleRequest(controller.createArticle), { auth: true }) router.put('/:id', controller.handleRequest(controller.updateArticle), { auth: true }) return router } } export default ArticleController ``` ## 最佳实践 ### 1. 统一错误处理 - ✅ 使用 `handleRequest` 包装所有控制器方法 - ✅ 业务异常抛出 `CommonError` - ✅ 让 BaseController 自动处理系统异常 ### 2. 参数验证 - ✅ 总是验证用户输入 - ✅ 使用有意义的字段标签 - ✅ 根据业务需求设置合适的验证规则 ### 3. 权限控制 - ✅ 在需要的地方调用 `checkPermission` - ✅ 对用户资源使用 `checkOwnership` - ✅ 在路由级别设置基础权限要求 ### 4. 响应格式 - ✅ 使用统一的响应方法 - ✅ 提供有意义的消息 - ✅ 分页数据使用 `paginated` 方法 ### 5. 代码组织 - ✅ 保持控制器方法简洁 - ✅ 业务逻辑放在 Service 层 - ✅ 使用静态 `createRoutes` 方法定义路由 通过遵循这些模式,您可以创建一致、可维护且健壮的控制器代码。