文章学习参考:

基础认知

控制器 (controller)

负责解析用户的输入,处理后返回相应的结果.

  • 接收、校验、处理 HTTP 请求参数
  • 向下调用服务(Service)处理业务
  • 通过 HTTP 将结果响应给用户
    1. ctx.queryURL 中的请求参数(忽略重复 key
    2. ctx.quriesURL 中的请求参数(重复的 key 被放入数组中)
    3. ctx.paramsRouter 上的命名参数
    4. ctx.request.bodyHTTP 请求体中的内容
    5. ctx.request.files:前端上传的文件对象
    6. ctx.getFileStream():获取上传的文件流
    7. ctx.multipart():获取 multipart/form-data 数据
    8. ctx.cookies:读取和设置 cookie
    9. ctx.session:读取和设置 session
    10. ctx.service.xxx:获取指定 service 对象的实例(懒加载)
    11. ctx.status:设置状态码
    12. ctx.body:设置响应体
    13. ctx.set:设置响应头
    14. ctx.redirect(url):重定向
    15. ctx.render(template):渲染模板

服务 service

具体业务逻辑的实现,封装好一个 service 之后 通过 controller 去调用即可。 需要注意的是 service 是懒加载的,只有当访问到它的时候框架才会去实例化它。

通常情况下,在 Service 中会做如下几件事情:

  • 处理复杂业务逻辑
  • 调用数据库或第三方服务(例如 GitHub 信息获取等)

image.png

post

  • 实际业务中,为了接口请求的安全性,和多数据请求参数,多类型请求方式,附件,图片上传等多采用post请求。

下面就介绍如何在Egg中使用post请求: 跨域等基础配置。

  1. // 安装跨域插件
  2. npm i egg-cors --save
  3. // 配置跨域插件 配置config下的plugin.js
  4. cors: {
  5. enable: true,
  6. package: 'egg-cors',
  7. },
  8. // 配置config下的config.default.js
  9. // 关闭crsf,开启跨域
  10. config.security = {
  11. csrf: {
  12. enable: false,
  13. },
  14. domainWhiteList: [ ],
  15. };
  16. // 允许跨域方法
  17. config.cors = {
  18. origin: '*',
  19. allowMethods: 'GET, PUT, POST, DELETE, PATCH',
  20. };

image.pngimage.png

到这里我们 post 请求就通了~

router

路由定义了 请求路径(URL) 和 控制器(Controller) 之间的映射关系. router.verb(‘path-match’, controllerAction)

如果有很多模块的话建议采用分组模式,添加 router 文件夹创建 各个业务块路由.js ,然后在 router.js 统一导入。
image.png

异常处理-中间件

默认 Egg 框架中返回的错误html页面,这既不友好,也不知道是为了什么报错。

避免重复造轮子

egg基于koa,这里我们没必要重复造轮子,可以使用社区一个优秀的轮子koa-json-err

  1. npm install --save koa-json-error
  2. // middlware/error.js
  3. module.exports = require('koa-json-error');
  4. // config/config.xxx.js中可以配置
  5. config.middleware = ['error'];
  6. config.error = {
  7. // 这里使用appInfo.env来判断环境,仅仅在非生产环境下打开堆栈信息,用于调试
  8. postFormat: (e, { stack, ...rest}) => appInfo.env === 'prod' ? rest: { stack, ...rest}
  9. }

跨域

image.png

jwt 鉴权

配置MySQL数据库 (进行中)

之前的文章都是静态的数据,要完成持久化的真实数据,就需要用到数据库,这里我们使用的是工作中使用最频繁的数据库MySQL,通过 egg-sequelize 插件,就可以像操作一些对象属性一样,完成业务的增删改查,语义化的操作,而不用手动的去写SQL。

在 Node.js 社区中,sequelize 是一个广泛使用的 ORM 框架。

model

首先需要定义表结构映射,这个是通过 Sequelize 插件来的,完了之后才能正常使用 sequelize 操作数据库的方法。


编写接口基础服务

入参:get \ post 统一转换

默认:

  1. // 1、params 获取参数方式
  2. router.get('/user/findById/:id', controller.user.info);
  3. const { ctx, params } = this;
  4. const userId = params;
  5. // 2、query
  6. router.get('/user/findById?id', controller.user.info);
  7. const { ctx } = this;
  8. const userId = ctx.query.id;

出参:统一输出模板函数

在 controller 中 const { ctx } = this; 重点关注 ctx

封装 controller 基类

  1. 由于 Controller 是类,因此可以通过自定义基类的方式封装常用方法,例如:

    1. // app/core/base_controller.js
    2. const { Controller } = require('egg');
    3. class BaseController extends Controller {
    4. get user() {
    5. return this.ctx.session.user;
    6. }
    7. success(data) {
    8. this.ctx.body = { success: true, data };
    9. }
    10. notFound(msg) {
    11. this.ctx.throw(404, msg || 'not found');
    12. }
    13. }
    14. module.exports = BaseController;
  2. 然后让所有 Controller 继承这个自定义的 BaseController:

    1. // app/controller/post.js
    2. const Controller = require('../core/base_controller');
    3. class PostController extends Controller {
    4. async list() {
    5. const posts = await this.service.listByUser(this.user);
    6. this.success(posts);
    7. }
    8. }

参数验证

对于一些客户端的参数,有必要进行一些校验,保证了系统的安全性和数据的可靠性

  1. // 1. 安装egg-valparams
  2. npm i egg-valparams --save
  3. // 2.配置
  4. // config/plugin.js
  5. valparams : {
  6. enable : true,
  7. package: 'egg-valparams'
  8. },
  9. // config/config.default.js
  10. config.valparams = {
  11. locale : 'zh-cn',
  12. throwError: true
  13. };
  14. // 3.
  15. // 创建单个用户
  16. async createUser() {
  17. const { ctx } = this;
  18. ctx.validate({
  19. username: { type: 'string', required: true, desc: '用户名' },
  20. password: { type: 'string', required: true, desc: '密码' },
  21. sex: { type: 'string', required: false, defValue: '男', desc: '性别' },
  22. });
  23. const { username, password } = ctx.request.body;
  24. const result = await ctx.model.User.create({ username, password });
  25. ctx.body = {
  26. code: 200,
  27. data: result,
  28. msg: '操作成功',
  29. };
  30. }