框架对象
框架对象
在Egg
框架中,包含了很多内置的基础对象,常用到的有:
- Application
- Context
- Request
- Response
- Controller
- Service
- Helper
- Config
- Logger
其中,从Koa框架中继承的对象有:
- Application
- Context
- Request
- Response
下面将会以官方文档为基础,加上自己的使用、学习体验进行一一整理说明。
Application对象
Application 是全局应用对象,在一个应用中,只会实例化一个,它继承自 Koa.Application,在上面我们可以挂载一些全局的方法和对象。
支持事件
在项目入口通过app.js来启动项目,在项目运行时,会在Application对象实例上触发一些事件,应用开发者或者插件开发者可以监听这些事件做一些操作,一般在app.js中进行监听
// app.js
module.exports = (app) => {
app.once('server', (server) => {
// websocket
})
app.on('error', (err, ctx) => {
// 监听错误信息
})
app.on('request', (ctx) => {
// 接收request对象日志
})
app.on('response', (ctx) => {
// 报告项目启动事件,其中ctx.start_time可以配置
const used = Date.now() - ctx.start_time
// 日志记录
})
}
server
: 该事件一个 worker 进程只会触发一次,在 HTTP 服务完成启动后,会将 HTTP server 通过这个事件暴露出来给开发者。error
: 运行时有任何的异常被 onerror 插件捕获后,都会触发 error 事件,将错误对象和关联的上下文(如果有)暴露给开发者,可以进行自定义的日志记录上报等处理。request 和 response
: 应用收到请求和响应请求时,分别会触发 request 和 response 事件,并将当前请求上下文暴露出来,开发者可以监听这两个事件来进行日志记录。
获取方式
Application 对象几乎可以在编写应用时的任何一个地方获取到;
- 在app.js中使用
// app.js
module.exports = (app) => {
// 绑定缓存redis
app.cache = new Cache()
}
**框架 Loader 加载的文件(Controller,Service,Schedule 等),都可以 export 一个函数,这个函数会被 Loader 调用,并使用 app 作为参数 **
- Controller中使用
// app/controller/user.js
class UserController extends Controller {
// 查询用户
async getUser() {
const { ctx, app } = this
const { id } = ctx.query
ctx.body = app.cache.get(id)
}
}
和 Koa 一样,在 Context 对象上,可以通过 ctx.app 访问到 Application 对象。上面的UserController文件也可以改为:
// app/controller/user.js
class UserController extends Controller {
async getUser() {
const { ctx, app } = this
const { id } = ctx.query
ctx.body = ctx.app.cache.get(id)
}
}
在继承于 Controller
, Service 基类的实例中,可以通过 this.app 访问到 Application 对象。
// app/service/user.js
class UserService extends Service {
async getUser(uid) {
const { ctx, app } = this
return app.cache.get(uid)
}
};
综上,访问application对象实例可以通过ctx.app或者this.app或者app参数即可。
Context对象
Context 是一个请求级别的对象,继承自 Koa.Context。在每一次收到用户请求时,框架会实例化一个 Context 对象,这个对象封装了这次用户请求的信息,并提供了许多便捷的方法来获取请求参数或者设置响应信息。框架会将所有的 Service 挂载到 Context 实例上. 如果你考虑封装一个插件,非常建议将一些方法和对象挂载到ctx对象上,这样在调用时就非常方便,就我个人而言,* *ctx的使用是明显高于app的;**
获取方式
- 中间件里获取
'use strict'
// 中间件
async function middleware(ctx, next) {
// 日志打印参数
console.log(ctx.query)
}
这里跟Koa框架是非常类似的,如果没有将ctx作为参数,context对象也是可以从this对象中获取;
- 支持Controller和Service里面获取
// 直接在this对象中拿到
const { ctx } = this
- 创建匿名Context实例
在有些非用户请求的场景下我们需要访问 service / model 等 Context 实例上的对象,可以通过 Application.createAnonymousContext() 方法创建一个匿名 Context 实例
// 例如:app.js中
module.exports=app=>{
app.beforeStart(async ()=>{
const ctx=app.createAnonymousContext()
// 使用ctx对象
....
})
}
注意,createAnonymousContext()函数是在application对象提供
- 定时任务中使用
在定时任务中的每一个 task 都接受一个 Context 实例作为参数,非常方便的帮助执行一些定时的业务逻辑
// app/schedule/refresh.js
exports.task = async (ctx) => {
// 使用ctx对象
}
Request和Response对象
这两个对象在项目中使用频率非常高,而且两者用法类似。
Request 是一个请求级别的对象 ,继承自 Koa.Request。封装了 Node.js 原生的 HTTP Request 对象,提供了一系列辅助方法获取 HTTP 请求常用参数。
Response 是一个请求级别的对象,继承自 Koa.Response。封装了 Node.js 原生的 HTTP Response 对象,提供了一系列辅助方法设置 HTTP 响应。
获取方式
可以在 Context 的实例上获取到当前请求的 Request(ctx.request) 和 Response(ctx.response) 实例
// app/controller/user.js
class UserController extends Controller {
async getUser() {
const { app, ctx } = this
// request对象获取参数
// 获取query参数 get请求
const { id } = ctx.request.query
// 获取表单数据 post请求
const { user_name } = ctx.request.body
// response对象设置响应
ctx.response.body = app.cache.get(id)
}
}
值得注意的时,Koa框架会在Context对象上面代理一部分Request和Response上方法和属性,因此:
ctx.request.query
和ctx.query
在使用上是等价的。ctx.response.body
和ctx.body
在使用上是等价的。
Controller对象
项目结构中的controller是继承框架中的Controller
基类实现的,当继承该对象,也就继承了对应的属性:
ctx
当前请求的 Context 实例。app
应用的 Application 实例。config
应用的配置。service
应用所有的 service。logger
为当前 controller 封装的 logger 对象。
获取框架提供的Controller
对象的方案:
- 方案一:从 egg模块上获取(推荐)
// app/controller/user.js
'use strict'
const Controller = require('egg').Controller;
// 这里可以用解构 const {Controller} =require('egg')
class UserController extends Controller {
// 构造函数
constructor(){
....
}
// 实际方法定义
}
module.exports = UserController;
- 方案二:从 app 实例上获取
// app/controller/user.js
'use strict'
module.exports = app => {
return class UserController extends app.Controller {
// 构造函数
constructor(){
....
}
// 实际方法定义
};
Service对象
和Controller
对象一样,框架也提供了Service对象,项目结构中的Service是继承框架中的Service基类实现的,当继承该对象,也就继承了对应的属性,两者的共性非常多,用法基本一致;
获取框架提供的Service对象的方案:
- 方案一:从 egg模块上获取(推荐)
// app/service/user.js
'use strict'
const Service = require('egg').Service;
// 这里可以用解构 const {Controller} =require('egg')
class UserService extends Service {
// 构造函数
constructor(){
....
}
// 实际方法定义
}
module.exports = UserService;
- 方案二:从 app 实例上获取
// app/service/user.js
'use strict'
module.exports = app => {
return class UserService extends app.Service {
// 构造函数
constructor(){
....
}
// 实际方法定义
};
Helper对象
Helper 用来提供一些实用的 utility 函数。作用在于我们可以将一些常用的动作抽离在 helper.js 里面成为一个独立的函数,这样可以用 JavaScript 来写复杂的逻辑,避免逻辑分散各处,同时可以更好的编写测试用例。不通的处理;
**Helper 自身是一个类,有和 Controller 基类一样的属性,它也会在每次请求时进行实例化,因此 Helper 上的所有函数也能获取到当前请求相关的ctx上下文信息 **
获取方式
可以在 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.js文件的功能主要提供一些常用工具函数的封装,所以除了系统自带的,更多的使用场景是进行方法自定义,比如:常用正则校验、长度检测、排序处理等;
// app/extend/helper.js
module.exports = {
// 封装统一返回
returnFormat(code, message, result) {
return { code, message, result }
}
}
另外,像ctx.helper.escape()
也是非常常用的,用于防止xss攻击,对标签进行序列化;
Config对象
框架提供了强大且可扩展的配置功能,可以自动合并应用、插件、框架的配置,按顺序覆盖,且可以根据环境维护不同的配置。合并后的配置可直接从app.config
获取
获取方式
app.config 从 Application 实例上获取到 config 对象
也可以在 Controller, Service, Helper 的实例上通过 this.config 获取到 config 对象
除了框架自带的配置,其他自定义的配置,也是可以初始化到Config对象中的
Logger对象
框架内置了功能强大的日志功能,可以非常方便的打印各种级别的日志到对应的日志文件中,每一个 logger 对象都提供了 4 个级别的方法
logger.debug()
logger.info()
logger.warn()
logger.error()
Logger对象根据不同的使用场景,主要有:
- App Logger
通过 app.logger 来获取到它,如果我们想做一些应用级别的日志记录,如记录启动阶段的一些数据信息,记录一些业务上与请求无关的信息,都可以通过 App Logger 来完成。
- App CoreLogger
可以通过 app.coreLogger 来获取到它,一般我们在开发应用时都不应该通过 CoreLogger 打印日志,而框架和插件则需要通过它来打印应用级别的日志,这样可以更清晰的区分应用和框架打印的日志,通过 CoreLogger 打印的日志会放到和 Logger 不同的文件中。
- Context Logger
可以通过 ctx.logger 从 Context 实例上获取到它,从访问方式上我们可以看出来,Context Logger 一定是与请求相关的,它打印的日志都会在前面带上一些当前请求相关的信息(如 [$userId/$ip/$traceId/${cost}ms $method $url] ),通过这些信息,我们可以从日志快速定位请求,并串联一次请求中的所有的日志。。
- Context CoreLogger
可以通过 ctx.coreLogger 获取到它,和 Context Logger 的区别是一般只有插件和框架会通过它来记录日志。
- Controller Logger & Service Logger
可以在 Controller 和 Service 实例上通过 this.logger 获取到它们,它们本质上就是一个 Context Logger,不过在打印日志的时候还会额外的加上文件路径,方便定位日志的打印位置。。
定时任务
除了上面介绍的几大对象外,框架还专门提供了处理定时任务的方案——subscription
订阅模型是一种比较常见的开发模式,譬如消息中间件的消费者或调度任务。因此提供了 Subscription 基类来规范化这个模式
简单的定时任务:
// 引用 Subscription 基类:
const { Subscription } = require('egg')
class Schedule extends Subscription {
// 需要实现此方法,方法里面可以调用其他封装
async subscribe() {}
}