-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: add document for built-in objects (#914)
- Loading branch information
1 parent
6180d5d
commit fd04ea2
Showing
4 changed files
with
281 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,278 @@ | ||
title: 框架内置基础对象 | ||
--- | ||
|
||
在往下阅读之前,我们先初步介绍一下框架中内置的一些基础对象,包括从 [Koa] 继承而来的 4 个对象(Application, Context, Request, Response) 以及框架扩展的一些对象(Controller, Service, Helper, Config, Logger),在后续的文档阅读中我们会经常遇到它们。 | ||
|
||
## Application | ||
|
||
Application 是全局应用对象,在一个应用中,只会实例化一个,它继承自 [koa.Application],在它上面我们可以挂载一些全局的方法和对象。我们可以轻松的在插件或者应用中[扩展 Application 对象](./extend.md#Application)。 | ||
|
||
### 获取方式 | ||
|
||
Application 对象几乎可以在编写应用时的任何一个地方获取到,下面介绍几个经常用到的获取方式: | ||
|
||
几乎所有被框架 [Loader](../advanced/loader.md) 加载的文件(Controller,Service,Schedule 等),都可以 export 一个函数,这个函数会被 Loader 调用,并使用 app 作为参数: | ||
|
||
- [启动自定义脚本](./app-start.md) | ||
|
||
```js | ||
// app.js | ||
module.exports = app => { | ||
app.cache = new Cache(); | ||
}; | ||
``` | ||
|
||
- [Controller 文件](./controller.md) | ||
|
||
```js | ||
// app/controller/user.js | ||
module.exports = app => { | ||
return class UserController extends app.Controller { | ||
*fetch () { | ||
this.ctx.body = app.cache.get(this.query.id); | ||
} | ||
}; | ||
}; | ||
``` | ||
|
||
和 [Koa] 一样,在 Context 对象上,可以通过 `ctx.app` 访问到 Application 对象。以上面的 Controller 文件举例: | ||
|
||
```js | ||
// app/controller/user.js | ||
module.exports = app => { | ||
return class UserController extends app.Controller { | ||
*fetch () { | ||
this.ctx.body = this.ctx.app.cache.get(this.query.id); | ||
} | ||
}; | ||
}; | ||
``` | ||
|
||
在继承于 Controller, Service 基类的实例中,可以通过 `this.app` 访问到 Application 对象。 | ||
|
||
```js | ||
// app/controller/user.js | ||
module.exports = app => { | ||
return class UserController extends app.Controller { | ||
*fetch () { | ||
this.ctx.body = this.app.cache.get(this.query.id); | ||
} | ||
}; | ||
}; | ||
``` | ||
|
||
## Context | ||
|
||
Context 是一个**请求级别的对象**,继承自 [koa.Context]。在每一次收到用户请求时,框架会实例化一个 Context 对象,这个对象封装了这次用户请求的信息,并提供了许多便捷的方法来获取请求参数或者设置响应信息。框架会将所有的 [Service] 挂载到 Context 实例上,一些插件也会将一些其他的方法和对象挂载到它上面([egg-sequelize] 会将所有的 model 挂载在 Context 上。 | ||
|
||
### 获取方式 | ||
|
||
最常见的 Context 实例获取方式是在 [Middleware], [Controller] 以及 [Service] 中。[Controller] 中的获取方式在上面的例子中已经展示过了,在 [Service] 中获取和 [Controller] 中获取的方式一样,在 [Middleware] 中获取 Context 实例则和 [Koa] 框架在中间件中获取 Context 对象的方式一致。 | ||
|
||
框架的 [Middleware] 同时支持 koa v1 和 koa v2 两种不同的中间件写法,根据不同的写法,获取 Context 实例的方式也稍有不同: | ||
|
||
```js | ||
// 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); | ||
} | ||
``` | ||
|
||
除了在请求时可以获取 Context 实例之外, 在有些非用户请求的场景下我们需要访问 service / model 等 Context 实例上的对象,我们可以通过 `Application.createAnonymousContext()` 方法创建一个匿名 Context 实例: | ||
|
||
```js | ||
// app.js | ||
module.exports = app => { | ||
app.beforeStart(function* () { | ||
const ctx = app.createAnonymousContext(); | ||
// preload before app start | ||
yield ctx.service.posts.load(); | ||
}); | ||
} | ||
``` | ||
|
||
在[定时任务](./schedule.md)中的每一个 task 都接受一个 Context 实例作为参数,以便我们更方便的执行一些定时的业务逻辑: | ||
|
||
```js | ||
// app/schedule/refresh.js | ||
exports.task = function* (ctx) { | ||
yield ctx.service.posts.refresh(); | ||
}; | ||
``` | ||
|
||
## Request & Response | ||
|
||
Request 是一个**请求级别的对象**,继承自 [koa.Request]。封装了 node 原生的 http request 对象,提供了一系列辅助方法获取 HTTP 请求常用参数。 | ||
|
||
Response 是一个**请求级别的对象**,继承自 [koa.Response]。封装了 node 原生的 http response 对象,提供了一系列辅助方法设置 HTTP 响应。 | ||
|
||
### 获取方式 | ||
|
||
可以在 Context 的实例上获取到当前请求的 Request(`ctx.request`) 和 Response(`ctx.response`) 实例。 | ||
|
||
```js | ||
// app/controller/user.js | ||
module.exports = app => { | ||
return class UserController extends app.Controller { | ||
*fetch () { | ||
const { app, ctx } = this; | ||
const id = ctx.request.query.id; | ||
ctx.response.body = app.cache.get(id); | ||
} | ||
}; | ||
}; | ||
``` | ||
|
||
在上面的例子中,`ctx.request.query.id` 和 `ctx.query.id` 是等价的,`ctx.response.body=` 和 `ctx.body=` 是等价的,[Koa] 系列的框架会在 Context 上代理一部分 Request 和 Response 上的方法和属性。 | ||
|
||
## Controller | ||
|
||
框架提供了一个 Controller 基类,并推荐所有的 [Controller] 都继承于该基类实现。这个 Controller 基类有下列属性: | ||
|
||
- `ctx` - 当前请求的 [Context](#context) 实例。 | ||
- `app` - 应用的 [Application](#application) 实例。 | ||
- `config` - 应用的[配置](./config.md)。 | ||
- `service` - 应用所有的 [service](./service.md)。 | ||
- `logger` - 为当前 controller 封装的 logger 对象。 | ||
|
||
在 Controller 文件中,可以通过两种方式来引用 Controller 基类: | ||
|
||
```js | ||
// app/controller/user.js | ||
|
||
// 从 egg 上获取 | ||
const Controller = require('egg'); | ||
module.exports = UserController extends egg.Controller { | ||
// implement | ||
}; | ||
|
||
// 从 app 实例上获取 | ||
module.exports = app => { | ||
return UserController extends app.Controller { | ||
// implement | ||
}; | ||
}; | ||
``` | ||
|
||
## Service | ||
|
||
框架提供了一个 Service 基类,并推荐所有的 [Service] 都继承于该基类实现。 | ||
|
||
Service 基类的属性和 [Controller](#controller) 基类属性一致,访问方式也类似: | ||
|
||
```js | ||
// app/service/user.js | ||
|
||
// 从 egg 上获取 | ||
module.exports = UserService extends require('egg').Service { | ||
// implement | ||
}; | ||
|
||
// 从 app 实例上获取 | ||
module.exports = app => { | ||
return UserService extends app.Service { | ||
// implement | ||
}; | ||
}; | ||
``` | ||
|
||
## Helper | ||
|
||
Helper 用来提供一些实用的 utility 函数。它的作用在于我们可以将一些常用的动作抽离在 helper.js 里面成为一个独立的函数,这样可以用 JavaScript 来写复杂的逻辑,避免逻辑分散各处,同时可以更好的编写测试用例。 | ||
|
||
Helper 自身是一个类,有和 [Controller](#controller) 基类一样的属性,它也会在每次请求时进行实例化,因此 Helper 上的所有函数也能获取到当前请求相关的上下文信息。 | ||
|
||
### 获取方式 | ||
|
||
可以在 Context 的实例上获取到当前请求的 Helper(`ctx.helper`) 实例。 | ||
|
||
```js | ||
// app/controller/user.js | ||
module.exports = app => { | ||
return class UserController extends app.Controller { | ||
*fetch () { | ||
const { app, ctx } = this; | ||
const id = ctx.request.query.id; | ||
const user = app.cache.get(id); | ||
ctx.body = ctx.helper.formatUser(user); | ||
} | ||
}; | ||
}; | ||
``` | ||
|
||
除此之外,Helper 的实例还可以在模板中获取到,例如可以在模板中获取到 [security](../core/security.md) 插件提供的 `shtml` 方法。 | ||
|
||
``` | ||
// app/view/home.nj | ||
{{ helper.shtml(value) }} | ||
``` | ||
|
||
### 自定义 helper 方法 | ||
|
||
应用开发中,我们可能经常要自定义一些 helper 方法,例如上面例子中的 `formatUser`,我们可以通过[框架扩展](./extend.md#helper)的形式来自定义 helper 方法。 | ||
|
||
```js | ||
// app/extend/helper.js | ||
module.exports = { | ||
formatUser(user) { | ||
return only(user, [ 'name', 'phone' ]); | ||
} | ||
}; | ||
``` | ||
|
||
## Config | ||
|
||
我们推荐应用开发遵循配置和代码分离的原则,将一些需要硬编码的业务配置都放到配置文件中,同时配置文件支持各个不同的运行环境使用不同的配置,使用起来也非常方便,所有框架、插件和应用级别的配置都可以通过 Config 对象获取到,关于框架的配置,可以详细阅读 [Config 配置](./config.md)章节。 | ||
|
||
### 获取方式 | ||
|
||
我们可以通过 `app.config` 从 Application 实例上获取到 config 对象,也可以在 Controller, Service, Helper 的实例上通过 `this.config` 获取到 config 对象。 | ||
|
||
## Logger | ||
|
||
框架内置了功能强大的[日志功能](../core/logger.md),可以非常方便的打印各种级别的日志到对应的日志文件中,每一个 logger 对象都提供了 5 个级别的方法: | ||
|
||
- `logger.debug()` | ||
- `logger.info()` | ||
- `logger.warn()` | ||
- `logger.error()` | ||
|
||
在框架中提供了多个 Logger 对象,下面我们简单的介绍一下各个 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,不过在打印日志的时候还会额外的加上文件路径,方便定位日志的打印位置。 | ||
|
||
[Koa]: http://koajs.com | ||
[koa.Application]: http://koajs.com/#application | ||
[koa.Context]: http://koajs.com/#context | ||
[koa.Request]: http://koajs.com/#request | ||
[koa.Response]: http://koajs.com/#response | ||
[egg-sequelize]: https://github.com/eggjs/egg-sequelize | ||
[Middleware]: ./middleware.md | ||
[Controller]: ./controller.md | ||
[Service]: ./service.md |