Application

Application 是全局应用对象,在一个应用中,只会实例化一个,它继承自 Koa.Application,在它上面可以挂载一些全局的方法和对象。可以轻松的在插件或者应用中扩展 Application 对象;

事件

  1. 在框架运行时,会在 Application 实例上触发一些事件,应用开发者或者插件开发者可以监听这些事件做一些操作;作为应用开发者,我们一般会在启动自定义脚本中进行监听;
    • server: 该事件一个 worker 进程只会触发一次,在 HTTP 服务完成启动后,会将 HTTP server 通过这个事件暴露出来给开发者;
    • error: 运行时有任何的异常被 onerror 插件捕获后,都会触发 error 事件,将错误对象和关联的上下文(如果有)暴露给开发者,可以进行自定义的日志记录上报等处理;
    • request 和 response: 应用收到请求和响应请求时,分别会触发 request 和 response 事件,并将当前请求上下文暴露出来,开发者可以监听这两个事件来进行日志记录;
  2. 示例代码:
    // app.js
    module.exports = app => {
      app.once('server', server => {
        // websocket
      });
    
      app.on('error', (err, ctx) => {
        // report error
      });
    
      app.on('request', ctx => {
        // log receive request
      });
    
      app.on('response', ctx => {
        // ctx.starttime is set by framework
        const used = Date.now() - ctx.starttime;
        // log total cost
      });
    };
    

获取方式

  1. 几乎所有被框架 Loader 加载的文件(Controller,Service,Schedule 等),都可以 export 一个函数,这个函数会被 Loader 调用,并使用 app 作为参数:
    // app.js 启动自定义脚本
    module.exports = (app) => {
        app.cache = new Cache();
    };
    
  2. 和 Koa 一样,在 Context 对象上,可以通过 ctx.app 访问到 Application 对象:
    // app/controller/user.js
    class UserController extends Controller {
      async fetch() {
        this.ctx.body = this.ctx.app.cache.get(this.ctx.query.id);
      }
    }
    
  3. 在继承于 Controller, Service 基类的实例中,可以通过 this.app 访问到 Application 对象:
    // app/controller/user.js
    class UserController extends Controller {
      async fetch() {
        this.ctx.body = this.app.cache.get(this.ctx.query.id);
      }
    }
    

Context

Context 是一个请求级别的对象,继承自 Koa.Context

  1. 在每一次收到用户请求时,框架会实例化一个 Context 对象,这个对象封装了这次用户请求的信息,并提供了许多便捷的方法来获取请求参数或者设置响应信息;
  2. 框架会将所有的 Service 挂载到 Context 实例上,一些插件也会将一些其他的方法和对象挂载到它上面(egg-sequelize 会将所有的 model 挂载在 Context 上);

获取方式

  1. 最常见的 Context 实例获取方式是在 Middleware、Controller 以及 Service 中:

    Controller 中的获取方式在之前的例子中已经展示过了,在 Service 中获取和 Controller 中获取的方式一样,在 Middleware 中获取 Context 实例则和 Koa 框架在中间件中获取 Context 对象的方式一致;

  2. 框架的 Middleware 同时支持 Koa v1 和 Koa v2 两种不同的中间件写法,根据不同的写法,获取 Context 实例的方式也稍有不同:
    // Koa v1
    function* middleware(next) {
      // this is instance of Context
      console.log(this.query);
      yield next;
    }
    
    // Koa v2
    async function middleware(ctx, next) {
      // ctx is instance of Context
      console.log(ctx.query);
    }
    
  3. 除了在请求时可以获取 Context 实例之外, 在有些非用户请求的场景下需要访问 service / model 等 Context 实例上的对象,可以通过 Application.createAnonymousContext() 方法创建一个匿名 Context 实例:
    // app.js
    module.exports = (app) => {
      app.beforeStart(async () => {
        const ctx = app.createAnonymousContext();
        // preload before app start
        await ctx.service.posts.load();
      });
    };
    
  4. 在定时任务中的每一个 task 都接受一个 Context 实例作为参数,以便我们更方便的执行一些定时的业务逻辑:
      // app/schedule/refresh.js
    exports.task = async (ctx) => {
      await ctx.service.posts.refresh();
    };
    

Request & Response

Request & Response

  1. Request 是一个请求级别的对象,继承自 Koa.Request,封装了 Node.js 原生的 HTTP Request 对象,提供了一系列辅助方法获取 HTTP 请求常用参数;
  2. Response 是一个请求级别的对象,继承自 Koa.Response,封装了 Node.js 原生的 HTTP Response 对象,提供了一系列辅助方法设置 HTTP 响应;

