You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
8.8 KiB
8.8 KiB
BaseController 使用指南
BaseController 是项目的基础控制器类,提供了一套完整的 Web 开发常用功能,包括错误处理、参数验证、分页、权限检查等。所有控制器都应该继承此类以保持代码的一致性和可维护性。
特性概览
- 🛡️ 统一异常处理 - 自动捕获和格式化错误响应
- ✅ 参数验证 - 内置常用参数验证规则
- 📄 分页支持 - 标准化分页参数处理
- 🔐 权限控制 - 用户权限和资源所有权检查
- 📁 文件上传 - 文件上传处理助手
- 🎨 响应格式化 - 统一的 JSON 和视图响应
基本用法
1. 继承 BaseController
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 包装控制器方法,自动处理异常:
// 路由注册时使用 handleRequest
router.get('/', controller.handleRequest(controller.yourMethod))
// 控制器方法正常编写,无需 try-catch
async yourMethod(ctx) {
const data = await this.yourService.getData()
return this.success(ctx, data, \"获取数据成功\")
}
手动方式:如果需要自定义异常处理:
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)
生成成功响应
return this.success(ctx, { id: 1, name: 'test' }, \"操作成功\", 200)
// 响应: { success: true, data: {...}, error: \"操作成功\" }
error(ctx, message, data, statusCode)
生成错误响应
return this.error(ctx, \"数据不存在\", null, 404)
// 响应: { success: false, data: null, error: \"数据不存在\" }
paginated(ctx, paginationResult, message)
生成分页响应
const result = await this.service.getDataWithPagination(page, limit)
return this.paginated(ctx, result, \"获取列表成功\")
// 响应: { success: true, data: { list: [...], pagination: {...} } }
参数处理
validateParams(ctx, rules)
验证请求参数(支持 body、query、params)
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)
获取分页参数
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)
获取搜索参数
const params = this.getSearchParams(ctx)
// 从 query 中提取: keyword, status, category, author
权限控制
getCurrentUser(ctx)
获取当前登录用户
const user = this.getCurrentUser(ctx)
if (!user) {
throw new CommonError(\"用户未登录\")
}
checkPermission(ctx, permission)
检查用户权限(需要根据业务实现权限逻辑)
this.checkPermission(ctx, 'article.create')
checkOwnership(ctx, resource, ownerField)
检查资源所有权
const article = await this.articleService.getById(id)
this.checkOwnership(ctx, article, 'author') // 检查 article.author 是否为当前用户
视图和文件处理
render(ctx, template, data, options)
渲染视图模板
return this.render(ctx, 'articles/detail', {
article,
title: article.title
}, {
includeSite: true,
includeUser: true
})
redirect(ctx, url, message)
页面重定向
this.redirect(ctx, '/articles', '文章创建成功')
getUploadedFile(ctx, fieldName)
处理文件上传
const file = this.getUploadedFile(ctx, 'avatar')
if (file) {
console.log('文件名:', file.name)
console.log('文件大小:', file.size)
console.log('文件类型:', file.type)
console.log('文件路径:', file.path)
}
完整示例
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方法定义路由
通过遵循这些模式,您可以创建一致、可维护且健壮的控制器代码。