获取方式

  1. 可以在 Context 的实例上获取到当前请求的 Request(ctx.request) 和 Response(ctx.response) 实例:
    // app/controller/user.js
    class UserController extends Controller {
      async fetch() {
        const { app, ctx } = this;
        // ctx.request.query.id 和 ctx.query.id 是等价的
        const id = ctx.request.query.id;
        // ctx.response.body= 和 ctx.body= 是等价的
        ctx.response.body = app.cache.get(id);
      }
    }
    
  2. 需要注意的是,获取 POST 的 body 应该使用 ctx.request.body,而不是 ctx.body;

Controller

  1. 框架提供了一个 Controller 基类,并推荐所有的 Controller 都继承于该基类实现,这个 Controller 基类有下列属性:
    • ctx - 当前请求的 Context 实例;
    • app - 应用的 Application 实例;
    • config - 应用的配置;
    • service - 应用所有的 service;
    • logger - 为当前 controller 封装的 logger 对象;
  2. 在 Controller 文件中,可以通过两种方式来引用 Controller 基类
    // app/controller/user.js
    // 从 egg 上获取(推荐)
    const Controller = require('egg').Controller;
    class UserController extends Controller {
      // implement
    }
    module.exports = UserController;
    
    // 从 app 实例上获取
    module.exports = (app) => {
      return class UserController extends app.Controller {
        // implement
      };
    };
    

Service

  1. 框架提供了一个 Service 基类,并推荐所有的 Service 都继承于该基类实现;
  2. Service 基类的属性和 Controller 基类属性一致,访问方式也类似:
    // app/service/user.js
    // 从 egg 上获取(推荐)
    const Service = require('egg').Service;
    class UserService extends Service {
      // implement
    }
    module.exports = UserService;
    
    // 从 app 实例上获取
    module.exports = (app) => {
      return class UserService extends app.Service {
        // implement
      };
    };
    

Helper

  1. Helper 用来提供一些实用的 utility 函数:
    • 它的作用在于可以将一些常用的动作抽离在 helper.js 里面成为一个独立的函数,这样可以用 JavaScript 来写复杂的逻辑,避免逻辑分散各处,同时可以更好的编写测试用例;
    • Helper 自身是一个类,有和 Controller 基类一样的属性,它也会在每次请求时进行实例化,因此 Helper 上的所有函数也能获取到当前请求相关的上下文信息;
  2. 获取方式
    • 可以在 Context 的实例上获取到当前请求的 Helper(ctx.helper) 实例:
      // app/controller/user.js
      class UserController extends Controller {
        async fetch() {
          const { app, ctx } = this;
          const id = ctx.query.id;
          const user = app.cache.get(id);
          ctx.body = ctx.helper.formatUser(user);
        }
      }
      
    • 除此之外,Helper 的实例还可以在模板中获取到,例如可以在模板中获取到 security 插件提供的 shtml 方法:
      // app/view/home.nj
      {{ helper.shtml(value) }}
      
  3. 自定义 helper 方法
    • 应用开发中,可能经常要自定义一些 helper 方法,例如上面例子中的 formatUser,我们可以通过框架扩展的形式来自定义 helper 方法:
      // app/extend/helper.js
      module.exports = {
        formatUser(user) {
          return only(user, ['name', 'phone']);
        },
      };
      

Config

  1. 推荐应用开发遵循配置和代码分离的原则:

    • 将一些需要硬编码的业务配置都放到配置文件中,同时配置文件支持各个不同的运行环境使用不同的配置,使用起来也非常方便;
    • 所有框架、插件和应用级别的配置都可以通过 Config 对象获取到;
  2. 获取方式:

    • 通过 app.config 从 Application 实例上获取到 config 对象;
    • 也可以在 Controller, Service, Helper 的实例上通过 this.config 获取到 config 对象;

Logger

框架内置了功能强大的日志功能,可以非常方便的打印各种级别的日志到对应的日志文件中,每一个 logger 对象都提供了 4 个级别的方法:

  • logger.debug()
  • logger.info()
  • logger.warn()
  • logger.error()

打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

中午好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于产品、设计、开发的问题和看法,还有技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程

了解更多

目录

  1. 1. Application
    1. 1.1. 事件
    2. 1.2. 获取方式
  2. 2. Context
    1. 2.1. Context 是一个请求级别的对象,继承自 Koa.Context
    2. 2.2. 获取方式
  3. 3. Request & Response
    1. 3.1. Request & Response
    2. 3.2. 获取方式
  4. 4. Controller
  5. 5. Service
  6. 6. Helper
  7. 7. Config
  8. 8. Logger