diff --git a/docs/_config.yml b/docs/_config.yml index 748d1769a4..37036bca26 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,4 +1,4 @@ -title: egg +title: Egg.js subtitle: "Born to build better enterprise frameworks and apps" description: "Born to build better enterprise frameworks and apps" language: diff --git a/docs/source/_data/guide_toc.yml b/docs/source/_data/guide_toc.yml index 2ce3f3781d..23e4f3982c 100644 --- a/docs/source/_data/guide_toc.yml +++ b/docs/source/_data/guide_toc.yml @@ -1,5 +1,5 @@ Intro: - What is egg: /intro/index.html + What is Egg: /intro/index.html Egg and Koa: /intro/egg-and-koa.html Get Start: /intro/quickstart.html Basics: diff --git a/docs/source/en/basics/env.md b/docs/source/en/basics/env.md index 5fbe8545e5..dd58783099 100644 --- a/docs/source/en/basics/env.md +++ b/docs/source/en/basics/env.md @@ -3,7 +3,7 @@ title: Runtime Environment # Runtime Environment -There could be all kinds of difference during various stages of a web application development, but the application itself should be stateless, so EGG provide environment variables to cope with such difference. +There could be all kinds of difference during various stages of a Web application development, but the application itself should be stateless, so EGG provide environment variables to cope with such difference. EGG framework provides a variable named `env` for setting up the runtime environment. The `env` could be used to determine which configuration file should be applied, or you can perform any operations by detecting the `env` directly. @@ -27,7 +27,7 @@ You can also load different configuration file for different environment by addi ## Difference with NODE_ENV -Lots of node applications use `NODE_ENV` for environment setting, but `EGG_SERVER_ENV` distinguishes the environments much more specific. Generally speaking, there are local environment, unit test environment, test environment, production environment during the application development. Test and production environment are **Server Environment** and their `NODE_ENV` should be set to `production` , just like how npm make use of `NODE_ENV`. when you depoly applications in test and production environment, you don't install the devDependencies, so `production` should be applied. +Lots of Node.js applications use `NODE_ENV` for environment setting, but `EGG_SERVER_ENV` distinguishes the environments much more specific. Generally speaking, there are local environment, unit test environment, test environment, production environment during the application development. Test and production environment are **Server Environment** and their `NODE_ENV` should be set to `production` , just like how npm make use of `NODE_ENV`. when you depoly applications in test and production environment, you don't install the devDependencies, so `production` should be applied. Default mapping of `EGG_SERVER_ENV` and `NODE_ENV` (will generate `EGG_SERVER_ENV` from `NODE_ENV` setting if `EGG_SERVER_ENV` is not specified) @@ -47,4 +47,4 @@ Set `EGG_SERVER_ENV` to `sit` (also set `NODE_ENV = production` as recommend), t ## Difference with Koa -We are using `app.env` to distinguishes the environments in koa, and `app.env` defualt to `process.env.NODE_ENV`. But in egg (and frameworks base on egg), we put all the configurations in `app.config`, so we should use `app.config.env` to distinguishes the environments, `app.env` is no logger used. +We are using `app.env` to distinguishes the environments in Koa, and `app.env` defualt to `process.env.NODE_ENV`. But in Egg (and frameworks base on Egg), we put all the configurations in `app.config`, so we should use `app.config.env` to distinguishes the environments, `app.env` is no logger used. diff --git a/docs/source/en/faq.md b/docs/source/en/faq.md index 2ed71608b4..aa7fd86d56 100644 --- a/docs/source/en/faq.md +++ b/docs/source/en/faq.md @@ -1,7 +1,7 @@ title: FAQ --- -If you have questions that is not contained below, please check [egg issues](https://github.com/eggjs/egg/issues). +If you have questions that is not contained below, please check [Egg issues](https://github.com/eggjs/egg/issues). ## Why my config don't work ? @@ -47,6 +47,6 @@ There are two kinds of common csrf errors: - `missing csrf token` - `invalid csrf token` -By default [egg-security](https://github.com/eggjs/egg-security/) plugin built in egg requires CSRF validation against all 'unsafe' request such as `POST`, `PUT`, `DELETE` requests. +By default [egg-security](https://github.com/eggjs/egg-security/) plugin built in Egg requires CSRF validation against all 'unsafe' request such as `POST`, `PUT`, `DELETE` requests. The error will disappear in the presence of correct csrf token in request. For more implentation details, see [./core/security.md#csrf]. diff --git a/docs/source/en/intro/egg-and-koa.md b/docs/source/en/intro/egg-and-koa.md index a462a0a0c0..bb7c82e364 100644 --- a/docs/source/en/intro/egg-and-koa.md +++ b/docs/source/en/intro/egg-and-koa.md @@ -1,4 +1,4 @@ -title: egg and koa +title: Egg and Koa --- ## Asynchronous programming model @@ -6,13 +6,13 @@ title: egg and koa Node.js is an asynchronous world, asynchronous programming models in official API support are all in callback form ,it brings many problems. For example: - [callback hell](http://callbackhell.com/): Notorious "callback hell"。 -- [release zalgo](https://oren.github.io/blog/zalgo.html): Asynchronous functions may call callback function response data synchronously which would bring inconsistency. +- [release zalgo](https://oren.github.io/blog/zalgo.html): Asynchronous functions may call callback function response data synchronously which would bring inconsistency. The community has provided many solutions for the problems, the winner is Promise, it is built into ECMAScript 2015. On the basis of Promise, and Generator with the ability to switch context, we can write asynchronous code in synchronous way with [co] and other third party libraries. Meanwhile [async function], the official solution has been finalized, and will be published in ECMAScript 2017. ### Generator and co -#### Asynchronous programming model in synchronous way on basis of Generator and Promise. +#### Asynchronous programming model in synchronous way on basis of Generator and Promise. As metioned before, we can write asynchronous code in synchronous way with the help of Generator and Promise. The most popular library implementing this feature is [co]. the core principle of [co] can be described by lines of code below: @@ -50,7 +50,7 @@ run(main()); With the run function, asynchronous code can be written in synchronous way in main function in the example above. If you want to know more about Generator, you can have a look at [this document](https://github.com/dead-horse/koa-step-by-step#generator) -Compared with the run function, [co] has `yield [Object / Array / thunk / Generator Function / Generator]`, and builds a Promise with wrapping a Generator Function. [co] is also the underlying library of koa 1 providing the asynchronous feature. Every middleware in koa 1 must be a `generator function`. +Compared with the run function, [co] has `yield [Object / Array / thunk / Generator Function / Generator]`, and builds a Promise with wrapping a Generator Function. [co] is also the underlying library of Koa 1 providing the asynchronous feature. Every middleware in Koa 1 must be a `generator function`. ### Async function @@ -76,17 +76,17 @@ fn().then(res => console.log(res)).catch(err => console.error(err.stack)); Other than the features supported by [co], async function can not await a `Promise` array (You can wrap it with `Promise.all`), await `thunk` is not avaliable either. -Though async function has not been published with the spec yet, it is supported in the V8 runtime built in node 7.x, you can use it without flag parameter after 7.6.0 version. +Though async function has not been published with the spec yet, it is supported in the V8 runtime built in Node.js 7.x, you can use it without flag parameter after 7.6.0 version. ## Koa -> Koa is a new web framework designed by the team behind Express, which aims to be a smaller, more expressive, and more robust foundation for web applications and APIs. +> Koa is a new Web framework designed by the team behind Express, which aims to be a smaller, more expressive, and more robust foundation for Web applications and APIs. -The design style of koa and express are very similar, The underlying base library is the same, [HTTP library](https://github.com/jshttp). There are several significant differences between them. Besides the asynchronous solution by default metioned above, there are the following points. +The design style of Koa and Express are very similar, The underlying base library is the same, [HTTP library](https://github.com/jshttp). There are several significant differences between them. Besides the asynchronous solution by default metioned above, there are the following points. ### Midlleware -The middleware in koa is different from express, koa use the onion model: +The middleware in Koa is different from Express, Koa use the onion model: - Middleware onion diagram: @@ -96,16 +96,16 @@ The middleware in koa is different from express, koa use the onion model: ![](https://raw.githubusercontent.com/koajs/koa/a7b6ed0529a58112bac4171e4729b8760a34ab8b/docs/middleware.gif) -All the requests will be executed twice during one middleware. Compared to express middleware, it is very easy to implementing post-processing logic. You can obviously feel the advantage of koa middleware model comparing to the compress middleware implementing in koa and express. +All the requests will be executed twice during one middleware. Compared to Express middleware, it is very easy to implementing post-processing logic. You can obviously feel the advantage of Koa middleware model comparing to the compress middleware implementing in Koa and Express. -- [koa-compress](https://github.com/koajs/compress/blob/master/index.js) for koa. -- [compression](https://github.com/expressjs/compression/blob/master/index.js) for express. +- [koa-compress](https://github.com/koajs/compress/blob/master/index.js) for Koa. +- [compression](https://github.com/expressjs/compression/blob/master/index.js) for Express. ### Context -Unlike that there are only two objects `Request` and `Response` in express, koa has one more, `Context` object in one http request(it is `this` in koa 1, while it is the first parameter for middleware function in koa 2). We can attach all the relative things to the object. Such as [traceId](https://github.com/eggjs/egg-tracer/blob/1.0.0/lib/tracer.js#L12) that runs through the request lifetime (which will be called anywhere afterward) could be attached. It is more semantic other than request and response. +Unlike that there are only two objects `Request` and `Response` in Express, Koa has one more, `Context` object in one HTTP request(it is `this` in Koa 1, while it is the first parameter for middleware function in Koa 2). We can attach all the relative things to the object. Such as [traceId](https://github.com/eggjs/egg-tracer/blob/1.0.0/lib/tracer.js#L12) that runs through the request lifetime (which will be called anywhere afterward) could be attached. It is more semantic other than request and response. -At the same time Request and Response are attached to Context object. Just like express, the two objects provide lots of easy ways to help developing. For example: +At the same time Request and Response are attached to Context object. Just like Express, the two objects provide lots of easy ways to help developing. For example: - `get request.query` - `get request.hostname` @@ -130,16 +130,16 @@ function* onerror(next) { Putting the middleware before others, you can catch all the exceptions thrown by the synchronous or asynchronous code. -## Egg inherit from koa +## Egg inherit from Koa -As the above words, koa is an excellent framework. However, it is not enough to building an enterprise-class application. +As the above words, Koa is an excellent framework. However, it is not enough to building an enterprise-class application. -Egg is built around the koa. On the basis of koa model, egg implements enhancements one step further. +Egg is built around the Koa. On the basis of Koa model, Egg implements enhancements one step further. ### Extension -In the framework or application based on egg, we can extend the prototype of 4 koa objects by defining `app/extend/{application,context,request,response}.js`. With this, we can write more utility methods quickly. For example, we have the following code in `app/extend/context.js`: +In the framework or application based on Egg, we can extend the prototype of 4 Koa objects by defining `app/extend/{application,context,request,response}.js`. With this, we can write more utility methods quickly. For example, we have the following code in `app/extend/context.js`: ```js // app/extend/context.js @@ -166,13 +166,13 @@ More about extension, please check [Exception](../basics/extend.md) section. ### Plugin -As is known to all, Many middlewares are imported to provide different kind of features in express and koa. Eg, [koa-session](https://github.com/koajs/session) provides the session support, [koa-bodyparser](https://github.com/koajs/bodyparser) help to parse request body. Egg has provided a powerful plugin mechanism to make it more easy to write stand alone features. +As is known to all, Many middlewares are imported to provide different kind of features in Express and Koa. Eg, [koa-session](https://github.com/koajs/session) provides the Session support, [koa-bodyparser](https://github.com/koajs/bodyparser) help to parse request body. Egg has provided a powerful plugin mechanism to make it more easy to write stand alone features. One plugin can include: - extend:extend the context of base object, provide utility and attributes. -- middleware:add one or more middlewares, provide pre or post processing logic for request. -- config:configure the default value in different environments. +- middleware:add one or more middlewares, provide pre or post processing logic for request. +- config:configure the default value in different environments. Stand alone module plugin can provide rich features with high maintenancability. You can almost forget the configuration as the plugin supports configuring the default value in different environments. @@ -182,22 +182,22 @@ More about plugin, please check [plugin](../advanced/plugin.md) section. ### Roadmap -#### egg 1.x +#### Egg 1.x -The node LTS version now does not support async function,so egg is based on koa 1.x. On the basis of this, egg has added full async function support. Egg is completely compatible with middlewares in koa 2.x, all application layer are implemented based on [async function](../tutorials/async-function.md). +The Node.js LTS version now does not support async function,so Egg is based on Koa 1.x. On the basis of this, Egg has added full async function support. Egg is completely compatible with middlewares in Koa 2.x, all application layer are implemented based on [async function](../tutorials/async-function.md). -- The underlying is based on koa 1.x, asynchronous solution is based on generator function wrapped by [co]. -- Official plugin and core of egg are written in generator function, keep supporting node LTS version, use [co] when necessary to be compatiable with async function. -- Application developers can choose either async function(node 7.6+) or generator function(node 6.0+), **we recommend generator function way for ensuring you application can be runned on node LTS version**. +- The underlying is based on Koa 1.x, asynchronous solution is based on generator function wrapped by [co]. +- Official plugin and core of Egg are written in generator function, keep supporting Node.js LTS version, use [co] when necessary to be compatiable with async function. +- Application developers can choose either async function (Node.js 7.6+) or generator function (Node.js 6.0+), **we recommend generator function way for ensuring you application can be runned on Node.js LTS version**. -#### egg next +#### Egg next -Egg will transfer core to koa 2.x until node LTS supports async function, compatibility with generator function will also be kept. +Egg will transfer core to Koa 2.x until Node.js LTS supports async function, compatibility with generator function will also be kept. -- The underlying will be based on koa 2.x, asynchronous solution will be based on async function. +- The underlying will be based on Koa 2.x, asynchronous solution will be based on async function. - Official plugin and core of egge will be written in async function. - Recommend user transfer business layer to async function. -- node 6.x will be no longer supported. +- Node.js 6.x will be no longer supported. [co]: https://github.com/tj/co [Async function]: https://github.com/tc39/ecmascript-asyncawait diff --git a/docs/source/en/intro/index.md b/docs/source/en/intro/index.md index 29dd569d48..e590cfe0ea 100644 --- a/docs/source/en/intro/index.md +++ b/docs/source/en/intro/index.md @@ -1,32 +1,32 @@ -title: What is egg? +title: What is Egg? --- -**egg is born for building enterprise application and framework**,we hope egg will give birth to more application framework to help developers and developing team reduce development and maintenance costs. +**Egg is born for building enterprise application and framework**,we hope Egg will give birth to more application framework to help developers and developing team reduce development and maintenance costs. ## Design principles -Since we know well that enterprise applications need to consider how to balance the differences between different teams, seeking common ground while reserving differences in the pursuit of clarifying specification and cooperation, we focus on providing core features for web development and a flexible and extensible plug-in mechanism instead of giant bazaar mode which is popular in common web frameworks(with integrated such as database, template engine, front-end framework and other functions). We will not make technical selection because default technical selection makes the scalability of the framework too poor to meet a variety of custom requirements. With the help of egg , it is very easy for architects and technical leaders to build their own framework which is suitable for their business scenarios based on existing technology stack . +Since we know well that enterprise applications need to consider how to balance the differences between different teams, seeking common ground while reserving differences in the pursuit of clarifying specification and cooperation, we focus on providing core features for Web development and a flexible and extensible plug-in mechanism instead of giant bazaar mode which is popular in common Web frameworks(with integrated such as database, template engine, front-end framework and other functions). We will not make technical selection because default technical selection makes the scalability of the framework too poor to meet a variety of custom requirements. With the help of Egg , it is very easy for architects and technical leaders to build their own framework which is suitable for their business scenarios based on existing technology stack . -The plug-in mechanism of egg is very extensible, **one purpose for one plugin**(Eg:[nunjucks] is encapsulated in to [egg-view-nunjucks](https://github.com/eggjs/egg-view-nunjucks), and MySQL is encapsulated in to [egg-mysql](https://github.com/eggjs/egg-mysql)). Aggregating the plugins and customizing the configurations according to their own business scenarios greatly reduces the development cost. +The plug-in mechanism of Egg is very extensible, **one purpose for one plugin**(Eg: [Nunjucks] is encapsulated in to [egg-view-nunjucks](https://github.com/eggjs/egg-view-nunjucks), and MySQL is encapsulated in to [egg-mysql](https://github.com/eggjs/egg-mysql)). Aggregating the plugins and customizing the configurations according to their own business scenarios greatly reduces the development cost. -Egg is a convention-over-configuration framework, follows the [Loader](../advanced/loader.md) to do the development, it helps to reduce the cost of learning. Developers no longer work as 'nails'. The cost of communication is very high for a team without convention. it is easy to get fault without convention. However convention is not equal to diffcult extension, instead, egg does well in extension part, you can build your own framework according to team convention. [Loader](../advanced/loader.md) can help load different default configuration in different environment, egg default convention can also be covered by your own. +Egg is a convention-over-configuration framework, follows the [Loader](../advanced/loader.md) to do the development, it helps to reduce the cost of learning. Developers no longer work as 'nails'. The cost of communication is very high for a team without convention. it is easy to get fault without convention. However convention is not equal to diffcult extension, instead, Egg does well in extension part, you can build your own framework according to team convention. [Loader](../advanced/loader.md) can help load different default configuration in different environment, Egg default convention can also be covered by your own. -## differences between community framework +## Differences between community framework -[express] is well used in node community, it is easy and extensible, fit personal project a lot. However, without default convention, standard mvc model has lots of strange impl which would leads to misunderstand. egg's teamwork cost is really low by following convention convention-over-configuration. +[Express] is well used in Node.js community, it is easy and extensible, fit personal project a lot. However, without default convention, standard mvc model has lots of strange impl which would leads to misunderstand. Egg's teamwork cost is really low by following convention convention-over-configuration. -[sails] is a framework that also follows convention-over-configuration,it does well in extensible work. Compared with egg, [sails] supports blueprint REST API, [WaterLine] , Frontend integration, websocks and so on, all of these are provided by sails. Egg does not provide these functions, it only has integration of different functional extension, eg, egg-blueprint, egg-waterline, if you use sails-egg to integrate these extensions, sails can be replaced. +[Sails] is a framework that also follows convention-over-configuration,it does well in extensible work. Compared with Egg, [Sails] supports blueprint REST API, [WaterLine] , Frontend integration, websocks and so on, all of these are provided by Sails. Egg does not provide these functions, it only has integration of different functional extension, eg, egg-blueprint, egg-waterline, if you use sails-egg to integrate these extensions, [Sails] can be replaced. ## features - depth [customizd framework](../advanced/framework.md) -- highly extensible [plug-in mechanism] (../advanced/plugin.md) +- highly extensible [plug-in mechanism](../advanced/plugin.md) - built-in [cluster](../advanced/cluster.md) - based on [koa] with high performance - stable core framework with high test coverage. - [progressive development](../tutorials/progressive.md) -[sails]: http://sailsjs.com -[express]: http://expressjs.com -[koa]: http://koajs.com -[nunjucks]: https://mozilla.github.io/nunjucks +[Sails]: http://sailsjs.com +[Express]: http://expressjs.com +[Koa]: http://koajs.com +[Nunjucks]: https://mozilla.github.io/nunjucks [WaterLine]: https://github.com/balderdashy/waterline diff --git a/docs/source/en/intro/quickstart.md b/docs/source/en/intro/quickstart.md index a05886968f..8a924a2ab4 100644 --- a/docs/source/en/intro/quickstart.md +++ b/docs/source/en/intro/quickstart.md @@ -9,7 +9,7 @@ By following along with this guide step by step, you can quickly get started wit ## Prerequisites - Operating System: Linux, OS X or Windows. -- Node.js Runtime: 6.x or newer; it is recommended that you use [LTS Releases][node]. +- Node.js Runtime: 6.x or newer; it is recommended that you use [LTS Releases][Node.js]. ## the Quick Way @@ -149,7 +149,7 @@ to allow the developers to use different plug-ins for their individual needs. For more information, cf. [View](../core/view.md). -In this example, we will use [nunjucks]. +In this example, we will use [Nunjucks]. First install the corresponding plug-in [egg-view-nunjucks]. @@ -231,7 +231,7 @@ module.exports = app => { Open a browser window and navigate to http://localhost:7001/news. You should be able to see the rendered page. -**Tip:In development, egg enables the [development][egg-development] plug-in by default, which reloads your worker process when changes are made to your back-end code.** +**Tip:In development, Egg enables the [development][egg-development] plug-in by default, which reloads your worker process when changes are made to your back-end code.** ### Create a Service @@ -251,7 +251,7 @@ module.exports = app => { // read config const { serverUrl, pageSize } = this.app.config.news; - // use build-in http client to GET hacker-news api + // use build-in HttpClient to GET hacker-news api const { data: idList } = yield this.ctx.curl(`${serverUrl}/topstories.json`, { data: { orderBy: '"$key"', @@ -273,7 +273,7 @@ module.exports = app => { }; ``` -> Egg has [http client](../core/httpclient.md) built in in order to help you make http requests. +> Egg has [HttpClient](../core/httpclient.md) built in in order to help you make HTTP requests. Then slightly modify our previous controller. @@ -406,7 +406,7 @@ module.exports = app => { ### Add Unit Testing -Unit Testing is very important, and egg also provide [egg-bin] to help you write tests painless. +Unit Testing is very important, and Egg also provide [egg-bin] to help you write tests painless. ```js // test/app/middleware/robot.test.js @@ -469,11 +469,11 @@ Where to go from here? Browse our documentation to better understand the framewo [nvm]: https://github.com/creationix/nvm [nvs]: https://github.com/jasongin/nvs -[node]: http://nodejs.org +[Node.js]: http://nodejs.org [npm]: https://www.npmjs.org [egg-init]: https://github.com/eggjs/egg-init [egg-static]: https://github.com/eggjs/egg-static [egg-development]: https://github.com/eggjs/egg-development [egg-view-nunjucks]: https://github.com/eggjs/egg-view-nunjucks [urllib]: https://www.npmjs.com/package/urllib -[nunjucks]: https://mozilla.github.io/nunjucks/ +[Nunjucks]: https://mozilla.github.io/nunjucks/ diff --git a/docs/source/zh-cn/advanced/cluster-client.md b/docs/source/zh-cn/advanced/cluster-client.md index 55b5b03f37..f0dc304b25 100644 --- a/docs/source/zh-cn/advanced/cluster-client.md +++ b/docs/source/zh-cn/advanced/cluster-client.md @@ -387,7 +387,7 @@ class APIClient extends Base { constructor(options) { super(options); - // options.cluster 用于给 egg 的插件传递 app.cluster 进来 + // options.cluster 用于给 Egg 的插件传递 app.cluster 进来 this._client = (options.cluster || cluster)(RegistryClient).create(options); this._client.ready(() => this.ready(true)); @@ -469,4 +469,4 @@ exports.apiClient = { - ClusterClient - 通过 cluster-client 模块进行简单 wrap 得到的 client 实例,负责自动抹平多进程模型的差异。 - APIClient - 内部调用 ClusterClient 做数据同步,无需关心多进程模型,用户最终使用的模块。API 都通过此处暴露,支持同步和异步。 -有兴趣的同学可以看一下 [增强多进程研发模式](https://github.com/eggjs/egg/issues/322) 讨论过程。 +有兴趣的同学可以看一下[增强多进程研发模式](https://github.com/eggjs/egg/issues/322) 讨论过程。 diff --git a/docs/source/zh-cn/advanced/framework.md b/docs/source/zh-cn/advanced/framework.md index 68454bf821..bb70e82676 100644 --- a/docs/source/zh-cn/advanced/framework.md +++ b/docs/source/zh-cn/advanced/framework.md @@ -12,9 +12,9 @@ title: 框架开发 - 统一的技术选型,比如数据库、模板、前端框架及各种中间件设施都需要选型,而框架封装后保证应用使用一套架构。 - 统一的默认配置,开源社区的配置可能不适用于公司,而又不希望应用去配置。 - 统一的部署方案,通过框架和平台的双向控制,应用只需要关注自己的代码,具体查看[部署章节](./deployment.md) -- 统一的代码风格,框架不仅仅解决代码重用问题,还可以对应用做一定约束,作为企业框架是很必要的。egg 在 koa 基础上做了很多约定,框架可以使用 [Loader](./loader.md) 自己定义代码规则。 +- 统一的代码风格,框架不仅仅解决代码重用问题,还可以对应用做一定约束,作为企业框架是很必要的。Egg 在 Koa 基础上做了很多约定,框架可以使用 [Loader](./loader.md) 自己定义代码规则。 -为此,egg 为团队架构师和技术负责人提供 `框架定制` 的能力,框架是一层抽象,可以基于 egg 去封装上层框架,并且 egg 支持多层继承。 +为此,Egg 为团队架构师和技术负责人提供 `框架定制` 的能力,框架是一层抽象,可以基于 Egg 去封装上层框架,并且 Egg 支持多层继承。 这样,整个团队就可以遵循统一的方案,并且在项目中可以根据业务场景自行使用插件做差异化,当后者验证为最佳实践后,就能下沉到框架中,其他项目仅需简单的升级下框架的版本即可享受到。 @@ -26,16 +26,16 @@ title: 框架开发 在 Agent Worker 启动的时候会实例化 Agent,而在 App Worker 启动时会实例化 Application,这两个类又同时继承 [EggCore](https://github.com/eggjs/egg-core)。 -EggCore 可以看做 koa Application 的升级版,默认内置 [Loader](./loader.md)、[Router](../basics/router.md) 及应用异步启动等功能,可以看做是支持 Loader 的 koa。 +EggCore 可以看做 Koa Application 的升级版,默认内置 [Loader](./loader.md)、[Router](../basics/router.md) 及应用异步启动等功能,可以看做是支持 Loader 的 Koa。 ``` - koa Application + Koa Application ^ EggCore ^ ┌──────┴───────┐ │ │ - egg Agent egg Application + Egg Agent Egg Application ^ ^ agent worker app worker ``` @@ -55,11 +55,11 @@ $ npm test ### 框架 API -egg 框架提供了一些 API,所有继承的框架都需要提供,只增不减。这些 API 基本都有 Agent 和 Application 两份。 +Egg 框架提供了一些 API,所有继承的框架都需要提供,只增不减。这些 API 基本都有 Agent 和 Application 两份。 #### `egg.startCluster` -egg 的多进程启动器,由这个方法来启动 Master,主要的功能实现在 [egg-cluster](https://github.com/eggjs/egg-cluster) 上。所以直接使用 EggCore 还是单进程的方式,而 egg 实现了多进程。 +Egg 的多进程启动器,由这个方法来启动 Master,主要的功能实现在 [egg-cluster](https://github.com/eggjs/egg-cluster) 上。所以直接使用 EggCore 还是单进程的方式,而 Egg 实现了多进程。 ```js const startCluster = require('egg').startCluster; @@ -77,17 +77,17 @@ startCluster({ #### `egg.Application` 和 `egg.Agent` -进程中的唯一单例,但 Application 和 Agent 存在一定差异。如果框架继承于 egg,会定制这两个类,那 framework 应该 export 这两个类。 +进程中的唯一单例,但 Application 和 Agent 存在一定差异。如果框架继承于 Egg,会定制这两个类,那 framework 应该 export 这两个类。 #### `egg.AppWorkerLoader` 和 `egg.AgentWorkerLoader` -框架也存在定制 Loader 的场景,覆盖原方法或者新加载目录都需要提供自己的 Loader,而且必须要继承 egg 的 Loader。 +框架也存在定制 Loader 的场景,覆盖原方法或者新加载目录都需要提供自己的 Loader,而且必须要继承 Egg 的 Loader。 ### 框架继承 -框架支持继承关系,可以把框架比作一个类,那么基类就是 egg 框架,如果想对 egg 做扩展就继承。 +框架支持继承关系,可以把框架比作一个类,那么基类就是 Egg 框架,如果想对 Egg 做扩展就继承。 -首先定义一个框架需要实现 egg 所有的 API +首先定义一个框架需要实现 Egg 所有的 API ```js // package.json @@ -108,7 +108,7 @@ Object.assign(exports, egg); ```js // index.js -// 覆盖了 egg 的 Application +// 覆盖了 Egg 的 Application exports.Application = require('./lib/application.js'); // lib/application.js @@ -145,7 +145,7 @@ module.exports = YadanApplication; 现在的实现方案是基于类继承的,每一层框架都必须继承上一层框架并且指定 eggPath,然后遍历原型链就能获取每一层的框架路径了。 -比如有三层框架:部门框架(department)> 企业框架(enterprise)> egg +比如有三层框架:部门框架(department)> 企业框架(enterprise)> Egg ```js // enterprise @@ -173,11 +173,11 @@ const app = new Application(); app.ready(); ``` -以上均是伪代码,为了详细说明框架路径的加载过程,不过 egg 已经在[本地开发](../core/development.md)和[应用部署](./deployment.md)提供了很好的工具,不需要自己实现。 +以上均是伪代码,为了详细说明框架路径的加载过程,不过 Egg 已经在[本地开发](../core/development.md)和[应用部署](./deployment.md)提供了很好的工具,不需要自己实现。 ### 自定义 Agent -上面的例子自定义了 Application,因为 egg 是多进程模型,所以还需要定义 Agent,原理是一样的。 +上面的例子自定义了 Application,因为 Egg 是多进程模型,所以还需要定义 Agent,原理是一样的。 ```js // index.js @@ -219,7 +219,7 @@ class YadanApplication extends Application { get [EGG_PATH]() { return path.dirname(__dirname); } - // 覆盖 egg 的 Loader,启动时使用这个 Loader + // 覆盖 Egg 的 Loader,启动时使用这个 Loader get [EGG_LOADER]() { return AppWorkerLoader; } @@ -236,7 +236,7 @@ class YadanAppWorkerLoader extends AppWorkerLoader { module.exports = YadanAppWorkerLoader; ``` -AgentWorkerLoader 扩展也类似,这里不再举例。AgentWorkerLoader 加载的文件可以于 AppWorkerLoader 不同,比如 默认加载时,egg 的 AppWorkerLoader 会加载 `app.js` 而 AgentWorkerLoader 加载的是 `agent.js`。 +AgentWorkerLoader 扩展也类似,这里不再举例。AgentWorkerLoader 加载的文件可以于 AppWorkerLoader 不同,比如 默认加载时,Egg 的 AppWorkerLoader 会加载 `app.js` 而 AgentWorkerLoader 加载的是 `agent.js`。 ## 框架启动原理 @@ -328,7 +328,7 @@ describe('/test/index.test.js', () => { 很少场景会使用多进程测试,因为多进程无法进行 API 级别的 mock 导致测试成本很高,而进程在有覆盖率的场景启动很慢,测试会超时。但多进程测试是验证多进程模型最好的方式,还可以测试 stdout 和 stderr。 -多进程测试和 `mm.app` 参数一致,但 app 的 API 完全不同,不过 supertest 依然可用。 +多进程测试和 `mm.app` 参数一致,但 app 的 API 完全不同,不过 SuperTest 依然可用。 ```js const mock = require('egg-mock'); diff --git a/docs/source/zh-cn/advanced/loader.md b/docs/source/zh-cn/advanced/loader.md index 4d4e1470a2..f1015e765e 100644 --- a/docs/source/zh-cn/advanced/loader.md +++ b/docs/source/zh-cn/advanced/loader.md @@ -1,11 +1,11 @@ title: Loader --- -egg 在 koa 的基础上进行增强最重要的就是基于一定的约定,根据功能差异将代码放到不同的目录下管理,对整体团队的开发成本提升有着明显的效果。Loader 实现了这套约定,并抽象了很多底层 API 可以进一步扩展。 +Egg 在 Koa 的基础上进行增强最重要的就是基于一定的约定,根据功能差异将代码放到不同的目录下管理,对整体团队的开发成本提升有着明显的效果。Loader 实现了这套约定,并抽象了很多底层 API 可以进一步扩展。 ## 应用、框架和插件 -egg 是一个底层框架,应用可以直接使用,但 egg 本身的插件比较少,应用需要自己配置插件增加各种特性,比如 MySQL。 +Egg 是一个底层框架,应用可以直接使用,但 Egg 本身的插件比较少,应用需要自己配置插件增加各种特性,比如 MySQL。 ```js // 应用配置 @@ -26,7 +26,7 @@ module.exports = { } ``` -当应用达到一定数量,我们会发现大部分应用的配置都是类似的,这时可以基于 egg 扩展出一个框架,应用的配置就会简化很多。 +当应用达到一定数量,我们会发现大部分应用的配置都是类似的,这时可以基于 Egg 扩展出一个框架,应用的配置就会简化很多。 ```js // 框架配置 @@ -72,7 +72,7 @@ module.exports = { - 我们在应用中完成业务,需要指定一个框架才能运行起来,当需要某个特性场景的功能时可以配置插件(比如 MySQL)。 - 插件只完成特定功能,当两个独立的功能有互相依赖时,还是分开两个插件,但需要配置依赖。 -- 框架是一个启动器(默认就是 egg),必须有它才能运行起来。框架还是一个封装器,将插件的功能聚合起来统一提供,框架也可以配置插件。 +- 框架是一个启动器(默认就是 Egg),必须有它才能运行起来。框架还是一个封装器,将插件的功能聚合起来统一提供,框架也可以配置插件。 - 在框架的基础上还可以扩展出新的框架,也就是说**框架是可以无限级继承的**,有点像类的继承。 ``` @@ -83,15 +83,15 @@ module.exports = { + | framework1 +--------------+ plugin | | | | framework2 | | + +--------------+--------------+ | -| egg | | +| Egg | | +-----------------------------------+--------| -| koa | +| Koa | +-----------------------------------+--------+ ``` ## loadUnit -egg 将应用、框架和插件都称为加载单元(loadUnit),因为在代码结构上几乎没有什么差异,下面是目录结构 +Egg 将应用、框架和插件都称为加载单元(loadUnit),因为在代码结构上几乎没有什么差异,下面是目录结构 ``` loadUnit @@ -132,7 +132,7 @@ config/config.{env}.js | ✔︎ | ✔︎ | ✔︎ config/plugin.js | ✔︎ | ✔︎ | package.json | ✔︎ | ✔︎ | ✔︎ -在加载过程中,egg 会遍历所有的 loadUnit 加载上述的文件(应用、框架、插件各有不同),加载时有一定的优先级 +在加载过程中,Egg 会遍历所有的 loadUnit 加载上述的文件(应用、框架、插件各有不同),加载时有一定的优先级 - 按插件 => 框架 => 应用依次加载 - 插件之间的顺序由依赖关系决定,被依赖方先加载,无依赖按 object key 配置顺序加载,具体可以查看[插件章节](./plugin.md) @@ -166,7 +166,7 @@ plugin1 为 framework1 依赖的插件,配置合并后 object key 的顺序会 ### 文件顺序 -上面已经列出了默认会加载的文件,egg 会按如下文件顺序加载,每个文件或目录再根据 loadUnit 的顺序去加载(应用、框架、插件各有不同)。 +上面已经列出了默认会加载的文件,Egg 会按如下文件顺序加载,每个文件或目录再根据 loadUnit 的顺序去加载(应用、框架、插件各有不同)。 - 加载 [plugin](./plugin.md),找到应用和框架,加载 `config/plugin.js` - 加载 [config](../basics/config.md), 遍历 loadUnit 加载 `config/config.{env}.js` @@ -212,7 +212,7 @@ Loader 还提供了 [caseStyle](#caseStyle-boolean) 强制指定首字母大小 - loadController() - loadRouter() -egg 基于 Loader 实现了 [AppWorkerLoader] 和 [AgentWorkerLoader],上层框架基于这两个类来扩展,**Loader 的扩展只能在框架进行**。 +Egg 基于 Loader 实现了 [AppWorkerLoader] 和 [AgentWorkerLoader],上层框架基于这两个类来扩展,**Loader 的扩展只能在框架进行**。 ```js // 自定义 AppWorkerLoader @@ -240,7 +240,7 @@ class CustomAppWorkerLoader extends AppWorkerLoader { exports.AppWorkerLoader = CustomAppWorkerLoader; // index.js -// 拷贝一份 egg 的 API +// 拷贝一份 Egg 的 API Object.assign(exports, require('egg')); // 将自定义的 Loader exports 出来 exports.AppWorkerLoader = require('./lib/loader').AppWorkerLoader; diff --git a/docs/source/zh-cn/advanced/plugin.md b/docs/source/zh-cn/advanced/plugin.md index f7cc1957c3..75b2a97bf4 100644 --- a/docs/source/zh-cn/advanced/plugin.md +++ b/docs/source/zh-cn/advanced/plugin.md @@ -4,7 +4,7 @@ title: 插件开发 插件机制是我们框架的一大特色。它不但可以保证框架核心的足够精简、稳定、高效,还可以促进业务逻辑的复用,生态圈的形成。有人可能会问了 -> koa 已经有了中间件的机制,为啥还要插件呢? +> Koa 已经有了中间件的机制,为啥还要插件呢? > 中间件、插件、应用它们之间是什么关系,有什么区别? > 我该怎么使用一个插件? > 如何编写一个插件? @@ -14,7 +14,7 @@ title: 插件开发 ## 为什么要插件 -我们在使用 koa 中间件过程中发现了下面一些问题: +我们在使用 Koa 中间件过程中发现了下面一些问题: 1. 中间件加载其实是有先后顺序的,但是中间件自身却无法管理这种顺序,只能交给使用者。这样其实非常不友好,一旦顺序不对,结果可能有天壤之别。 2. 中间件的定位是拦截用户请求,并在它前后做一些事情,例如:鉴权、安全检查、访问日志等等。但实际情况是,有些功能是和请求无关的,例如:定时任务、消息订阅、后台逻辑等等。 @@ -66,7 +66,7 @@ title: 插件开发 - `{String} name` - 插件名(必须配置),具有唯一性,配置依赖关系时会指定依赖插件的 name。 - `{Array} dependencies` - 当前插件强依赖的插件列表(如果依赖的插件没找到,应用启动失败)。 - `{Array} optionalDependencies` - 当前插件的可选依赖插件列表(如果依赖的插件未开启,只会 warning,不会影响应用启动)。 - - `{Array} env` - 只有在指定运行环境才能开启,具体有哪些环境可以参考 [运行环境](../basics/env.md)。此配置是可选的,一般情况下都不需要配置。 + - `{Array} env` - 只有在指定运行环境才能开启,具体有哪些环境可以参考[运行环境](../basics/env.md)。此配置是可选的,一般情况下都不需要配置。 ```json { @@ -129,12 +129,12 @@ title: 插件开发 在插件相应的文件内对框架内置对象进行扩展,和应用一样 -- `app/extend/request.js` - 扩展 koa#Request 对象 -- `app/extend/response.js` - 扩展 koa#Response 对象 -- `app/extend/context.js` - 扩展 koa#Context 对象 -- `app/extend/helper.js ` - 扩展 Helper 对象 -- `app/extend/application.js` - 扩展 app 对象 -- `app/extend/agent.js` - 扩展 agent 对象 +- `app/extend/request.js` - 扩展 Koa#Request 类 +- `app/extend/response.js` - 扩展 Koa#Response 类 +- `app/extend/context.js` - 扩展 Koa#Context 类 +- `app/extend/helper.js ` - 扩展 Helper 类 +- `app/extend/application.js` - 扩展 Application 类 +- `app/extend/agent.js` - 扩展 Agent 类 ### 插入自定义中间件 @@ -450,7 +450,7 @@ exports.onerror = false; 框架已内置插件列表: - [onerror](https://github.com/eggjs/egg-onerror) 统一异常处理 -- [session](https://github.com/eggjs/egg-session) session 实现 +- [Session](https://github.com/eggjs/egg-session) Session 实现 - [i18n](https://github.com/eggjs/egg-i18n) 多语言 - [watcher](https://github.com/eggjs/egg-watcher) 文件和文件夹监控 - [multipart](https://github.com/eggjs/egg-multipart) 文件流式上传 @@ -545,11 +545,11 @@ $ npm test ## 为何不使用 npm 包名来做插件名? -egg 是通过 `eggPlugin.name` 来定义插件名的,只在应用或框架具备唯一性,也就是说**多个 npm 包可能有相同的插件名**,为什么这么设计呢? +Egg 是通过 `eggPlugin.name` 来定义插件名的,只在应用或框架具备唯一性,也就是说**多个 npm 包可能有相同的插件名**,为什么这么设计呢? -首先 egg 插件不仅仅支持 npm 包,还支持通过目录来找插件。在[渐进式开发](../tutorials/progressive.md)章节提到如何使用这两个配置来进行代码演进。目录对单元测试也比较友好。所以 egg 无法通过 npm 的包名来做唯一性。 +首先 Egg 插件不仅仅支持 npm 包,还支持通过目录来找插件。在[渐进式开发](../tutorials/progressive.md)章节提到如何使用这两个配置来进行代码演进。目录对单元测试也比较友好。所以 Egg 无法通过 npm 的包名来做唯一性。 -更重要的是 egg 可以使用这种特性来做适配器。比如[模板开发规范](./view-plugin.md#插件命名规范)定义的插件名为 view,而存在 `egg-view-nunjucks`,`egg-view-react` 等插件,使用者只需要更换插件和修改模板,不需要动 Controller, 因为所有的模板插件都实现了相同的 API。 +更重要的是 Egg 可以使用这种特性来做适配器。比如[模板开发规范](./view-plugin.md#插件命名规范)定义的插件名为 view,而存在 `egg-view-nunjucks`,`egg-view-react` 等插件,使用者只需要更换插件和修改模板,不需要动 Controller, 因为所有的模板插件都实现了相同的 API。 **将相同功能的插件赋予相同的插件名,具备相同的 API,可以快速切换**。这在模板、数据库等领域非常适用。 diff --git a/docs/source/zh-cn/advanced/view-plugin.md b/docs/source/zh-cn/advanced/view-plugin.md index 4aa2c5f355..878716622f 100644 --- a/docs/source/zh-cn/advanced/view-plugin.md +++ b/docs/source/zh-cn/advanced/view-plugin.md @@ -155,7 +155,7 @@ module.exports = class MyCustomView { render(filename, locals) { locals.helper = new ViewHelper(this.ctx); - // 调用 nunjucks render + // 调用 Nunjucks render } } ``` diff --git a/docs/source/zh-cn/basics/app-start.md b/docs/source/zh-cn/basics/app-start.md index c1ba9bd685..afa84118de 100644 --- a/docs/source/zh-cn/basics/app-start.md +++ b/docs/source/zh-cn/basics/app-start.md @@ -3,7 +3,7 @@ title: 启动自定义 我们常常需要在应用启动期间进行一些初始化工作,等初始化完成后应用才可以启动成功,并开始对外提供服务。 -框架提供了统一的入口文件(`app.js`)进行启动过程自定义,这个文件只返回一个函数。例如,我们需要在应用启动期间从远程接口加载一份全国城市列表,以便于后续在 controller 中使用: +框架提供了统一的入口文件(`app.js`)进行启动过程自定义,这个文件只返回一个函数。例如,我们需要在应用启动期间从远程接口加载一份全国城市列表,以便于后续在 Controller 中使用: ```js // app.js @@ -18,7 +18,7 @@ module.exports = app => { }; ``` -在 controller 中就可以使用了: +在 Controller 中就可以使用了: ```js // app/controller/city.js diff --git a/docs/source/zh-cn/basics/config.md b/docs/source/zh-cn/basics/config.md index a7b48e9cf8..de6d7d08a9 100644 --- a/docs/source/zh-cn/basics/config.md +++ b/docs/source/zh-cn/basics/config.md @@ -1,8 +1,6 @@ -title: 配置 +title: Config 配置 --- -## config 配置 - 框架提供了强大且可扩展的配置功能,可以自动合并应用、插件、框架的配置,按顺序覆盖,且可以根据环境维护不同的配置。合并后的配置可直接从 `app.config` 获取。 配置的管理有多种方案,以下列一些常见的方案 @@ -28,7 +26,7 @@ config `config.default.js` 为默认的配置文件,所有环境都会加载这个配置文件,一般也会作为开发环境的默认配置文件。 -当指定 env 时会同时加载对应的配置文件,并覆盖默认配置文件的同名配置。如 prod 环境会加载 `config.prod.js` 和 `config.default.js` 文件,`config.prod.js` 会覆盖 `config.default.js` 的同名配置。 +当指定 env 时会同时加载对应的配置文件,并覆盖默认配置文件的同名配置。如 `prod` 环境会加载 `config.prod.js` 和 `config.default.js` 文件,`config.prod.js` 会覆盖 `config.default.js` 的同名配置。 ### 配置写法 @@ -132,7 +130,7 @@ module.exports = { 框架默认内置了企业级应用常用的[一部分插件](https://github.com/eggjs/egg/blob/master/config/plugin.js)。 -而应用开发者可以根据业务需求,引入其他插件,只需要指定 package 配置。 +而应用开发者可以根据业务需求,引入其他插件,只需要指定 `package` 配置。 ```js // 使用 mysql 插件 @@ -144,7 +142,7 @@ module.exports = { }; ``` -package 为一个 npm 模块,必须添加依赖到 `pkg.dependencies` 中。框架会在 node_modules 目录中找到这个模块作为插件入口。 +`package` 为一个 npm 模块,必须添加依赖到 `pkg.dependencies` 中。框架会在 node_modules 目录中找到这个模块作为插件入口。 ```json { @@ -174,7 +172,7 @@ path 为一个绝对路径,这样应用可以把自己写的插件直接放到 框架在启动时会把合并后的最终配置 dump 到 `run/application_config.json`(worker 进程)和 `run/agent_config.json`(agent 进程)中,可以用来分析问题。 -配置文件中会隐藏一些字段,主要包括两类 +配置文件中会隐藏一些字段,主要包括两类: - 如密码、密钥等安全字段,这里可以通过 `config.dump.ignore` 配置,必须是 [Set] 类型,查看[默认配置](https://github.com/eggjs/egg/blob/master/config/config.default.js)。 - 如函数、Buffer 等类型,`JSON.stringify` 后的内容特别大 diff --git a/docs/source/zh-cn/basics/controller.md b/docs/source/zh-cn/basics/controller.md index d322fdd38f..65e21f44e3 100644 --- a/docs/source/zh-cn/basics/controller.md +++ b/docs/source/zh-cn/basics/controller.md @@ -1,30 +1,30 @@ title: controller --- -## 什么是 controller +## 什么是 Controller -[前面章节](./router.md)写到,我们通过 router 将用户的请求基于 method 和 URL 分发到了对应的 controller 上,那 controller 负责做什么? +[前面章节](./router.md)写到,我们通过 Router 将用户的请求基于 method 和 URL 分发到了对应的 Controller 上,那 Controller 负责做什么? -简单的说 controller 负责**解析用户的输入,处理后返回相应的结果**,例如 +简单的说 Controller 负责**解析用户的输入,处理后返回相应的结果**,例如 -- 在 [RESTful](https://en.wikipedia.org/wiki/Representational_state_transfer) 接口中,controller 接受用户的参数,从数据库中查找内容返回给用户或者将用户的请求更新到数据库中。 -- 在 html 页面请求中,controller 根据用户访问不同的 URL,渲染不同的模板得到 html 返回给用户。 -- 在代理服务器中,controller 将用户的请求转发到其他服务器上,并将其他服务器的处理结果返回给用户。 +- 在 [RESTful](https://en.wikipedia.org/wiki/Representational_state_transfer) 接口中,Controller 接受用户的参数,从数据库中查找内容返回给用户或者将用户的请求更新到数据库中。 +- 在 HTML 页面请求中,Controller 根据用户访问不同的 URL,渲染不同的模板得到 HTML 返回给用户。 +- 在代理服务器中,Controller 将用户的请求转发到其他服务器上,并将其他服务器的处理结果返回给用户。 -框架推荐 controller 层主要对用户的请求参数进行处理(校验、转换),然后调用对应的 [service](./service.md) 方法处理业务,得到业务结果后封装并返回: +框架推荐 Controller 层主要对用户的请求参数进行处理(校验、转换),然后调用对应的 [service](./service.md) 方法处理业务,得到业务结果后封装并返回: 1. 获取用户通过 HTTP 传递过来的请求参数。 1. 校验、组装参数。 -1. 调用 service 进行业务处理,必要时处理转换 service 的返回结果,让它适应用户的需求。 +1. 调用 Service 进行业务处理,必要时处理转换 Service 的返回结果,让它适应用户的需求。 1. 通过 HTTP 将结果响应给用户。 -## 如何编写 controller +## 如何编写 Controller -所有的 controller 文件都必须放在 `app/controller` 目录下。controller 支持多种形式进行编写,可以根据不同的项目场景和开发习惯来选择。 +所有的 Controller 文件都必须放在 `app/controller` 目录下。Controller 支持多种形式进行编写,可以根据不同的项目场景和开发习惯来选择。 -### controller 类(推荐) +### Controller 类(推荐) -我们可以通过定义 controller 类的方式来编写代码: +我们可以通过定义 Controller 类的方式来编写代码: ```js // app/controller/post.js @@ -41,7 +41,7 @@ module.exports = app => { // 组装参数 const author = ctx.session.userId; const req = Object.assign(ctx.request.body, { author }); - // 调用 service 进行业务处理 + // 调用 Service 进行业务处理 const res = yield service.post.create(req); // 设置响应内容和响应状态码 ctx.body = { id: res.id }; @@ -53,7 +53,7 @@ module.exports = app => { } ``` -我们通过上面的代码定义了一个 `PostController` 的类,类里面的每一个方法都可以作为一个 controller 在 router 中引用到。 +我们通过上面的代码定义了一个 `PostController` 的类,类里面的每一个方法都可以作为一个 Controller 在 Router 中引用到。 ```js // app/router.js @@ -62,18 +62,18 @@ module.exports = { } ``` -定义的 controller 类,会在每一个请求访问到 server 时实例化一个全新的对象,而项目中的 controller 类继承于 `app.Controller`,会有下面几个属性挂在 `this` 上。 +定义的 Controller 类,会在每一个请求访问到 server 时实例化一个全新的对象,而项目中的 Controller 类继承于 `app.Controller`,会有下面几个属性挂在 `this` 上。 - `this.ctx`: 当前请求的上下文 [Context](./extend.md#context) 对象的实例,通过它我们可以拿到框架封装好的处理当前请求的各种便捷属性和方法。 - `this.app`: 当前应用 [Application](./extend.md#application) 对象的实例,通过它我们可以拿到框架提供的全局对象和方法。 -- `this.service`:应用定义的 [service](./service.md),通过它我们可以访问到抽象出的业务层。 +- `this.service`:应用定义的 [Service](./service.md),通过它我们可以访问到抽象出的业务层。 - `this.config`:应用运行时的[配置项](./config.md)。 #### 自定义 Controller 基类 -按照类的方式编写 controller,不仅可以让我们更好的对 controller 层代码进行抽象(例如将一些统一的处理抽象成一些私有方法),还可以通过自定义 controller 基类的方式封装应用中常用的方法。 +按照类的方式编写 Controller,不仅可以让我们更好的对 Controller 层代码进行抽象(例如将一些统一的处理抽象成一些私有方法),还可以通过自定义 Controller 基类的方式封装应用中常用的方法。 -在[启动自定义](./app-start.md)中,应用可自己定义 controller 基类,这样在 `app/controller` 中编写 controller 时就可以使用到定义在基类上的这些方法了。 +在[启动自定义](./app-start.md)中,应用可自己定义 Controller 基类,这样在 `app/controller` 中编写 Controller 时就可以使用到定义在基类上的这些方法了。 ```js // app.js @@ -99,7 +99,7 @@ module.exports = app => { } ``` -此时在编写应用的 controller 时,可以直接使用基类上的方法: +此时在编写应用的 Controller 时,可以直接使用基类上的方法: ```js //app/controller/post.js @@ -113,11 +113,11 @@ module.exports = app => { }; ``` -### controller 方法 +### Controller 方法 -每一个 controller 都是一个 generator function,它的第一个参数是请求的上下文 [Context](./extend.md#context) 对象的实例,通过它我们可以拿到框架封装好的各种便捷属性和方法。 +每一个 Controller 都是一个 generator function,它的第一个参数是请求的上下文 [Context](./extend.md#context) 对象的实例,通过它我们可以拿到框架封装好的各种便捷属性和方法。 -例如我们写一个对应到 `POST /api/posts` 接口的 controller,我们会在 `app/controller` 目录下创建一个 `post.js` 文件 +例如我们写一个对应到 `POST /api/posts` 接口的 Controller,我们会在 `app/controller` 目录下创建一个 `post.js` 文件 ```js // app/controller/post.js @@ -143,9 +143,9 @@ exports.create = function* (ctx) { ## HTTP 基础 -由于 controller 基本上是业务开发中唯一和 HTTP 协议打交道的地方,在继续往下了解之前,我们首先简单的看一下 HTTP 协议是怎样的。 +由于 Controller 基本上是业务开发中唯一和 HTTP 协议打交道的地方,在继续往下了解之前,我们首先简单的看一下 HTTP 协议是怎样的。 -如果我们发起一个 HTTP 请求来访问前面例子中提到的 controller: +如果我们发起一个 HTTP 请求来访问前面例子中提到的 Controller: ``` curl -X POST http://localhost:3000/api/posts --data '{"title":"controller", "content": "what is controller"}' --header 'Content-Type:application/json; charset=UTF-8' @@ -166,9 +166,9 @@ Content-Type: application/json; charset=UTF-8 - method:这个请求中 method 的值是 `POST`。 - path:值为 `/api/posts`,如果用户的请求中包含 query,也会在这里出现 -从第二行开始直到遇到的第一个空行位置,都是请求的 headers 部分,这一部分中有许多常用的属性,包括这里看到的 Host,Content-Type,还有 `Cookie`,`User-Agent` 等等。在这个请求中有两个头: +从第二行开始直到遇到的第一个空行位置,都是请求的 Headers 部分,这一部分中有许多常用的属性,包括这里看到的 Host,Content-Type,还有 `Cookie`,`User-Agent` 等等。在这个请求中有两个头: -- `Host`:我们在浏览器发起请求的时候,域名会用来通过 DNS 解析找到服务的 ip 地址,但是浏览器也会将域名和端口号放在 Host 头中一并发送给服务端。 +- `Host`:我们在浏览器发起请求的时候,域名会用来通过 DNS 解析找到服务的 IP 地址,但是浏览器也会将域名和端口号放在 Host 头中一并发送给服务端。 - `Content-Type`:当我们的请求有 body 的时候,都会有 Content-Type 来标明我们的请求体是什么格式的。 之后的内容全部都是请求的 body,当请求是 POST, PUT, DELETE 等方法的时候,可以带上请求体,服务端会根据 Content-Type 来解析请求体。 @@ -187,17 +187,17 @@ Connection: keep-alive 第一行中也包含了三段,其中我们常用的主要是[响应状态码](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes),这个例子中它的值是 201,它的含义是在服务端成功创建了一条资源。 -和请求一样,从第二行开始到下一个空行之间都是响应头,这里的 Content-Type, Content-Length 表示这个响应的格式是 json,长度为 8 个字符。 +和请求一样,从第二行开始到下一个空行之间都是响应头,这里的 Content-Type, Content-Length 表示这个响应的格式是 JSON,长度为 8 个字符。 最后剩下的部分就是这次响应真正的内容。 ## 获取 HTTP 请求参数 -从上面的 HTTP 请求示例中可以看到,有好多地方可以放用户的请求数据,框架通过在 controller 上绑定的 context 实例,提供了许多便捷方法和属性获取用户通过 HTTP 请求发送过来的参数。 +从上面的 HTTP 请求示例中可以看到,有好多地方可以放用户的请求数据,框架通过在 Controller 上绑定的 Context 实例,提供了许多便捷方法和属性获取用户通过 HTTP 请求发送过来的参数。 ### query -在 URL 中 `?` 后面的部分是一个 query string,这一部分经常用于 GET 类型的请求中传递参数。例如 `GET /posts?category=egg&language=node` 中 `category=egg&language=node` 就是用户传递过来的参数。我们可以通过 `context.query` 拿到解析过后的这个参数体 +在 URL 中 `?` 后面的部分是一个 Query String,这一部分经常用于 GET 类型的请求中传递参数。例如 `GET /posts?category=egg&language=node` 中 `category=egg&language=node` 就是用户传递过来的参数。我们可以通过 `context.query` 拿到解析过后的这个参数体 ```js exports.listPosts = function* (ctx) { @@ -209,9 +209,9 @@ exports.listPosts = function* (ctx) { }; ``` -当 query string 中的 key 重复时,`context.query` 只取 key 第一次出现时的值,后面再出现的都会被忽略。`GET /posts?category=egg&category=koa` 通过 `context.query` 拿到的值是 `{ category: 'egg' }`。 +当 Query String 中的 key 重复时,`context.query` 只取 key 第一次出现时的值,后面再出现的都会被忽略。`GET /posts?category=egg&category=koa` 通过 `context.query` 拿到的值是 `{ category: 'egg' }`。 -这样处理的原因是为了保持统一性,由于通常情况下我们都不会设计让用户传递 key 相同的 query string,所以我们经常会写类似下面的代码: +这样处理的原因是为了保持统一性,由于通常情况下我们都不会设计让用户传递 key 相同的 Query String,所以我们经常会写类似下面的代码: ```js const key = ctx.query.key || ''; @@ -220,11 +220,11 @@ if (key.startsWith('egg')) { } ``` -而如果有人故意发起请求在 query string 中带上重复的 key 来请求时就会引发系统异常。因此框架保证了从 `context.query` 上获取的参数一旦存在,一定是字符串类型。 +而如果有人故意发起请求在 Query String 中带上重复的 key 来请求时就会引发系统异常。因此框架保证了从 `context.query` 上获取的参数一旦存在,一定是字符串类型。 #### queries -有时候我们的系统会设计成让用户传递相同的 key,例如 `GET /posts?category=egg&id=1&id=2&id=3`。针对此类情况,框架提供了 `context.queries` 对象,这个对象也解析了 query string,但是它不会丢弃任何一个重复的数据,而是将他们都放到一个数组中: +有时候我们的系统会设计成让用户传递相同的 key,例如 `GET /posts?category=egg&id=1&id=2&id=3`。针对此类情况,框架提供了 `context.queries` 对象,这个对象也解析了 Query String,但是它不会丢弃任何一个重复的数据,而是将他们都放到一个数组中: ```js // GET /posts?category=egg&id=1&id=2&id=3 @@ -240,9 +240,9 @@ exports.listPosts = function* (ctx) { `context.queries` 上所有的 key 如果有值,也一定会是数组类型。 -### router params +### Router params -在 [router](./router.md) 中,我们介绍了 router 上也可以申明参数,这些参数都可以通过 `context.params` 获取到。 +在 [Router](./router.md) 中,我们介绍了 Router 上也可以申明参数,这些参数都可以通过 `context.params` 获取到。 ```js // app.get('/projects/:projectId/app/:appId', 'app.listApp'); @@ -261,7 +261,7 @@ exports.listApp = function* (ctx) { - [浏览器中会对 URL 的长度有所限制](http://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers),如果需要传递的参数过多就会无法传递。 - 服务端经常会将访问的完整 URL 记录到日志文件中,有一些敏感数据通过 URL 传递会不安全。 -在前面的 HTTP 请求报文示例中,我们看到在 header 之后还有一个 body 部分,我们通常会在这个部分传递 POST、PUT 和 DELETE 等方法的参数。一般请求中有 body 的时候,客户端(浏览器)会同时发送 `Content-Type` 告诉服务端这次请求的 body 是什么格式的。web 开发中数据传递最常用的两类格式分别是 json 和 form。 +在前面的 HTTP 请求报文示例中,我们看到在 header 之后还有一个 body 部分,我们通常会在这个部分传递 POST、PUT 和 DELETE 等方法的参数。一般请求中有 body 的时候,客户端(浏览器)会同时发送 `Content-Type` 告诉服务端这次请求的 body 是什么格式的。Web 开发中数据传递最常用的两类格式分别是 JSON 和 Form。 框架内置了 [bodyParser](https://github.com/koajs/bodyparser) 中间件来对这两类格式的请求 body 解析成 object 挂载到 `context.request.body` 上。HTTP 协议中并不建议在通过 GET、HEAD 方法访问时传递 body,所以我们无法在 GET、HEAD 方法中按照此方法获取到内容。 @@ -296,13 +296,13 @@ module.exports = { 如果用户的请求 body 超过了我们配置的解析最大长度,会抛出一个状态码为 `413` 的异常,如果用户请求的 body 解析失败(错误的 JSON),会抛出一个状态码为 `400` 的异常。 -**注意:在调整 bodyParser 支持的 body 长度时,如果我们应用前面还有一层反向代理(nginx),可能也需要调整它的配置,确保反向代理也支持同样长度的请求 body。** +**注意:在调整 bodyParser 支持的 body 长度时,如果我们应用前面还有一层反向代理(Nginx),可能也需要调整它的配置,确保反向代理也支持同样长度的请求 body。** ### 获取上传的文件 -请求 body 除了可以带参数之外,还可以发送文件,一般来说,浏览器上都是通过 `Multipart/form-data` 格式发送文件的,框架通过内置 [multipart](https://github.com/eggjs/egg-multipart) 插件来支持获取用户上传的文件。 +请求 body 除了可以带参数之外,还可以发送文件,一般来说,浏览器上都是通过 `Multipart/form-data` 格式发送文件的,框架通过内置 [Multipart](https://github.com/eggjs/egg-multipart) 插件来支持获取用户上传的文件。 -在 controller 中,我们可以通过 `context.getFileStream*()` 接口能获取到上传的文件流。 +在 Controller 中,我们可以通过 `context.getFileStream*()` 接口能获取到上传的文件流。 ```html
@@ -446,7 +446,7 @@ module.exports = { 由于 header 比较特殊,有一些是 `HTTP` 协议规定了具体含义的(例如 `Content-Type`,`Accept`),有些是反向代理设置的,已经约定俗成(X-Forwarded-For),框架也会对他们增加一些便捷的 getter,详细的 getter 可以查看 [API](https://eggjs.org/api/) 文档。 -特别是如果我们通过 `config.proxy = true` 设置了应用部署在反向代理(nginx)之后,有一些 getter 的内部处理会发生改变。 +特别是如果我们通过 `config.proxy = true` 设置了应用部署在反向代理(Nginx)之后,有一些 Getter 的内部处理会发生改变。 #### `context.host` @@ -456,29 +456,29 @@ module.exports = { #### `context.protocol` -通过这个 getter 获取 protocol 时,首先会判断当前连接是否是加密连接,如果是加密连接,返回 https。 +通过这个 Getter 获取 protocol 时,首先会判断当前连接是否是加密连接,如果是加密连接,返回 https。 -如果处于非加密连接时,优先读通过 `config.protocolHeaders` 中配置的 header 的值来判断是 http 还是 https,如果读取不到,我们可以在配置中通过 `config.protocol` 来设置兜底值,默认为 http。 +如果处于非加密连接时,优先读通过 `config.protocolHeaders` 中配置的 header 的值来判断是 HTTP 还是 https,如果读取不到,我们可以在配置中通过 `config.protocol` 来设置兜底值,默认为 HTTP。 `config.protocolHeaders` 默认配置为 `x-forwarded-proto`。 #### `context.ips` -通过 `context.ips` 获取请求经过所有的中间设备 ip 地址列表,只有在 `config.proxy = true` 时,才会通过读取 `config.ipHeaders` 中配置的 header 的值来获取,获取不到时为空数组。 +通过 `context.ips` 获取请求经过所有的中间设备 IP 地址列表,只有在 `config.proxy = true` 时,才会通过读取 `config.ipHeaders` 中配置的 header 的值来获取,获取不到时为空数组。 `config.ipHeaders` 默认配置为 `x-forwarded-for`。 #### `context.ip` -通过 `context.ip` 获取请求发起方的 ip 地址,优先从 `context.ips` 中获取,`context.ips` 为空时使用连接上发起方的 ip 地址。 +通过 `context.ip` 获取请求发起方的 IP 地址,优先从 `context.ips` 中获取,`context.ips` 为空时使用连接上发起方的 IP 地址。 -**注意:ip 和 ips 不同,ip 当 `config.proxy = false` 时会返回当前连接发起者的 ip 地址,ips 此时会为空数组。** +**注意:`ip` 和 `ips` 不同,`ip` 当 `config.proxy = false` 时会返回当前连接发起者的 `ip` 地址,`ips` 此时会为空数组。** -### cookie +### Cookie -HTTP 请求都是无状态的,但是我们的 web 应用通常都需要知道发起请求的人是谁。为了解决这个问题,HTTP 协议设计了一个特殊的请求头:[cookie](https://en.wikipedia.org/wiki/HTTP_cookie)。服务端可以通过响应头(set-cookie)将少量数据响应给客户端,浏览器会遵循协议将数据保存,并在下次请求同一个服务的时候带上(浏览器也会遵循协议,只在访问符合 cookie 指定规则的网站时带上对应的 cookie 来保证安全性)。 +HTTP 请求都是无状态的,但是我们的 Web 应用通常都需要知道发起请求的人是谁。为了解决这个问题,HTTP 协议设计了一个特殊的请求头:[Cookie](https://en.wikipedia.org/wiki/HTTP_cookie)。服务端可以通过响应头(set-cookie)将少量数据响应给客户端,浏览器会遵循协议将数据保存,并在下次请求同一个服务的时候带上(浏览器也会遵循协议,只在访问符合 Cookie 指定规则的网站时带上对应的 Cookie 来保证安全性)。 -通过 `context.cookies`,我们可以在 controller 中便捷、安全的设置和读取 cookie。 +通过 `context.cookies`,我们可以在 Controller 中便捷、安全的设置和读取 Cookie。 ```js exports.add = function* (ctx) { @@ -494,22 +494,22 @@ exports.remove = function* (ctx) { }; ``` -cookie 虽然在 HTTP 中只是一个头,但是通过 `foo=bar;foo1=bar1;` 的格式可以设置多个键值对。 +Cookie 虽然在 HTTP 中只是一个头,但是通过 `foo=bar;foo1=bar1;` 的格式可以设置多个键值对。 -cookie 在 web 应用中经常承担了传递客户端身份信息的作用,因此有许多安全相关的配置,不可忽视, [cookie](../core/cookie-and-session.md#cookie) 文档中详细介绍了 cookie 的用法和安全相关的配置项,可以深入阅读了解。 +Cookie 在 Web 应用中经常承担了传递客户端身份信息的作用,因此有许多安全相关的配置,不可忽视, [Cookie](../core/cookie-and-session.md#cookie) 文档中详细介绍了 Cookie 的用法和安全相关的配置项,可以深入阅读了解。 -### session +### Session -通过 cookie,我们可以给每一个用户设置一个 session,用来存储用户身份相关的信息,这份信息会加密后存储在 cookie 中,实现跨请求的用户身份保持。 +通过 Cookie,我们可以给每一个用户设置一个 Session,用来存储用户身份相关的信息,这份信息会加密后存储在 Cookie 中,实现跨请求的用户身份保持。 -框架内置了 [session](https://github.com/eggjs/egg-session) 插件,给我们提供了 `context.session` 来访问或者修改当前用户 session 。 +框架内置了 [Session](https://github.com/eggjs/egg-session) 插件,给我们提供了 `context.session` 来访问或者修改当前用户 Session 。 ```js exports.fetchPosts = function* (ctx) { - // 获取 session 上的内容 + // 获取 Session 上的内容 const userId = ctx.session.userId; const posts = yield ctx.service.post.fetch(userId); - // 修改 session 的值 + // 修改 Session 的值 ctx.session.visited = ctx.session.visited ? ctx.session.visited++ : 1; ctx.body = { success: true, @@ -518,7 +518,7 @@ exports.fetchPosts = function* (ctx) { }; ``` -session 的使用方法非常直观,直接读取它或者修改它就可以了,如果要删除它,直接将它赋值为 null: +Session 的使用方法非常直观,直接读取它或者修改它就可以了,如果要删除它,直接将它赋值为 `null`: ```js exports.deleteSession = function* (ctx) { @@ -526,16 +526,16 @@ exports.deleteSession = function* (ctx) { }; ``` -和 cookie 一样,session 也有许多安全等选项和功能,在使用之前也最好阅读 [session](../core/cookie-and-session.md#session) 文档深入了解。 +和 Cookie 一样,Session 也有许多安全等选项和功能,在使用之前也最好阅读 [Session](../core/cookie-and-session.md#session) 文档深入了解。 #### 配置 -对于 session 来说,主要有下面几个属性可以在 `config.default.js` 中进行配置: +对于 Session 来说,主要有下面几个属性可以在 `config.default.js` 中进行配置: ```js module.exports = { - key: 'EGG_SESS', // 承载 session 的 cookie 键值对名字 - maxAge: 86400000, // session 的最大有效时间 + key: 'EGG_SESS', // 承载 Session 的 Cookie 键值对名字 + maxAge: 86400000, // Session 的最大有效时间 }; ``` @@ -543,7 +543,7 @@ module.exports = { 在获取到用户请求的参数后,不可避免的要对参数进行一些校验。 -借助 [validate](https://github.com/eggjs/egg-validate) 插件提供便捷的参数校验机制,帮助我们完成各种复杂的参数校验。 +借助 [Validate](https://github.com/eggjs/egg-validate) 插件提供便捷的参数校验机制,帮助我们完成各种复杂的参数校验。 ```js // config/plugin.js @@ -583,7 +583,7 @@ exports.create = function* (ctx) { ### 校验规则 -参数校验通过 [parameter](https://github.com/node-modules/parameter#rule) 完成,支持的校验规则可以在该模块的文档中查阅到。 +参数校验通过 [Parameter](https://github.com/node-modules/parameter#rule) 完成,支持的校验规则可以在该模块的文档中查阅到。 #### 自定义校验规则 @@ -600,7 +600,7 @@ app.validator.addRule('json', (rule, value) => { }); ``` -添加完自定义规则之后,就可以在 controller 中直接使用这条规则来进行参数校验了 +添加完自定义规则之后,就可以在 Controller 中直接使用这条规则来进行参数校验了 ```js exports.handler = function* (ctx) { @@ -610,11 +610,11 @@ exports.handler = function* (ctx) { }; ``` -## 调用 service +## 调用 Service -我们并不想在 controller 中实现太多业务逻辑,所以提供了一个 [service](./service.md) 层进行业务逻辑的封装,这不仅能提高代码的复用性,同时可以让我们的业务逻辑更好测试。 +我们并不想在 Controller 中实现太多业务逻辑,所以提供了一个 [Service](./service.md) 层进行业务逻辑的封装,这不仅能提高代码的复用性,同时可以让我们的业务逻辑更好测试。 -在 controller 中可以调用任何一个 service 上的任何方法,同时 service 是懒加载的,只有当访问到它的时候框架才会去实例化它。 +在 Controller 中可以调用任何一个 Service 上的任何方法,同时 Service 是懒加载的,只有当访问到它的时候框架才会去实例化它。 ```js exports.create = function* (ctx) { @@ -627,17 +627,17 @@ exports.create = function* (ctx) { }; ``` -service 的具体写法,请查看 [service](./service.md) 章节。 +Service 的具体写法,请查看 [Service](./service.md) 章节。 ## 发送 HTTP 响应 -当业务逻辑完成之后,controller 的最后一个职责就是将业务逻辑的处理结果通过 HTTP 响应发送给用户。 +当业务逻辑完成之后,Controller 的最后一个职责就是将业务逻辑的处理结果通过 HTTP 响应发送给用户。 ### 设置 status HTTP 设计了非常多的[状态码](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes),每一个状态码都代表了一个特定的含义,通过设置正确的状态码,可以让响应更符合语义。 -框架提供了一个便捷的 setter 来进行状态码的设置 +框架提供了一个便捷的 Setter 来进行状态码的设置 ```js exports.create = function* (ctx) { @@ -669,7 +669,7 @@ exports.page = function* (ctx) { }; ``` -由于 node 的流式特性,我们还有很多场景需要通过 stream 返回响应,例如返回一个大文件,代理服务器直接返回上游的内容,框架也支持直接将 body 设置成一个 stream,并会同时处理好这个 stream 上的错误事件。 +由于 Node.js 的流式特性,我们还有很多场景需要通过 Stream 返回响应,例如返回一个大文件,代理服务器直接返回上游的内容,框架也支持直接将 body 设置成一个 Stream,并会同时处理好这个 stream 上的错误事件。 ```js exports.proxy = function* (ctx) { @@ -684,9 +684,9 @@ exports.proxy = function* (ctx) { #### 渲染模板 -通常来说,我们不会手写 html 页面,而是会通过模板引擎进行生成。 -egg 自身没有集成任何一个模板引擎,但是约定了 [view 插件的规范](../advanced/view-plugin.md),通过接入的模板引擎,可以直接使用 `ctx.render(template)` 来渲染模板生成 html。 -具体示例可以查看 [模板渲染](../core/view.md)。 +通常来说,我们不会手写 HTML 页面,而是会通过模板引擎进行生成。 +Egg 自身没有集成任何一个模板引擎,但是约定了 [View 插件的规范](../advanced/view-plugin.md),通过接入的模板引擎,可以直接使用 `ctx.render(template)` 来渲染模板生成 html。 +具体示例可以查看[模板渲染](../core/view.md)。 #### JSONP @@ -705,7 +705,7 @@ module.exports = app => { }; ``` -- 在 controller 中,只需要正常编写即可: +- 在 Controller 中,只需要正常编写即可: ```js // app/controller/posts.js @@ -734,7 +734,7 @@ exports.jsonp = { 通过上面的方式配置之后,如果用户请求 `/api/posts/1?callback=fn`,响应为 JSONP 格式,如果用户请求 `/api/posts/1`,响应格式为 JSON。 -我们同样可以在 `app.jsonp()` 创建中间件时覆盖默认的配置,以达到不同路由使用不同配置的目的: +我们同样可以在 `app.jsonp()` 创建中间件时覆盖默认的配置,以达到不同路由使用不同配置的目的: ```js // app/router.js @@ -767,9 +767,9 @@ module.exports = { }; ``` -**注意,CSRF 校验依赖于 [security](../core/security.md) 插件提供的基于 cookie 的 CSRF 校验。** +**注意,CSRF 校验依赖于 [security](../core/security.md) 插件提供的基于 Cookie 的 CSRF 校验。** -在开启 CSRF 校验时,客户端在发起 JSONP 请求时,也要带上 CSRF token,如果发起 JSONP 的请求方所在的页面和我们的服务在同一个主域名之下的话,可以读取到 cookie 中的 CSRF token(在 CSRF token 缺失时也可以自行设置 CSRF token 到 cookie 中),并在请求时带上该 token。 +在开启 CSRF 校验时,客户端在发起 JSONP 请求时,也要带上 CSRF token,如果发起 JSONP 的请求方所在的页面和我们的服务在同一个主域名之下的话,可以读取到 Cookie 中的 CSRF token(在 CSRF token 缺失时也可以自行设置 CSRF token 到 Cookie 中),并在请求时带上该 token。 ##### referrer 校验 @@ -798,7 +798,7 @@ exports.jsonp = { // http://test.com/ ``` -- 字符串:设置字符串形式的白名单时分为两种,当字符串以 `.` 开头,例如 `.test.com` 时,代表 referrer 白名单为 `test.com` 的所有子域名,包括 `test.com` 自身。当字符串不以 `.` 开头,例如 `sub.test.com`,代表 referrer 白名单为 `sub.test.com` 这一个域名。(同时支持 http 和 https)。 +- 字符串:设置字符串形式的白名单时分为两种,当字符串以 `.` 开头,例如 `.test.com` 时,代表 referrer 白名单为 `test.com` 的所有子域名,包括 `test.com` 自身。当字符串不以 `.` 开头,例如 `sub.test.com`,代表 referrer 白名单为 `sub.test.com` 这一个域名。(同时支持 HTTP 和 HTTPS)。 ```js exports.jsonp = { @@ -833,11 +833,11 @@ exports.jsonp = { **当 CSRF 和 referrer 校验同时开启时,请求发起方只需要满足任意一个条件即可通过 JSONP 的安全校验。** -### 设置 header +### 设置 Header -我们通过状态码标识请求成功与否、状态如何,在 body 中设置响应的内容。而通过响应的 header,还可以设置一些扩展信息。 +我们通过状态码标识请求成功与否、状态如何,在 body 中设置响应的内容。而通过响应的 Header,还可以设置一些扩展信息。 -通过 `context.set(key, value)` 方法可以设置一个响应头,`context.set(headers)` 设置多个 header。 +通过 `context.set(key, value)` 方法可以设置一个响应头,`context.set(headers)` 设置多个 Header。 ```js // app/controller/api.js diff --git a/docs/source/zh-cn/basics/env.md b/docs/source/zh-cn/basics/env.md index 4e2f09d2bb..be0bff3fde 100644 --- a/docs/source/zh-cn/basics/env.md +++ b/docs/source/zh-cn/basics/env.md @@ -1,9 +1,9 @@ title: 运行环境 --- -一个 web 应用在流程的各个阶段可能存在各种差异,而应用本身应该是无状态的,所以提供环境变量可以让应用不感知差异,只需要定义不同的配置。 +一个 Web 应用在流程的各个阶段可能存在各种差异,而应用本身应该是无状态的,所以提供环境变量可以让应用不感知差异,只需要定义不同的配置。 -框架提供了 env 这个变量来决定此次的运行环境,可以通过这个来决定使用哪些配置,或直接判断。 +框架提供了 `env` 这个变量来决定此次的运行环境,可以通过这个来决定使用哪些配置,或直接判断。 ## 如何指定环境 @@ -12,7 +12,7 @@ title: 运行环境 1. 在 `config/env` 文件指定,一般通过构建工具来生成这个文件,该文件的内容就是环境变量的值,如 `prod`。 1. 通过 `EGG_SERVER_ENV` 环境变量指定。 -EGG_SERVER_ENV 会比较常用,在应用启动的时候指定是最简单的方式,比如在生产环境启动应用。 +`EGG_SERVER_ENV` 会比较常用,在应用启动的时候指定是最简单的方式,比如在生产环境启动应用。 ```shell EGG_SERVER_ENV=prod npm start @@ -22,11 +22,11 @@ EGG_SERVER_ENV=prod npm start 可以通过 `app.config.env` 来获取这个环境变量直接判断环境。 -也可以通过环境加载不同的配置,比如增加 `config/config.{env}.js` 这样的配置,可以在指定环境加载指定配置,具体看 [config 配置](./config.md) +也可以通过环境加载不同的配置,比如增加 `config/config.{env}.js` 这样的配置,可以在指定环境加载指定配置,具体看 [Config 配置](./config.md) ## 与 NODE_ENV 的区别 -很多 node 应用会使用 `NODE_ENV` 来区分环境,但 `EGG_SERVER_ENV` 会区分的更加精细。一般的项目开发流程包括本地开发环境、测试环境、生产环境,除了本地开发环境和单元测试环境外都为**服务器环境**,该环境的 `NODE_ENV` 都应该为 production。而且 npm 也会使用这个变量,在应用部署的时候一般不会安装 devDependencies,所以这个值也应该为 production。 +很多 Node.js 应用会使用 `NODE_ENV` 来区分环境,但 `EGG_SERVER_ENV` 会区分的更加精细。一般的项目开发流程包括本地开发环境、测试环境、生产环境,除了本地开发环境和单元测试环境外都为**服务器环境**,该环境的 `NODE_ENV` 都应该为 `production`。而且 npm 也会使用这个变量,在应用部署的时候一般不会安装 devDependencies,所以这个值也应该为 `production`。 框架默认支持的环境及映射关系(如果未指定 `EGG_SERVER_ENV` 会根据 `NODE_ENV` 来匹配) @@ -36,7 +36,7 @@ NODE_ENV | EGG_SERVER_ENV | 说明 test | unittest | 单元测试 production | prod | 生产环境 -例如,当 `NODE_ENV` 为 production 而 `EGG_SERVER_ENV` 未指定时,框架会将 EGG_SERVER_ENV 设置成 prod。 +例如,当 `NODE_ENV` 为 `production` 而 `EGG_SERVER_ENV` 未指定时,框架会将 `EGG_SERVER_ENV` 设置成 `prod`。 ## 自定义环境 @@ -44,6 +44,6 @@ production | prod | 生产环境 将 `EGG_SERVER_ENV` 设置成 `sit`(并建议设置 `NODE_ENV = production`),启动时会加载 `config/config.sit.js`,运行环境变量 `app.config.env` 会被设置成 `sit`。 -## 与 koa 的区别 +## 与 Koa 的区别 -在 koa 中我们通过 `app.env` 来进行环境判断,`app.env` 默认的值是 `process.env.NODE_ENV`。但是在 egg(和基于 egg 的框架)中,配置统一都放置在 `app.config` 上,所以我们需要通过 `app.config.env` 来区分环境,`app.env` 不再使用。 +在 Koa 中我们通过 `app.env` 来进行环境判断,`app.env` 默认的值是 `process.env.NODE_ENV`。但是在 Egg(和基于 Egg 的框架)中,配置统一都放置在 `app.config` 上,所以我们需要通过 `app.config.env` 来区分环境,`app.env` 不再使用。 diff --git a/docs/source/zh-cn/basics/extend.md b/docs/source/zh-cn/basics/extend.md index 828b653eb9..c8405071b5 100644 --- a/docs/source/zh-cn/basics/extend.md +++ b/docs/source/zh-cn/basics/extend.md @@ -13,13 +13,13 @@ title: 框架扩展 ## Application -Application 对象指的是 koa 的全局应用对象,全局只有一个,在应用启动时被创建。 +`app` 对象指的是 Koa 的全局应用对象,全局只有一个,在应用启动时被创建。 ### 访问方式 - `ctx.app` -- controller,middleware,helper,service 中都可以通过 `this.app` 访问到 application 对象,例如 `this.app.config` 访问配置对象。 -- 在 `app.js` 中 app 对象会作为第一个参数注入到入口函数中 +- Controller,Middleware,Helper,Service 中都可以通过 `this.app` 访问到 Application 对象,例如 `this.app.config` 访问配置对象。 +- 在 `app.js` 中 `app` 对象会作为第一个参数注入到入口函数中 ```js // app.js @@ -30,7 +30,7 @@ Application 对象指的是 koa 的全局应用对象,全局只有一个,在 ### 扩展方式 -框架会把 `app/extend/application.js` 中定义的对象与 koa application 的 prototype 对象进行合并,在应用启动时会基于扩展后的 prototype 生成 app 对象。 +框架会把 `app/extend/application.js` 中定义的对象与 Koa Application 的 prototype 对象进行合并,在应用启动时会基于扩展后的 prototype 生成 `app` 对象。 #### 方法扩展 @@ -49,9 +49,9 @@ module.exports = { 一般来说属性的计算只需要进行一次,那么一定要实现缓存,否则在多次访问属性时会计算多次,这样会降低应用性能。 -推荐的方式是使用 Symbol + getter 的模式。 +推荐的方式是使用 Symbol + Getter 的模式。 -例如,增加一个 `app.bar` 属性 getter: +例如,增加一个 `app.bar` 属性 Getter: ```js // app/extend/application.js @@ -71,7 +71,7 @@ module.exports = { ## Context -Context 指的是 koa 的请求上下文,这是 **请求级别** 的对象,每次请求生成一个 context 实例,通常我们也简写成 ctx。在所有的文档中,context 和 ctx 都是指 koa 的上下文对象。 +Context 指的是 Koa 的请求上下文,这是 **请求级别** 的对象,每次请求生成一个 Context 实例,通常我们也简写成 `ctx`。在所有的文档中,Context 和 `ctx` 都是指 Koa 的上下文对象。 ### 访问方式 @@ -80,7 +80,7 @@ Context 指的是 koa 的请求上下文,这是 **请求级别** 的对象, ### 扩展方式 -框架会把 `app/extend/context.js` 中定义的对象与 koa context 的 prototype 对象进行合并,在处理请求时会基于扩展后的 prototype 生成 ctx 对象。 +框架会把 `app/extend/context.js` 中定义的对象与 Koa Context 的 prototype 对象进行合并,在处理请求时会基于扩展后的 prototype 生成 ctx 对象。 #### 方法扩展 @@ -99,9 +99,9 @@ module.exports = { 一般来说属性的计算在同一次请求中只需要进行一次,那么一定要实现缓存,否则在同一次请求中多次访问属性时会计算多次,这样会降低应用性能。 -推荐的方式是使用 Symbol + getter 的模式。 +推荐的方式是使用 Symbol + Getter 的模式。 -例如,增加一个 `ctx.bar` 属性 getter: +例如,增加一个 `ctx.bar` 属性 Getter: ```js // app/extend/context.js @@ -121,7 +121,7 @@ module.exports = { ## Request -Request 对象和 koa 的 request 对象相同,是 **请求级别** 的对象,它提供了大量请求相关的属性和方法供使用。 +Request 对象和 Koa 的 Request 对象相同,是 **请求级别** 的对象,它提供了大量请求相关的属性和方法供使用。 ### 访问方式 @@ -129,15 +129,15 @@ Request 对象和 koa 的 request 对象相同,是 **请求级别** 的对象 ctx.request ``` -ctx 上的很多属性和方法都被代理到 request 对象上,对于这些属性和方法使用 ctx 和使用 request 去访问它们是等价的,例如 `ctx.url === ctx.request.url`。 +`ctx` 上的很多属性和方法都被代理到 `request` 对象上,对于这些属性和方法使用 `ctx` 和使用 `request` 去访问它们是等价的,例如 `ctx.url === ctx.request.url`。 -koa 内置的代理 request 的属性和方法列表:[koajs request aliases](http://koajs.com/#request-aliases) +Koa 内置的代理 `request` 的属性和方法列表:[Koa - Request aliases](http://koajs.com/#request-aliases) ### 扩展方式 -框架会把 `app/extend/request.js` 中定义的对象与内置 request 的 prototype 对象进行合并,在处理请求时会基于扩展后的 prototype 生成 request 对象。 +框架会把 `app/extend/request.js` 中定义的对象与内置 `request` 的 prototype 对象进行合并,在处理请求时会基于扩展后的 prototype 生成 `request` 对象。 -例如,增加一个 `request.foo` 属性 getter: +例如,增加一个 `request.foo` 属性 Getter: ```js // app/extend/request.js @@ -150,7 +150,7 @@ module.exports = { ## Response -Response 对象和 koa 的 response 对象相同,是 **请求级别** 的对象,它提供了大量响应相关的属性和方法供使用。 +Response 对象和 Koa 的 Response 对象相同,是 **请求级别** 的对象,它提供了大量响应相关的属性和方法供使用。 ### 访问方式 @@ -158,13 +158,13 @@ Response 对象和 koa 的 response 对象相同,是 **请求级别** 的对 ctx.response ``` -ctx 上的很多属性和方法都被代理到 response 对象上,对于这些属性和方法使用 ctx 和使用 response 去访问它们是等价的,例如 `ctx.status = 404` 和 `ctx.response.status = 404` 是等价的。 +ctx 上的很多属性和方法都被代理到 `response` 对象上,对于这些属性和方法使用 `ctx` 和使用 `response` 去访问它们是等价的,例如 `ctx.status = 404` 和 `ctx.response.status = 404` 是等价的。 -koa 内置的代理 response 的属性和方法列表:[koa response aliases](http://koajs.com/#response-aliases) +Koa 内置的代理 `response` 的属性和方法列表:[Koa Response aliases](http://koajs.com/#response-aliases) ### 扩展方式 -框架会把 `app/extend/response.js` 中定义的对象与内置 response 的 prototype 对象进行合并,在处理请求时会基于扩展后的 prototype 生成 response 对象。 +框架会把 `app/extend/response.js` 中定义的对象与内置 `response` 的 prototype 对象进行合并,在处理请求时会基于扩展后的 prototype 生成 `response` 对象。 例如,增加一个 `response.foo` 属性 setter: @@ -202,7 +202,7 @@ ctx.helper.pathFor('home', { by: 'recent', limit: 20 }) ### 扩展方式 -框架会把 `app/extend/helper.js` 中定义的对象与内置 helper 的 prototype 对象进行合并,在处理请求时会基于扩展后的 prototype 生成 helper 对象。 +框架会把 `app/extend/helper.js` 中定义的对象与内置 `helper` 的 prototype 对象进行合并,在处理请求时会基于扩展后的 prototype 生成 `helper` 对象。 例如,增加一个 `helper.foo()` 方法: @@ -232,4 +232,4 @@ module.exports = { 这个文件只会在 unittest 环境加载。 -同理,对于 application,context,request,response,helper 都可以使用这种方式针对某个环境进行扩展,更多参见[运行环境](./env.md)。 +同理,对于 Application,Context,Request,Response,Helper 都可以使用这种方式针对某个环境进行扩展,更多参见[运行环境](./env.md)。 diff --git a/docs/source/zh-cn/basics/middleware.md b/docs/source/zh-cn/basics/middleware.md index b9ab6dd26b..97a48d9b20 100644 --- a/docs/source/zh-cn/basics/middleware.md +++ b/docs/source/zh-cn/basics/middleware.md @@ -1,7 +1,7 @@ -title: 中间件 +title: Middleware 中间件 --- -在[前面的章节](../intro/egg-and-koa.md)中,我们介绍了 egg 是基于 koa 1 实现的,所以 egg 的中间件形式和 koa 1 的中间件形式是一样的,都是基于 generator function 的[洋葱圈模型](../intro/egg-and-koa.md#midlleware)。每次我们编写一个中间件,就相当于在洋葱外面包了一层。 +在[前面的章节](../intro/egg-and-koa.md)中,我们介绍了 Egg 是基于 Koa 1 实现的,所以 Egg 的中间件形式和 Koa 1 的中间件形式是一样的,都是基于 generator function 的[洋葱圈模型](../intro/egg-and-koa.md#midlleware)。每次我们编写一个中间件,就相当于在洋葱外面包了一层。 ## 编写中间件 @@ -27,7 +27,7 @@ function* gzip(next) { } ``` -可以看到,框架的中间件和 koa 的中间件写法是一模一样的,所以任何 koa 的中间件都可以直接被框架使用。 +可以看到,框架的中间件和 Koa 的中间件写法是一模一样的,所以任何 Koa 的中间件都可以直接被框架使用。 ### 配置 @@ -84,7 +84,7 @@ module.exports = { ## 框架默认中间件 -除了应用层引入中间件之外,框架自身和其他的插件也会引入许多中间件。所有的这些自带中间件的配置项都通过在配置中修改中间件同名配置项进行修改,例如 [框架自带的中间件](https://github.com/eggjs/egg/tree/master/app/middleware)中有一个 bodyParser 中间件(框架的加载器会将文件名中的各种分隔符都修改成驼峰形式的变量名),我们想要修改 bodyParser 的配置,只需要在 `config/config.default.js` 中编写 +除了应用层引入中间件之外,框架自身和其他的插件也会引入许多中间件。所有的这些自带中间件的配置项都通过在配置中修改中间件同名配置项进行修改,例如[框架自带的中间件](https://github.com/eggjs/egg/tree/master/app/middleware)中有一个 bodyParser 中间件(框架的加载器会将文件名中的各种分隔符都修改成驼峰形式的变量名),我们想要修改 bodyParser 的配置,只需要在 `config/config.default.js` 中编写 ```js module.exports = { @@ -98,7 +98,7 @@ module.exports = { ## router 中使用中间件 -应用层定义的中间件和框架默认中间件都会被加载器加载,并挂载到 `app.middlewares` 上(注意:此处为复数,因为 `app.middleware` 在 koa 中另有用处),所以应用层定义的中间件可以不通过配置引入,而是在 router 中引入,从而只对对应的路由生效。 +应用层定义的中间件和框架默认中间件都会被加载器加载,并挂载到 `app.middlewares` 上(注意:此处为复数,因为 `app.middleware` 在 Koa 中另有用处),所以应用层定义的中间件可以不通过配置引入,而是在 router 中引入,从而只对对应的路由生效。 还是拿刚才的 gzip 中间件举例,当我们想直接在 router 中使用的时候,在 `app/router.js` 中就可以这样写 @@ -109,17 +109,17 @@ module.exports = app => { } ``` -## 使用 koa 的中间件 +## 使用 Koa 的中间件 -框架兼容 koa 1.x 和 2.x 支持的所有形式的中间件,包括: +框架兼容 Koa 1.x 和 2.x 支持的所有形式的中间件,包括: - generator function: `function* (next) {}` - async function: `async (ctx, next) => {}` - common function: `(ctx, next) => {}` -所有可以在 koa 中使用的中间件都可以直接在框架中使用。 +所有可以在 Koa 中使用的中间件都可以直接在框架中使用。 -以 [koa-compress](https://github.com/koajs/compress) 为例,在 koa 中使用时: +以 [koa-compress](https://github.com/koajs/compress) 为例,在 Koa 中使用时: ```js var koa = require('koa'); @@ -131,7 +131,7 @@ const options = { threshold: 2048 }; app.use(compress(options)); ``` -我们按照框架的规范来在应用中引入这个 koa 的中间件: +我们按照框架的规范来在应用中引入这个 Koa 的中间件: ```js // app/middleware/compress.js diff --git a/docs/source/zh-cn/basics/router.md b/docs/source/zh-cn/basics/router.md index 3905dbb8f5..91fe6f3bcb 100644 --- a/docs/source/zh-cn/basics/router.md +++ b/docs/source/zh-cn/basics/router.md @@ -1,14 +1,14 @@ -title: router +title: Router 路由 --- -router 主要用来描述请求 URL 和具体承担执行动作的 controller 的对应关系, +Router 主要用来描述请求 URL 和具体承担执行动作的 Controller 的对应关系, 框架约定了 `app/router.js` 文件用于统一所有路由规则。 通过统一的配置,我们可以避免路由规则逻辑散落在多个地方,从而出现未知的冲突,集中在一起我们可以更方便的来查看全局的路由规则。 -## 如何定义 router +## 如何定义 Router -- `app/router.js` 里面定义 url 路由规则 +- `app/router.js` 里面定义 URL 路由规则 ```js // app/router.js @@ -17,7 +17,7 @@ module.exports = app => { }; ``` -- `app/controller` 目录下面实现 controller +- `app/controller` 目录下面实现 Controller ```js // app/controller/user.js @@ -28,11 +28,11 @@ exports.info = function* () { }; ``` -这样就完成了一个最简单的 router 定义,当用户执行 `GET /user/123`,`user.js` 这个里面的 info 方法就会执行。 +这样就完成了一个最简单的 Router 定义,当用户执行 `GET /user/123`,`user.js` 这个里面的 info 方法就会执行。 -## router 详细定义说明 +## Router 详细定义说明 -下面是路由的完整定义,参数可以根据场景的不同,自由选择。 +下面是路由的完整定义,参数可以根据场景的不同,自由选择: ```js app.verb('path-match', 'controller.action'); @@ -53,18 +53,18 @@ app.verb('router-name', 'path-match', middleware1, ..., middlewareN, 'controller * app.delete - DELETE * app.del - 由于 delete 是一个保留字,所以提供了一个 delete 方法的别名。 * app.redirect - 可以对 URL 进行重定向处理,比如我们最经常使用的可以把用户访问的根目录路由到某个主页。 -- router-name 给路由设定一个别名,可以通过 helper 提供的辅助函数 pathFor 和 urlFor 来生成 url。(可选) +- router-name 给路由设定一个别名,可以通过 Helper 提供的辅助函数 `pathFor` 和 `urlFor` 来生成 URL。(可选) - path-match - 路由 URL 路径。 -- middleware1 - 在 router 里面可以配置多个 middleware。(可选) -- controller.action - 注意是字符串,框架会自动从 `app/controller` 目录中区查找同名 controller, -并且把处理指定到配置的 action 方法。如果 controller 文件直接 export 一个方法,可以省略 action。 +- middleware1 - 在 Router 里面可以配置多个 Middleware。(可选) +- controller.action - 注意是字符串,框架会自动从 `app/controller` 目录中区查找同名 Controller, +并且把处理指定到配置的 action 方法。如果 Controller 文件直接 export 一个方法,可以省略 action。 ### 注意事项 -- 在 router 定义中, 可以支持多个 middleware 串联执行 -- controller 必须定义在 `app/controller` 目录中,并且对应的函数一定要是 generator function。 -- 一个文件里面也可以包含多个 controller 定义,在定义路由的时候, -可以通过 `controller-filename.function-name` 的方式指定对应的 controller。 +- 在 Router 定义中, 可以支持多个 Middleware 串联执行 +- Controller 必须定义在 `app/controller` 目录中,并且对应的函数一定要是 generator function。 +- 一个文件里面也可以包含多个 Controller 定义,在定义路由的时候, +可以通过 `controller-filename.function-name` 的方式指定对应的 Controller。 下面是一些路由定义的方式: @@ -87,7 +87,7 @@ module.exports = app => { }; ``` -上面代码就在 `/posts` 路径上部署了一组 CRUD 路径结构,对应的 controller 为 `app/controller/posts.js` 接下来, +上面代码就在 `/posts` 路径上部署了一组 CRUD 路径结构,对应的 Controller 为 `app/controller/posts.js` 接下来, 你只需要在 `posts.js` 里面实现对应的函数就可以了。 Method | Path | Route Name | Controller.Action @@ -117,15 +117,15 @@ exports.update = function* () {}; exports.destroy = function* () {}; ``` -如果我们不需要其中的某几个方法,可以不用在 `posts.js` 里面实现,这样对应 URL 路径也不会注册到 router。 +如果我们不需要其中的某几个方法,可以不用在 `posts.js` 里面实现,这样对应 URL 路径也不会注册到 Router。 -## router 实战 +## destroy 实战 -下面通过更多实际的例子,来说明 router 的用法。 +下面通过更多实际的例子,来说明 destroy 的用法。 ### 参数获取 -#### query 方式 +#### Query String 方式 ```js // app/router.js @@ -159,7 +159,7 @@ exports.info = function* () { #### 复杂参数的获取 -路由里面也支持定义正则,可以更加灵活的获取参数 +路由里面也支持定义正则,可以更加灵活的获取参数: ```js // app/router.js @@ -285,7 +285,7 @@ module.exports = function* () { ### 中间件的使用 如果我们想把用户某一类请求的参数都大写,可以通过中间件来实现。 -这里我们只是简单说明下如何使用中间件,更多请查看[中间件](./middleware.md)。 +这里我们只是简单说明下如何使用中间件,更多请查看 [中间件](./middleware.md)。 ```js // app/controller/search.js diff --git a/docs/source/zh-cn/basics/schedule.md b/docs/source/zh-cn/basics/schedule.md index 53663b8a13..d4e4ee8736 100644 --- a/docs/source/zh-cn/basics/schedule.md +++ b/docs/source/zh-cn/basics/schedule.md @@ -1,7 +1,7 @@ title: 定时任务 --- -虽然我们通过框架开发的 http server 是请求响应模型的,但是仍然还会有许多场景需要执行一些定时任务,例如: +虽然我们通过框架开发的 HTTP Server 是请求响应模型的,但是仍然还会有许多场景需要执行一些定时任务,例如: 1. 定时上报应用状态。 1. 定时从远程接口更新本地缓存。 @@ -32,7 +32,7 @@ module.exports = { }; ``` -编写完这个文件就意味着一个定时任务定义完成了。这个定时任务会在每一个 worker 进程上每 1 分钟执行一次,将远程数据请求回来挂载到 `app.cache` 上。 +编写完这个文件就意味着一个定时任务定义完成了。这个定时任务会在每一个 Worker 进程上每 1 分钟执行一次,将远程数据请求回来挂载到 `app.cache` 上。 ### 定时方式 diff --git a/docs/source/zh-cn/basics/service.md b/docs/source/zh-cn/basics/service.md index 5316f2769c..1a2baff565 100644 --- a/docs/source/zh-cn/basics/service.md +++ b/docs/source/zh-cn/basics/service.md @@ -1,18 +1,18 @@ -title: service +title: Service --- -简单来说,service 就是在复杂业务场景下用于做业务逻辑封装的一个抽象层,提供这个抽象有以下几个好处: +简单来说,Service 就是在复杂业务场景下用于做业务逻辑封装的一个抽象层,提供这个抽象有以下几个好处: -- 保持 controller 中的逻辑更加简洁。 -- 保持业务逻辑的独立性,抽象出来的 service 可以被多个 controller 重复调用。 -- 将逻辑和展现分离,更容易编写测试用例,测试用例的编写具体可以查看 [这里](../core/unittest.md)。 +- 保持 Controller 中的逻辑更加简洁。 +- 保持业务逻辑的独立性,抽象出来的 Service 可以被多个 Controller 重复调用。 +- 将逻辑和展现分离,更容易编写测试用例,测试用例的编写具体可以查看[这里](../core/unittest.md)。 ## 使用场景 - 复杂数据的处理,比如要展现的信息需要从数据库获取,还要经过一定的规则计算,才能返回用户显示。或者计算完成后,更新到数据库。 - 第三方服务的调用,比如 GitHub 信息获取等。 -## 定义 service +## 定义 Service - `app/service/user.js` @@ -30,30 +30,30 @@ title: service ### 注意事项 -- service 文件必须放在 `app/service` 目录,可以支持多级目录,访问的时候可以通过目录名级联访问。 +- Service 文件必须放在 `app/service` 目录,可以支持多级目录,访问的时候可以通过目录名级联访问。 ```js - app/service/biz/user.js => this.service.biz.user.find + app/service/biz/user.js => this.service.biz.user app/service/sync_user.js => this.service.syncUser app/service/HackerNews.js => this.service.hackerNews ``` -- 一个 service 文件只能包含一个类, 这个类需要通过 `module.exports` 的方式返回。 -- service 需要通过 Class 的方式定义,父类必须是 `app.Service`, 其中 `app.Service` 会在初始化 service 的时候通过参数传递进来。 -- service 不是单例,是 **请求级别** 的对象,框架在每次请求中首次访问 `ctx.service.xx` 时延迟实例化,所以 service 中可以通过 this.ctx 获取到当前请求的上下文。 +- 一个 Service 文件只能包含一个类, 这个类需要通过 `module.exports` 的方式返回。 +- Service 需要通过 Class 的方式定义,父类必须是 `app.Service`, 其中 `app.Service` 会在初始化 Service 的时候通过参数传递进来。 +- Service 不是单例,是 **请求级别** 的对象,框架在每次请求中首次访问 `ctx.service.xx` 时延迟实例化,所以 Service 中可以通过 this.ctx 获取到当前请求的上下文。 -### service ctx 详解 +### Service ctx 详解 -为了可以获取用户请求的链路,我们在 service 初始化中,注入了请求上下文, 用户在方法中可以直接通过 `this.ctx` 来获取上下文相关信息。关于上下文的具体详解可以参看 [Context](./extend.md#context), +为了可以获取用户请求的链路,我们在 Service 初始化中,注入了请求上下文, 用户在方法中可以直接通过 `this.ctx` 来获取上下文相关信息。关于上下文的具体详解可以参看 [Context](./extend.md#context), 有了 ctx 我们可以拿到框架给我们封装的各种便捷属性和方法。比如我们可以用: - `this.ctx.curl` 发起网络调用。 -- `this.ctx.service.otherService` 调用其他 service。 +- `this.ctx.service.otherService` 调用其他 Service。 - `this.ctx.db` 发起数据库调用等, db 可能是其他插件提前挂载到 app 上的模块。 -## 使用 service +## 使用 Service -下面就通过一个完整的例子,看看怎么使用 service。 +下面就通过一个完整的例子,看看怎么使用 Service。 ```js // app/router.js diff --git a/docs/source/zh-cn/basics/structure.md b/docs/source/zh-cn/basics/structure.md index cbfda5f223..c45cf42d2e 100644 --- a/docs/source/zh-cn/basics/structure.md +++ b/docs/source/zh-cn/basics/structure.md @@ -44,10 +44,11 @@ egg-project ``` 如上,由框架约定的目录: -- `app/router.js` 用于配置 URL 路由规则,具体参见[路由](./router.md)。 -- `app/controller/**` 用于解析用户的输入,处理后返回相应的结果,具体参见 [controller](./controller.md)。 -- `app/service/**` 用于编写业务逻辑层,可选,建议使用,具体参见 [service](./service.md)。 -- `app/middleware/**` 用于编写中间件,可选,具体参见 [middleware](./middleware.md)。 + +- `app/router.js` 用于配置 URL 路由规则,具体参见 [Router](./router.md)。 +- `app/controller/**` 用于解析用户的输入,处理后返回相应的结果,具体参见 [Controller](./controller.md)。 +- `app/service/**` 用于编写业务逻辑层,可选,建议使用,具体参见 [Service](./service.md)。 +- `app/middleware/**` 用于编写中间件,可选,具体参见 [Middleware](./middleware.md)。 - `app/public/**` 用于放置静态资源,可选,具体参见内置插件 [egg-static](https://github.com/eggjs/egg-static)。 - `app/extend/**` 用于框架的扩展,可选,具体参见[框架扩展](./extend.md)。 - `config/config.{env}.js` 用于编写配置文件,具体参见[配置](./config.md)。 @@ -56,9 +57,10 @@ egg-project - `app.js` 和 `agent.js` 用于自定义启动时的初始化工作,可选,具体参见[启动自动化](./app-start.md)。 由内置插件约定的目录: + - `app/public/**` 用于放置静态资源,可选,具体参见内置插件 [egg-static](https://github.com/eggjs/egg-static)。 - `app/schedule/**` 用于定时任务,可选,具体参见[定时任务](./schedule.md)。 -**若需自定义自己的目录规范,参见[Loader API](https://eggjs.org/zh-cn/advanced/loader.html)。** +**若需自定义自己的目录规范,参见 [Loader API](https://eggjs.org/zh-cn/advanced/loader.html)** - `app/view/**` 用于放置模板文件,可选,由模板插件约定,具体参见[模板渲染](../core/view.md)。 - `app/model/**` 用于放置领域模型,可选,由领域类相关插件约定,如 [egg-sequelize](https://github.com/eggjs/egg-sequelize)。 diff --git a/docs/source/zh-cn/core/cluster-and-ipc.md b/docs/source/zh-cn/core/cluster-and-ipc.md index 0522ad8d97..1d73fa3950 100644 --- a/docs/source/zh-cn/core/cluster-and-ipc.md +++ b/docs/source/zh-cn/core/cluster-and-ipc.md @@ -1,23 +1,23 @@ title: 多进程模型和进程间通讯 --- -我们知道 JavaScript 代码是运行在单线程上的,换句话说一个 node 进程只能运行在一个 CPU 上。那么如果用 node 来做 web server,就无法享受到多核运算的好处。作为企业级的解决方案,我们要解决的一个问题就是: +我们知道 JavaScript 代码是运行在单线程上的,换句话说一个 Node.js 进程只能运行在一个 CPU 上。那么如果用 Node.js 来做 Web Server,就无法享受到多核运算的好处。作为企业级的解决方案,我们要解决的一个问题就是: > 如何榨干服务器资源,利用上多核 CPU 的并发优势? -而 node 官方提供的解决方案是 [cluster 模块](https://nodejs.org/api/cluster.html) +而 Node.js 官方提供的解决方案是 [Cluster 模块](https://nodejs.org/api/cluster.html) > A single instance of Node.js runs in a single thread. To take advantage of multi-core systems the user will sometimes want to launch a cluster of Node.js processes to handle the load. > The cluster module allows you to easily create child processes that all share server ports. -## cluster 是什么呢? +## Cluster 是什么呢? 简单的说, - 在服务器上同时启动多个进程。 - 每个进程里都跑的是同一份源代码(好比把以前一个进程的工作分给多个进程去做)。 -- 更神奇的是,这些进程可以同时监听一个端口(具体原理推荐阅读 @DavidCai1993 这篇 [cluster 实现原理](https://cnodejs.org/topic/56e84480833b7c8a0492e20c))。 +- 更神奇的是,这些进程可以同时监听一个端口(具体原理推荐阅读 @DavidCai1993 这篇 [Cluster 实现原理](https://cnodejs.org/topic/56e84480833b7c8a0492e20c))。 其中: @@ -62,11 +62,11 @@ if (cluster.isMaster) { 健壮性(又叫鲁棒性)是企业级应用必须考虑的问题,除了程序本身代码质量要保证,框架层面也需要提供相应的『兜底』机制保证极端情况下应用的可用性。 -一般来说,node 进程退出可以分为两类: +一般来说,Node.js 进程退出可以分为两类: #### 未捕获异常 -当代码抛出了异常没有被捕获到时,进程将会退出,此时 node 提供了 `process.on('uncaughtException', handler)` 接口来捕获它,但是当一个 Worker 进程遇到[未捕获的异常](https://nodejs.org/dist/latest-v6.x/docs/api/process.html#process_event_uncaughtexception)时,它已经处于一个不确定状态,此时我们应该让这个进程优雅退出: +当代码抛出了异常没有被捕获到时,进程将会退出,此时 Node.js 提供了 `process.on('uncaughtException', handler)` 接口来捕获它,但是当一个 Worker 进程遇到 [未捕获的异常](https://nodejs.org/dist/latest-v6.x/docs/api/process.html#process_event_uncaughtexception) 时,它已经处于一个不确定状态,此时我们应该让这个进程优雅退出: 1. 关闭异常 Worker 进程所有的 TCP Server(将已有的连接快速断开,且不再接收新的连接),断开和 Master 的 IPC 通道,不再接受新的用户请求。 2. Master 立刻 fork 一个新的 Worker 进程,保证在线的『工人』总数不变。 @@ -100,7 +100,7 @@ if (cluster.isMaster) { ### Agent 机制 -说到这里,node 多进程方案貌似已经成型,这也是我们早期线上使用的方案。但后来我们发现有些工作其实不需要每个 Worker 都去做,如果都做,一来是浪费资源,更重要的是可能会导致多进程间资源访问冲突。举个例子:生产环境的日志文件我们一般会按照日期进行归档,在单进程模型下这再简单不过了: +说到这里,Node.js 多进程方案貌似已经成型,这也是我们早期线上使用的方案。但后来我们发现有些工作其实不需要每个 Worker 都去做,如果都做,一来是浪费资源,更重要的是可能会导致多进程间资源访问冲突。举个例子:生产环境的日志文件我们一般会按照日期进行归档,在单进程模型下这再简单不过了: > 1. 每天凌晨 0 点,将当前日志文件按照日期进行重命名 > 2. 销毁以前的文件句柄,并创建新的日志文件继续写入 @@ -135,9 +135,9 @@ if (cluster.isMaster) { +----------------------------------------->| | worker ready | | |<-----------------------------------------+ - | egg ready | | + | Egg ready | | +-------------------->| | - | egg ready | | + | Egg ready | | +----------------------------------------->| ``` @@ -156,7 +156,7 @@ if (cluster.isMaster) { ### Agent 的用法 -你可以在应用或插件根目录下的 `agent.js` 中实现你自己的逻辑(和 [启动自定义](../basics/app-start.md)用法类似,只是入口参数是 agent 对象) +你可以在应用或插件根目录下的 `agent.js` 中实现你自己的逻辑(和[启动自定义](../basics/app-start.md) 用法类似,只是入口参数是 agent 对象) ```js // agent.js @@ -223,13 +223,13 @@ $ EGG_SERVER_ENV=prod nohup node dispatch.js& #### Worker -Worker 进程负责处理真正的用户请求和[定时任务](../basics/schedule.md)的处理。而 egg 的定时任务也提供了只让一个 Worker 进程运行的能力,**所以能够通过定时任务解决的问题就不要放到 Agent 上执行**。 +Worker 进程负责处理真正的用户请求和[定时任务](../basics/schedule.md)的处理。而 Egg 的定时任务也提供了只让一个 Worker 进程运行的能力,**所以能够通过定时任务解决的问题就不要放到 Agent 上执行**。 Worker 运行的是业务代码,相对会比 Agent 和 Master 进程上运行的代码复杂度更高,稳定性也低一点,**当 Worker 进程异常退出时,Master 进程会重启一个 Worker 进程。** ## 进程间通讯(IPC) -虽然每个 Worker 进程是相对独立的,但是它们之间始终还是需要通讯的,叫进程间通讯(IPC)。下面是 node 官方提供的一段示例代码 +虽然每个 Worker 进程是相对独立的,但是它们之间始终还是需要通讯的,叫进程间通讯(IPC)。下面是 Node.js 官方提供的一段示例代码 ```js 'use strict'; @@ -290,7 +290,7 @@ if (cluster.isMaster) { - 在 app 调用该方法上会发送 agent 进程 - 在 agent 上调用该方法会发送给 agent 自己 - `agent.messenger.sendRandom(action, data)`: - - app 上没有该方法(现在 egg 的实现是等同于 sentToAgent) + - app 上没有该方法(现在 Egg 的实现是等同于 sentToAgent) - agent 上回随机发送消息给一个 app 进程(由 master 来控制发送给谁) - `app.messenger.sendTo(pid, action, data)`: 发送给指定进程 @@ -342,7 +342,7 @@ app.messenger.once(action, data => { ### 实现 -我们将所有的与远程数据源交互的逻辑封装在一个 service 中,并提供 `get` 方法给 Controller 调用。 +我们将所有的与远程数据源交互的逻辑封装在一个 Service 中,并提供 `get` 方法给 Controller 调用。 ```js // app/service/source.js diff --git a/docs/source/zh-cn/core/cookie-and-session.md b/docs/source/zh-cn/core/cookie-and-session.md index ea761f44a8..677a2989f8 100644 --- a/docs/source/zh-cn/core/cookie-and-session.md +++ b/docs/source/zh-cn/core/cookie-and-session.md @@ -1,11 +1,11 @@ -title: cookie 与 session +title: Cookie 与 Session --- -## cookie +## Cookie -HTTP 请求都是无状态的,但是我们的 web 应用通常都需要知道发起请求的人是谁。为了解决这个问题,HTTP 协议设计了一个特殊的请求头:[cookie](https://en.wikipedia.org/wiki/HTTP_cookie)。服务端可以通过响应头(set-cookie)将少量数据响应给客户端,浏览器会遵循协议将数据保存,并在下次请求同一个服务的时候带上(浏览器也会遵循协议,只在访问符合 cookie 指定规则的网站时带上对应的 cookie 来保证安全性)。 +HTTP 请求都是无状态的,但是我们的 Web 应用通常都需要知道发起请求的人是谁。为了解决这个问题,HTTP 协议设计了一个特殊的请求头:[Cookie](https://en.wikipedia.org/wiki/HTTP_cookie)。服务端可以通过响应头(set-cookie)将少量数据响应给客户端,浏览器会遵循协议将数据保存,并在下次请求同一个服务的时候带上(浏览器也会遵循协议,只在访问符合 Cookie 指定规则的网站时带上对应的 Cookie 来保证安全性)。 -通过 `context.cookies`,我们可以在 controller 中便捷、安全的设置和读取 cookie。 +通过 `context.cookies`,我们可以在 controller 中便捷、安全的设置和读取 Cookie。 ```js exports.add = function* (ctx) { @@ -23,11 +23,11 @@ exports.remove = function* (ctx) { #### `context.cookies.set(key, value, options)` -设置 cookie 其实是通过在 HTTP 响应中设置 set-cookie 头完成的,每一个 set-cookie 都会让浏览器在 cookie 中存一个键值对。在设置 cookie 值的同时,协议还支持许多参数来配置这个 cookie 的传输、存储和权限。 +设置 Cookie 其实是通过在 HTTP 响应中设置 set-cookie 头完成的,每一个 set-cookie 都会让浏览器在 Cookie 中存一个键值对。在设置 Cookie 值的同时,协议还支持许多参数来配置这个 Cookie 的传输、存储和权限。 - maxAge (Number): 设置这个键值对在浏览器的最长保存时间。是一个从服务器当前时刻开始的毫秒数。 -- expires (Date): 设置这个键值对的失效时间,如果设置了 maxAge,expires 将会被覆盖。如果 maxAge 和 expires 都没设置,cookie 将会在浏览器的会话失效(一般是关闭浏览器时)的时候失效。 -- path (String): 设置键值对生效的 URL 路径,默认设置在根路径上(`/`),也就是当前域名下的所有 URL 都可以访问这个 cookie。 +- expires (Date): 设置这个键值对的失效时间,如果设置了 maxAge,expires 将会被覆盖。如果 maxAge 和 expires 都没设置,Cookie 将会在浏览器的会话失效(一般是关闭浏览器时)的时候失效。 +- path (String): 设置键值对生效的 URL 路径,默认设置在根路径上(`/`),也就是当前域名下的所有 URL 都可以访问这个 Cookie。 - domain (String): 设置键值对生效的域名,默认没有配置,可以配置成只在指定域名才能访问。 - httpOnly (Boolean): 设置键值对是否可以被 js 访问,默认为 true,不允许被 js 访问。 - secure (Boolean): 设置键值对[只在 HTTPS 连接上传输](http://stackoverflow.com/questions/13729749/how-does-cookie-secure-flag-work),框架会帮我们判断当前是否在 HTTPS 连接上自动设置 secure 的值。 @@ -35,14 +35,14 @@ exports.remove = function* (ctx) { 除了这些属性之外,框架另外扩展了 3 个参数的支持: - overwrite(Boolean):设置 key 相同的键值对如何处理,如果设置为 true,则后设置的值会覆盖前面设置的,否则将会发送两个 set-cookie 响应头。 -- sign(Boolean):设置是否对 cookie 进行签名,如果设置为 true,则设置键值对的时候会同时对这个键值对的值进行签名,后面取的时候做校验,可以防止前端对这个值进行篡改。默认为 true。 -- encrypt(Boolean):设置是否对 cookie 进行加密,如果设置为 true,则在发送 cookie 前会对这个键值对的值进行加密,客户端无法读取到 cookie 的值。默认为 false。 +- sign(Boolean):设置是否对 Cookie 进行签名,如果设置为 true,则设置键值对的时候会同时对这个键值对的值进行签名,后面取的时候做校验,可以防止前端对这个值进行篡改。默认为 true。 +- encrypt(Boolean):设置是否对 Cookie 进行加密,如果设置为 true,则在发送 Cookie 前会对这个键值对的值进行加密,客户端无法读取到 Cookie 的值。默认为 false。 -在设置 cookie 时我们需要思考清楚这个 cookie 的作用,它需要被浏览器保存多久?是否可以被 js 获取到?是否可以被前端修改? +在设置 Cookie 时我们需要思考清楚这个 Cookie 的作用,它需要被浏览器保存多久?是否可以被 js 获取到?是否可以被前端修改? -**默认的配置下,cookie 是加签不加密的,浏览器可以看到明文,js 不能访问,不能被客户端(手工)篡改。** +**默认的配置下,Cookie 是加签不加密的,浏览器可以看到明文,js 不能访问,不能被客户端(手工)篡改。** -- 如果想要 cookie 在浏览器端可以被 js 访问并修改: +- 如果想要 Cookie 在浏览器端可以被 js 访问并修改: ```js ctx.cookies.set(key, value, { @@ -51,7 +51,7 @@ ctx.cookies.set(key, value, { }); ``` -- 如果想要 cookie 在浏览器端不能被修改,不能看到明文: +- 如果想要 Cookie 在浏览器端不能被修改,不能看到明文: ```js ctx.cookies.set(key, value, { @@ -62,19 +62,19 @@ ctx.cookies.set(key, value, { 注意: -1. 由于[浏览器和其他客户端实现的不确定性](http://stackoverflow.com/questions/7567154/can-i-use-unicode-characters-in-http-headers),为了保证 cookie 可以写入成功,建议 value 通过 base64 编码或者其他形式 encode 之后再写入。 -2. 由于[浏览器对 cookie 有长度限制限制](http://stackoverflow.com/questions/640938/what-is-the-maximum-size-of-a-web-browsers-cookies-key),所以尽量不要设置太长的 cookie。一般来说不要超过 4093 bytes。当设置的 cookie value 大于这个值时,框架会打印一条警告日志。 +1. 由于[浏览器和其他客户端实现的不确定性](http://stackoverflow.com/questions/7567154/can-i-use-unicode-characters-in-http-headers),为了保证 Cookie 可以写入成功,建议 value 通过 base64 编码或者其他形式 encode 之后再写入。 +2. 由于[浏览器对 Cookie 有长度限制限制](http://stackoverflow.com/questions/640938/what-is-the-maximum-size-of-a-web-browsers-cookies-key),所以尽量不要设置太长的 Cookie。一般来说不要超过 4093 bytes。当设置的 Cookie value 大于这个值时,框架会打印一条警告日志。 #### `context.cookies.get(key, options)` -由于 HTTP 请求中的 cookie 是在一个 header 中传输过来的,通过框架提供的这个方法可以快速的从整段 cookie 中获取对应的键值对的值。上面在设置 cookie 的时候,我们可以设置 `options.signed` 和 `options.encrypt` 来对 cookie 进行签名或加密,因此对应的在获取 cookie 的时候也要传相匹配的选项。 +由于 HTTP 请求中的 Cookie 是在一个 header 中传输过来的,通过框架提供的这个方法可以快速的从整段 Cookie 中获取对应的键值对的值。上面在设置 Cookie 的时候,我们可以设置 `options.signed` 和 `options.encrypt` 来对 Cookie 进行签名或加密,因此对应的在获取 Cookie 的时候也要传相匹配的选项。 - 如果设置的时候指定为 signed,获取时未指定,则不会在获取时对取到的值做验签,导致可能被客户端篡改。 - 如果设置的时候指定为 encrypt,获取时未指定,则无法获取到真实的值,而是加密过后的密文。 -### cookie 秘钥 +### Cookie 秘钥 -由于我们在 cookie 中需要用到加解密和验签,所以需要配置一个秘钥供加密使用。在 `config/config.default.js` 中 +由于我们在 Cookie 中需要用到加解密和验签,所以需要配置一个秘钥供加密使用。在 `config/config.default.js` 中 ```js module.exports = { @@ -82,25 +82,25 @@ module.exports = { }; ``` -keys 配置成一个字符串,可以按照逗号分隔配置多个 key。cookie 在使用这个配置进行加解密时: +keys 配置成一个字符串,可以按照逗号分隔配置多个 key。Cookie 在使用这个配置进行加解密时: - 加密和加签时只会使用第一个秘钥。 - 解密和验签时会遍历 keys 进行解密。 -如果我们想要更新 cookie 的秘钥,但是又不希望之前设置到用户浏览器上的 cookie 失效,可以将新的秘钥配置到 keys 最前面,等过一段时间之后再删去不需要的秘钥即可。 +如果我们想要更新 Cookie 的秘钥,但是又不希望之前设置到用户浏览器上的 Cookie 失效,可以将新的秘钥配置到 keys 最前面,等过一段时间之后再删去不需要的秘钥即可。 -## session +## Session -cookie 在 web 应用中经常承担标识请求方身份的功能,所以 web 应用在 cookie 的基础上封装了 session 的概念,专门用做用户身份识别。 +Cookie 在 Web 应用中经常承担标识请求方身份的功能,所以 Web 应用在 Cookie 的基础上封装了 Session 的概念,专门用做用户身份识别。 -框架内置了 [session](https://github.com/eggjs/egg-session) 插件,给我们提供了 `context.session` 来访问或者修改当前用户 session 。 +框架内置了 [Session](https://github.com/eggjs/egg-session) 插件,给我们提供了 `context.session` 来访问或者修改当前用户 Session 。 ```js exports.fetchPosts = function* (ctx) { - // 获取 session 上的内容 + // 获取 Session 上的内容 const userId = ctx.session.userId; const posts = yield ctx.service.post.fetch(userId); - // 修改 session 的值 + // 修改 Session 的值 ctx.session.visited = ctx.session.visited ? ctx.session.visited++ : 1; ctx.body = { success: true, @@ -109,7 +109,7 @@ exports.fetchPosts = function* (ctx) { }; ``` -session 的使用方法非常直观,直接读取它或者修改它就可以了,如果要删除它,直接将它赋值为 null: +Session 的使用方法非常直观,直接读取它或者修改它就可以了,如果要删除它,直接将它赋值为 null: ```js exports.deleteSession = function* (ctx) { @@ -117,7 +117,7 @@ exports.deleteSession = function* (ctx) { }; ``` -session 的实现是基于 cookie 的,默认配置下,用户 session 的内容加密后直接存储在 cookie 中的一个字段中,用户每次请求我们网站的时候都会带上这个 cookie,我们在服务端解密后使用。session 的默认配置如下: +Session 的实现是基于 Cookie 的,默认配置下,用户 Session 的内容加密后直接存储在 Cookie 中的一个字段中,用户每次请求我们网站的时候都会带上这个 Cookie,我们在服务端解密后使用。Session 的默认配置如下: ```js exports.session = { @@ -128,16 +128,16 @@ exports.session = { }; ``` -可以看到这些参数除了 `key` 都是 cookie 的参数,`key` 代表了存储 session 的 cookie 键值对的 key 是什么。在默认的配置下,存放 session 的 cookie 将会加密存储、不可被前端 js 访问,这样可以保证用户的 session 是安全的。 +可以看到这些参数除了 `key` 都是 Cookie 的参数,`key` 代表了存储 Session 的 Cookie 键值对的 key 是什么。在默认的配置下,存放 Session 的 Cookie 将会加密存储、不可被前端 js 访问,这样可以保证用户的 Session 是安全的。 ### 扩展存储 -session 默认存放在 cookie 中,但是如果我们的 session 对象过于庞大,就会带来一些额外的问题: +Session 默认存放在 Cookie 中,但是如果我们的 Session 对象过于庞大,就会带来一些额外的问题: -- 前面提到,浏览器通常都有限制最大的 cookie 长度,当设置的 session 过大时,浏览器可能拒绝保存。 -- cookie 在每次请求时都会带上,当 session 过大时,每次请求都要额外带上庞大的 cookie 信息。 +- 前面提到,浏览器通常都有限制最大的 Cookie 长度,当设置的 Session 过大时,浏览器可能拒绝保存。 +- Cookie 在每次请求时都会带上,当 Session 过大时,每次请求都要额外带上庞大的 Cookie 信息。 -框架提供了将 session 存储到除了 cookie 之外的其他存储的扩展方案,我们只需要设置 `app.sessionStore` 即可将 session 存储到指定的存储中。 +框架提供了将 Session 存储到除了 Cookie 之外的其他存储的扩展方案,我们只需要设置 `app.sessionStore` 即可将 Session 存储到指定的存储中。 ```js // app.js @@ -156,7 +156,7 @@ module.exports = app => { }; ``` -sessionStore 的实现我们也可以封装到插件中,例如 [egg-session-redis] 就提供了将 session 存储到 redis 中的能力,在应用层,我们只需要引入 [egg-redis] 和 [egg-session-redis] 插件即可。 +sessionStore 的实现我们也可以封装到插件中,例如 [egg-session-redis] 就提供了将 Session 存储到 redis 中的能力,在应用层,我们只需要引入 [egg-redis] 和 [egg-session-redis] 插件即可。 ```js // plugin.js @@ -170,13 +170,13 @@ exports.sessionRedis = { }; ``` -**注意:一旦选择了将 session 存入到外部存储中,就意味着系统将强依赖于这个外部存储,当它挂了的时候,我们就完全无法使用 session 相关的功能了。因此我们更推荐大家只将必要的信息存储在 session 中,保持 session 的精简并使用默认的 cookie 存储,用户级别的缓存不要存储在 session 中。** +**注意:一旦选择了将 Session 存入到外部存储中,就意味着系统将强依赖于这个外部存储,当它挂了的时候,我们就完全无法使用 Session 相关的功能了。因此我们更推荐大家只将必要的信息存储在 Session 中,保持 Session 的精简并使用默认的 Cookie 存储,用户级别的缓存不要存储在 Session 中。** -### session 实践 +### Session 实践 -#### 修改用户 session 失效时间 +#### 修改用户 Session 失效时间 -虽然在 session 的配置中有一项是 maxAge,但是它只能全局设置 session 的有效期,我们经常可以在一些网站的登陆页上看到有 **记住我** 的选项框,勾选之后可以让登陆用户的 session 有效期更长。这种针对特定用户的 session 有效时间设置我们可以通过 `context.session.maxAge=` 来实现。 +虽然在 Session 的配置中有一项是 maxAge,但是它只能全局设置 Session 的有效期,我们经常可以在一些网站的登陆页上看到有 **记住我** 的选项框,勾选之后可以让登陆用户的 Session 有效期更长。这种针对特定用户的 Session 有效时间设置我们可以通过 `context.session.maxAge=` 来实现。 ```js const ms = require('ms'); @@ -185,25 +185,25 @@ exports.login = function* (ctx) { const { username, password, rememberMe } = ctx.request.body; const user = yield ctx.loginAndGetUser(username, password); - // 设置 session + // 设置 Session this.session.user = user; // 如果用户勾选了 `记住我`,设置一个月的过期时间 if (rememberMe) this.session.maxAge = ms('1m'); }; ``` -#### 延长用户 session 有效期 +#### 延长用户 Session 有效期 -默认情况下,当用户请求没有导致 session 被修改时,框架都不会延长 session 的有效期,但是在有些场景下,我们希望用户每次访问都刷新 session 的有效时间,这样用户只有在长期未访问我们的网站的时候才会被登出。这个功能我们可以通过 `context.session.save()` 来实现。 +默认情况下,当用户请求没有导致 Session 被修改时,框架都不会延长 Session 的有效期,但是在有些场景下,我们希望用户每次访问都刷新 Session 的有效时间,这样用户只有在长期未访问我们的网站的时候才会被登出。这个功能我们可以通过 `context.session.save()` 来实现。 -例如,我们在项目中增加一个中间件,让它在 session 有值的时候强制保存一次,以达到延长 session 有效期的目的。 +例如,我们在项目中增加一个中间件,让它在 Session 有值的时候强制保存一次,以达到延长 Session 有效期的目的。 ```js // app/middleware/save_session.js module.exports = () => { return function* (next) { yield next; - // 如果 session 是空的,则不保存 + // 如果 Session 是空的,则不保存 if (!this.session.populated) return; this.session.save(); }; diff --git a/docs/source/zh-cn/core/deployment.md b/docs/source/zh-cn/core/deployment.md index 55566df102..ab1a4a26af 100644 --- a/docs/source/zh-cn/core/deployment.md +++ b/docs/source/zh-cn/core/deployment.md @@ -9,7 +9,7 @@ title: 应用部署 Javascript 语言本身不需要编译的,构建过程主要是下载依赖。但如果使用 Typscript 或者 Babel 支持 ES6 以上的特性,那就必须要这一步了。 -一般安装依赖会指定 `NODE_ENV=production` 或 `npm install --production` 只安装 dependencies 的依赖。因为 devDependencies 中的模块过大而且在生产环境不会使用,安装后也可能遇到未知问题。 +一般安装依赖会指定 `NODE_ENV=production` 或 `npm install --production` 只安装 dependencies 的依赖。因为 devDependencies 中的模块过大而且在生产环境不会使用,安装后也可能遇到未知问题。 ```bash $ cd baseDir @@ -55,7 +55,7 @@ EGG_SERVER_ENV=prod nohup node dispatch.js > stdout.log 2> stderr.log & 注意: -- **生产环境使用的 EGG_SERVER_ENV 必须为 prod**,可查看[运行环境](./basics/env.md)获取更多内容。 +- **生产环境使用的 `EGG_SERVER_ENV` 必须为 `prod`**,可查看[运行环境](./basics/env.md)获取更多内容。 - 如果使用 Docker,可直接前台运行。 - 默认情况框架会创建和 CPU 核数相当的 app worker 数,可以充分的利用 CPU 资源。 diff --git a/docs/source/zh-cn/core/development.md b/docs/source/zh-cn/core/development.md index 9bf9f43137..2831eda635 100644 --- a/docs/source/zh-cn/core/development.md +++ b/docs/source/zh-cn/core/development.md @@ -47,7 +47,7 @@ $ npm i egg-bin --save-dev ## 单元测试 -这里主要讲解工具部分的使用,更多关于单元测试的内容请参考 [这里](./unittest.md)。 +这里主要讲解工具部分的使用,更多关于单元测试的内容请参考[这里](./unittest.md)。 ### 添加命令 @@ -82,7 +82,7 @@ TESTS=test/x.test.js npm test ### 指定 reporter -mocha 支持多种形式的 reporter,默认使用 `spec` reporter。 +Mocha 支持多种形式的 reporter,默认使用 `spec` reporter。 可以手动设置 `TEST_REPORTER` 环境变量来指定 reporter,例如使用 `dot`: @@ -204,7 +204,7 @@ app.logger.debug('app init'); ### 使用 debug 模块调试 -[debug](https://www.npmjs.com/package/debug) 模块是 node 社区广泛使用的 debug 工具,很多模块都使用它模块打印调试信息,egg 社区也广泛采用这一机制打印 debug 信息,**推荐在框架和插件开发中使用它。** +[debug](https://www.npmjs.com/package/debug) 模块是 Node.js 社区广泛使用的 debug 工具,很多模块都使用它模块打印调试信息,Egg 社区也广泛采用这一机制打印 debug 信息,**推荐在框架和插件开发中使用它。** 我们可以通过 `DEBUG` 环境变量选择开启指定的调试代码,方便观测执行过程。 @@ -229,4 +229,4 @@ DEBUG=egg* npm run dev 如果想了解更多本地开发相关的内容,例如为你的团队定制一个本地开发工具,请参考 [egg-bin]。 [glob]: https://www.npmjs.com/package/glob -[egg-bin]: https://github.com/eggjs/egg-bin \ No newline at end of file +[egg-bin]: https://github.com/eggjs/egg-bin diff --git a/docs/source/zh-cn/core/error-handling.md b/docs/source/zh-cn/core/error-handling.md index 0e096456b7..1c067ab27b 100644 --- a/docs/source/zh-cn/core/error-handling.md +++ b/docs/source/zh-cn/core/error-handling.md @@ -48,7 +48,7 @@ this.runInBackground(function* () { ## 框架层统一异常处理 -框架通过 [onerror](https://github.com/eggjs/egg-onerror) 插件提供了统一的错误处理机制。对一个请求的所有处理方法(middleware、controller、service)中抛出的任何异常都会被它捕获,并自动根据请求想要获取的类型返回不同类型的错误(基于 [Content Negotiation](https://tools.ietf.org/html/rfc7231#section-5.3.2))。 +框架通过 [onerror](https://github.com/eggjs/egg-onerror) 插件提供了统一的错误处理机制。对一个请求的所有处理方法(Middleware、Controller、Service)中抛出的任何异常都会被它捕获,并自动根据请求想要获取的类型返回不同类型的错误(基于 [Content Negotiation](https://tools.ietf.org/html/rfc7231#section-5.3.2))。 | 请求需求的格式 | 环境 | errorPageUrl 是否配置 | 返回内容 | |-------------|------|----------------------|--------| diff --git a/docs/source/zh-cn/core/httpclient.md b/docs/source/zh-cn/core/httpclient.md index b758710d9c..9299d7d7ac 100644 --- a/docs/source/zh-cn/core/httpclient.md +++ b/docs/source/zh-cn/core/httpclient.md @@ -1,13 +1,13 @@ title: HttpClient --- -互联网时代,无数服务是基于 HTTP 协议进行通信的,web 应用调用后端 HTTP 服务是一种非常常见的应用场景。 +互联网时代,无数服务是基于 HTTP 协议进行通信的,Web 应用调用后端 HTTP 服务是一种非常常见的应用场景。 -为此框架基于 [urllib] 内置实现了一个 [httpclient],应用可以非常便捷地完成任何 HTTP 请求。 +为此框架基于 [urllib] 内置实现了一个 [HttpClient],应用可以非常便捷地完成任何 HTTP 请求。 ## 通过 app 使用 HttpClient -框架在应用初始化的时候,会自动将 [httpclient] 初始化到 `app.httpclient`。 +框架在应用初始化的时候,会自动将 [HttpClient] 初始化到 `app.httpclient`。 同时增加了一个 `app.curl(url, options)` 方法,它等价于 `app.httpclient.request(url, options)`。 这样就可以非常方便地使用 `app.curl` 方法完成一次 HTTP 请求。 @@ -20,15 +20,15 @@ module.exports = app => { const result = yield app.curl('https://registry.npm.taobao.org/egg/latest', { dataType: 'json', }); - app.logger.info('egg latest version: %s', result.data.version); + app.logger.info('Egg latest version: %s', result.data.version); }); }; ``` -## 通过 context 使用 HttpClient +## 通过 Context 使用 HttpClient -框架在 context 中同样提供了 `ctx.curl(url, options)` 和 `ctx.httpclient`,保持跟 app 下的使用体验一致。 -这样就可以在有 context 的地方(如在 controller 中)非常方便地使用 `ctx.curl()` 方法完成一次 HTTP 请求。 +框架在 Context 中同样提供了 `ctx.curl(url, options)` 和 `ctx.httpclient`,保持跟 app 下的使用体验一致。 +这样就可以在有 Context 的地方(如在 controller 中)非常方便地使用 `ctx.curl()` 方法完成一次 HTTP 请求。 ```js // app/controller/home.js @@ -70,12 +70,12 @@ module.exports = function* get() { }; ``` -- GET 请求可以不用设置 `options.method` 参数,httpclient 的默认 method 会设置为 `GET`。 +- GET 请求可以不用设置 `options.method` 参数,HttpClient 的默认 method 会设置为 `GET`。 - 返回值 `result` 会包含 3 个属性:`status`, `headers` 和 `data` - `status`: 响应状态码,如 `200`, `302`, `404`, `500` 等等 - `headers`: 响应头,类似 `{ 'content-type': 'text/html', ... }` - - `data`: 响应 body,默认 httpclient 不会做任何处理,会直接返回 Buffer 类型数据。 - 一旦设置了 `options.dataType`,httpclient 将会根据此参数对 `data` 进行相应的处理。 + - `data`: 响应 body,默认 HttpClient 不会做任何处理,会直接返回 Buffer 类型数据。 + 一旦设置了 `options.dataType`,HttpClient 将会根据此参数对 `data` 进行相应的处理。 完整的请求参数 `options` 和返回值 `result` 的说明请看下文的 [options 参数详解](#options 参数详解) 章节。 @@ -91,13 +91,13 @@ module.exports = function* post() { const result = yield this.curl('https://httpbin.org/post', { // 必须指定 method method: 'POST', - // 通过 contentType 告诉 httpclient 以 JSON 格式发送 + // 通过 contentType 告诉 HttpClient 以 JSON 格式发送 contentType: 'json', data: { hello: 'world', now: Date.now(), }, - // 明确告诉 httpclient 以 JSON 格式处理返回的响应 body + // 明确告诉 HttpClient 以 JSON 格式处理返回的响应 body dataType: 'json', }); this.body = result.data; @@ -117,12 +117,12 @@ module.exports = function* put() { const result = yield this.curl('https://httpbin.org/put', { // 必须指定 method method: 'PUT', - // 通过 contentType 告诉 httpclient 以 JSON 格式发送 + // 通过 contentType 告诉 HttpClient 以 JSON 格式发送 contentType: 'json', data: { update: 'foo bar', }, - // 明确告诉 httpclient 以 JSON 格式处理响应 body + // 明确告诉 HttpClient 以 JSON 格式处理响应 body dataType: 'json', }); this.body = result.data; @@ -131,7 +131,7 @@ module.exports = function* put() { ### DELETE -删除数据会选择 DELETE 请求,它通常可以不需要加请求 body,但是 httpclient 不会限制。 +删除数据会选择 DELETE 请求,它通常可以不需要加请求 body,但是 HttpClient 不会限制。 ```js // app/controller/delete.js @@ -139,7 +139,7 @@ module.exports = function* del() { const result = yield this.curl('https://httpbin.org/delete', { // 必须指定 method method: 'DELETE', - // 明确告诉 httpclient 以 JSON 格式处理响应 body + // 明确告诉 HttpClient 以 JSON 格式处理响应 body dataType: 'json', }); this.body = result.data; @@ -161,12 +161,12 @@ module.exports = function* form() { const result = yield this.curl('https://httpbin.org/post', { // 必须指定 method,支持 POST,PUT 和 DELETE method: 'POST', - // 不需要设置 contentType,httpclient 会默认以 application/x-www-form-urlencoded 格式发送请求 + // 不需要设置 contentType,HttpClient 会默认以 application/x-www-form-urlencoded 格式发送请求 data: { now: Date.now(), foo: 'bar', }, - // 明确告诉 httpclient 以 JSON 格式处理响应 body + // 明确告诉 HttpClient 以 JSON 格式处理响应 body dataType: 'json', }); this.body = result.data.form; @@ -178,11 +178,11 @@ module.exports = function* form() { }; ``` -### 以 multipart 方式上传文件 +### 以 Multipart 方式上传文件 当一个 Form 表单提交包含文件的时候,请求数据格式就必须以 [multipart/form-data](http://tools.ietf.org/html/rfc2388) 进行提交了。 -这个时候需要引入 [formstream] 这个第三方模块来帮助我们生成可以被 httpclient 消费的 `form` 对象。 +这个时候需要引入 [formstream] 这个第三方模块来帮助我们生成可以被 HttpClient 消费的 `form` 对象。 ```js // app/controller/multipart.js @@ -201,7 +201,7 @@ module.exports = function* multipart() { headers: form.headers(), // 以 stream 模式提交 stream: form, - // 明确告诉 httpclient 以 JSON 格式处理响应 body + // 明确告诉 HttpClient 以 JSON 格式处理响应 body dataType: 'json', }); this.body = result.data.files; @@ -219,11 +219,11 @@ form.file('file1', file1); form.file('file2', file2); ``` -### 以 stream 方式上传文件 +### 以 Stream 方式上传文件 -其实,在 node 的世界里面,stream 才是主流。 -如果服务端支持流式上传,最友好的方式还是直接发送 stream。 -stream 实际会以 `Transfer-Encoding: chunked` 传输编码格式发送,这个转换是 [http] 模块自动实现的。 +其实,在 Node.js 的世界里面,Stream 才是主流。 +如果服务端支持流式上传,最友好的方式还是直接发送 Stream。 +Stream 实际会以 `Transfer-Encoding: chunked` 传输编码格式发送,这个转换是 [HTTP] 模块自动实现的。 ```js // app/controller/stream.js @@ -252,7 +252,7 @@ module.exports = function* stream() { 由于 HTTP 请求的复杂性,导致 `httpclient.request(url, options)` 的 options 参数会非常多。 接下来将会以参数说明和代码配合一起讲解每个可选参数的实际用途。 -### httpclient 默认全局配置 +### HttpClient 默认全局配置 ```js // config/config.default.js @@ -307,9 +307,9 @@ this.curl(url, { ### `dataAsQueryString: Boolean` 如果设置了 `dataAsQueryString=true`,那么即使在 POST 情况下, -也会强制将 `options.data` 以 `querystring.stringify` 处理之后拼接到 url 的 query 参数上。 +也会强制将 `options.data` 以 `querystring.stringify` 处理之后拼接到 `url` 的 query 参数上。 -可以很好地解决以 `stream` 发送数据,且额外的请求参数以 url query 形式传递的应用场景: +可以很好地解决以 `stream` 发送数据,且额外的请求参数以 `url` query 形式传递的应用场景: ```js this.curl(url, { @@ -330,7 +330,7 @@ this.curl(url, { ```js this.curl(url, { method: 'POST', - // 直接发送原始 xml 数据,不需要 httpclient 做特殊处理 + // 直接发送原始 xml 数据,不需要 HttpClient 做特殊处理 content: 'world', headers: { 'content-type': 'text/html', @@ -341,7 +341,7 @@ this.curl(url, { ### `stream: ReadStream` 设置发送请求正文的可读数据流,默认是 `null`。 -一旦设置了此参数,httpclient 将会忽略 `data` 和 `content`。 +一旦设置了此参数,HttpClient 将会忽略 `data` 和 `content`。 ```js this.curl(url, { @@ -375,7 +375,7 @@ this.curl(url, { ### `contentType: String` -设置请求数据格式,默认是 `undefined`,httpclient 会自动根据 `data` 和 `content` 参数自动设置。 +设置请求数据格式,默认是 `undefined`,HttpClient 会自动根据 `data` 和 `content` 参数自动设置。 `data` 是 object 的时候默认设置的是 `form`。支持 `json` 格式。 如需要以 JSON 格式发送 `data`: @@ -452,7 +452,7 @@ this.curl(url, { ### `agent: HttpAgent` -允许通过此参数覆盖默认的 http agent,如果你不想开启 KeepAlive,可以设置此参数为 `false`。 +允许通过此参数覆盖默认的 HttpAgent,如果你不想开启 KeepAlive,可以设置此参数为 `false`。 ```js this.curl(url, { @@ -462,7 +462,7 @@ this.curl(url, { ### `httpsAgent: HttpsAgent` -允许通过此参数覆盖默认的 https agent,如果你不想开启 KeepAlive,可以设置此参数为 `false`。 +允许通过此参数覆盖默认的 HttpsAgent,如果你不想开启 KeepAlive,可以设置此参数为 `false`。 ```js this.curl(url, { @@ -534,7 +534,7 @@ this.curl(url, { ### `beforeRequest: Function(options)` -httpclient 在请求正在发送之前,会尝试调用 `beforeRequest` 钩子,允许我们在这里对请求参数做最后一次修改。 +HttpClient 在请求正在发送之前,会尝试调用 `beforeRequest` 钩子,允许我们在这里对请求参数做最后一次修改。 ```js this.curl(url, { @@ -548,7 +548,7 @@ this.curl(url, { ### `streaming: Boolean` 是否直接返回响应流,默认为 `false`。 -开启 streaming 之后,httpclient 会在拿到响应对象 res 之后马上返回, +开启 streaming 之后,HttpClient 会在拿到响应对象 res 之后马上返回, 此时 `result.headers` 和 `result.status` 已经可以读取到,只是没有读取 data 数据而已。 ```js @@ -566,7 +566,7 @@ this.body = result.res; ### `gzip: Boolean` 是否支持 gzip 响应格式,默认为 `false`。 -开启 gzip 之后,httpclient 将自动设置 `Accept-Encoding: gzip` 请求头, +开启 gzip 之后,HttpClient 将自动设置 `Accept-Encoding: gzip` 请求头, 并且会自动解压带 `Content-Encoding: gzip` 响应头的数据。 ```js @@ -606,7 +606,7 @@ console.log(result.res.timing); ### `ca,rejectUnauthorized,pfx,key,cert,passphrase,ciphers,secureProtocol` -这几个都是透传给 [https] 模块的参数,具体请查看 [`https.request(options, callback)`](https://nodejs.org/api/https.html#https_https_request_options_callback)。 +这几个都是透传给 [HTTPS] 模块的参数,具体请查看 [`https.request(options, callback)`](https://nodejs.org/api/https.html#https_https_request_options_callback)。 ## 调试辅助 @@ -639,7 +639,7 @@ $ anyproxy --port 8888 $ http_proxy=http://127.0.0.1:8888 npm run dev ``` -然后就可以正常操作了,所有经过 httpclient 的请求,都可以在 http://localhost:8002 这个控制台中查看到。 +然后就可以正常操作了,所有经过 HttpClient 的请求,都可以在 http://localhost:8002 这个控制台中查看到。 ![anyproxy](https://cloud.githubusercontent.com/assets/227713/21976937/06a63694-dc0f-11e6-98b5-e9e279c4867c.png) @@ -686,7 +686,7 @@ $ http_proxy=http://127.0.0.1:8888 npm run dev ## 全局 `request` 和 `response` 事件 在企业应用场景,常常会有统一 tracer 日志的需求。 -为了方便在 app 层面统一监听 httpclient 的请求和响应,我们约定了全局 `request` 和 `response` 事件来暴露这两个时机。 +为了方便在 app 层面统一监听 HttpClient 的请求和响应,我们约定了全局 `request` 和 `response` 事件来暴露这两个时机。 ```bash init options @@ -734,10 +734,10 @@ app.httpclient.on('response', result => { 完整示例代码可以在 [eggjs/exmaples/httpclient](https://github.com/eggjs/examples/blob/master/httpclient) 找到。 [urllib]: https://github.com/node-modules/urllib -[httpclient]: https://github.com/eggjs/egg/blob/master/lib/core/urllib.js +[HttpClient]: https://github.com/eggjs/egg/blob/master/lib/core/urllib.js [formstream]: https://github.com/node-modules/formstream -[http]: https://nodejs.org/api/http.html -[https]: https://nodejs.org/api/https.html +[HTTP]: https://nodejs.org/api/http.html +[HTTPS]: https://nodejs.org/api/https.html [egg-development-proxyagent]: https://github.com/eggjs/egg-development-proxyagent [charles]: https://www.charlesproxy.com/ [fiddler]: http://www.telerik.com/fiddler diff --git a/docs/source/zh-cn/core/i18n.md b/docs/source/zh-cn/core/i18n.md index 8e7d5cfc49..f94a64cc66 100644 --- a/docs/source/zh-cn/core/i18n.md +++ b/docs/source/zh-cn/core/i18n.md @@ -1,7 +1,7 @@ -title: 国际化 +title: I18n 国际化 --- -为了方便开发多语言应用,框架内置了国际化(i18n)支持,由 [egg-i18n](https://github.com/eggjs/egg-i18n) 插件提供。 +为了方便开发多语言应用,框架内置了国际化(I18n)支持,由 [egg-i18n](https://github.com/eggjs/egg-i18n) 插件提供。 ## 默认语言 @@ -16,7 +16,7 @@ exports.i18n = { ## 切换语言 -我们可以通过下面几种方式修改应用的当前语言(修改后会记录到 `locale` 这个 cookie),下次请求直接用设定好的语言。 +我们可以通过下面几种方式修改应用的当前语言(修改后会记录到 `locale` 这个 Cookie),下次请求直接用设定好的语言。 优先级从高到低: @@ -24,7 +24,7 @@ exports.i18n = { 2. cookie: `locale=zh-TW` 3. header: `Accept-Language: zh-CN,zh;q=0.5` -如果想修改 query 或者 cookie 参数名称: +如果想修改 query 或者 Cookie 参数名称: ```js // config/config.default.js @@ -36,7 +36,7 @@ exports.i18n = { }; ``` -## 编写 i18n 多语言文件 +## 编写 I18n 多语言文件 多种语言的配置是独立的,统一存放在 `config/locales/*.js` 下。 @@ -124,7 +124,7 @@ module.exports = function* () { ### View 中使用 -假设我们使用的模板引擎是 [nunjucks](https://github.com/eggjs/egg-view-nunjucks) +假设我们使用的模板引擎是 [Nunjucks](https://github.com/eggjs/egg-view-nunjucks) ```html
  • {{ __('Email') }}: {{ user.email }}
  • diff --git a/docs/source/zh-cn/core/logger.md b/docs/source/zh-cn/core/logger.md index 0a55c2ba49..1f3524b4f8 100644 --- a/docs/source/zh-cn/core/logger.md +++ b/docs/source/zh-cn/core/logger.md @@ -1,7 +1,7 @@ title: 日志 --- -日志对于 web 开发的重要性毋庸置疑,它对于监控应用的运行状态、问题排查等都有非常重要的意义。 +日志对于 Web 开发的重要性毋庸置疑,它对于监控应用的运行状态、问题排查等都有非常重要的意义。 框架内置了强大的企业级日志支持,由 [egg-logger](https://github.com/eggjs/egg-logger) 模块提供。 @@ -18,7 +18,7 @@ title: 日志 ## 日志路径 - 所有日志文件默认都放在 `${appInfo.root}/logs/${appInfo.name}` 路径下,例如 `/home/admin/logs/example-app`。 -- 在本地开发环境(env: local) 和单元测试环境(env: unittest),为了避免冲突以及集中管理,日志会打印在项目目录下的 logs 目录,例如 `/path/to/example-app/logs/example-app`。 +- 在本地开发环境 (env: local) 和单元测试环境 (env: unittest),为了避免冲突以及集中管理,日志会打印在项目目录下的 logs 目录,例如 `/path/to/example-app/logs/example-app`。 如果想自定义日志路径: @@ -56,9 +56,9 @@ module.exports = appInfo => { ## 如何打印日志 -### context logger +### Context Logger -如果我们在处理请求时需要打印日志,这时候使用 context logger,用于记录 web 行为相关的日志。 +如果我们在处理请求时需要打印日志,这时候使用 Context Logger,用于记录 Web 行为相关的日志。 每行日志会自动记录上当前请求的一些基本信息, 如 `[$userId/$ip/$traceId/${cost}ms $method $url]`。 @@ -73,7 +73,7 @@ ctx.logger.warn('WARNNING!!!!'); ctx.logger.error(new Error('whoops')); ``` -对于框架开发者和插件开发者会使用到的 context logger 还有 `ctx.coreLogger`。 +对于框架开发者和插件开发者会使用到的 Context Logger 还有 `ctx.coreLogger`。 例如 @@ -81,9 +81,9 @@ ctx.logger.error(new Error('whoops')); ctx.coreLogger.info('info'); ``` -### app logger +### App Logger -如果我们想做一些应用级别的日志记录,如记录启动阶段的一些数据信息,可以通过 app logger 来完成。 +如果我们想做一些应用级别的日志记录,如记录启动阶段的一些数据信息,可以通过 App Logger 来完成。 ```js // app.js @@ -96,7 +96,7 @@ module.exports = app => { }; ``` -对于框架和插件开发者会使用到的 app logger 还有 `app.coreLogger`。 +对于框架和插件开发者会使用到的 App Logger 还有 `app.coreLogger`。 ```js // app.js @@ -105,9 +105,9 @@ module.exports = app => { }; ``` -### agent logger +### Agent Logger -在开发框架和插件时有时会需要在 agent 进程运行代码,这时使用 `agent.coreLogger`。 +在开发框架和插件时有时会需要在 Agent 进程运行代码,这时使用 `agent.coreLogger`。 ```js // agent.js @@ -120,7 +120,7 @@ module.exports = agent => { }; ``` -如需详细了解 agent 进程,请参考 [多进程模型](../advanced/cluster.md)。 +如需详细了解 Agent 进程,请参考[多进程模型](../advanced/cluster.md)。 ## 日志文件编码 @@ -317,7 +317,7 @@ module.exports = appInfo => { ## 性能 -通常 web 访问是高频访问,每次打印日志都写磁盘会造成频繁磁盘 io,为了提高性能,我们采用的文件日志写入策略是: +通常 Web 访问是高频访问,每次打印日志都写磁盘会造成频繁磁盘 IO,为了提高性能,我们采用的文件日志写入策略是: > 日志同步写入内存,异步每隔一段时间(默认 1 秒)刷盘 diff --git a/docs/source/zh-cn/core/security.md b/docs/source/zh-cn/core/security.md index c6b9d4ba23..0c2537f686 100644 --- a/docs/source/zh-cn/core/security.md +++ b/docs/source/zh-cn/core/security.md @@ -1,21 +1,21 @@ title: 安全 --- -## web 安全概念 +## Web 安全概念 -web 应用中存在很多安全风险,这些风险会被黑客利用,轻则篡改网页内容,重则窃取网站内部数据,更为严重的则是在网页中植入恶意代码,使得用户受到侵害。常见的安全漏洞如下: +Web 应用中存在很多安全风险,这些风险会被黑客利用,轻则篡改网页内容,重则窃取网站内部数据,更为严重的则是在网页中植入恶意代码,使得用户受到侵害。常见的安全漏洞如下: -- XSS 攻击:对 web 页面注入脚本,使用 JavaScript 窃取用户信息,诱导用户操作。 +- XSS 攻击:对 Web 页面注入脚本,使用 JavaScript 窃取用户信息,诱导用户操作。 - CSRF 攻击:伪造用户请求向网站发起恶意请求。 - 钓鱼攻击:利用网站的跳转链接或者图片制造钓鱼陷阱。 - HTTP参数污染:利用对参数格式验证的不完善,对服务器进行参数注入攻击。 - 远程代码执行:用户通过浏览器提交执行命令,由于服务器端没有针对执行函数做过滤,导致在没有指定绝对路径的情况下就执行命令。 -而框架本身针对 web 端常见的安全风险内置了丰富的解决方案: +而框架本身针对 Web 端常见的安全风险内置了丰富的解决方案: -- 利用 [extend](https://github.com/eggjs/egg/blob/master/docs/source/zh-cn/basics/extend.md) 机制扩展了 helper API, 提供了各种模板过滤函数,防止钓鱼或 XSS 攻击。 -- 常见 web 安全头的支持。 +- 利用 [extend](https://github.com/eggjs/egg/blob/master/docs/source/zh-cn/basics/extend.md) 机制扩展了 Helper API, 提供了各种模板过滤函数,防止钓鱼或 XSS 攻击。 +- 常见 Web 安全头的支持。 - CSRF 的防御方案。 - 灵活的安全配置,可以匹配不同的请求 url 。 - 可定制的白名单,用于安全跳转和 url 过滤。 @@ -69,7 +69,7 @@ exports.security = { **注意:不能同时存在 match 和 ignore ,否则会报错。** -下面我们会针对具体的场景,来讲解如何使用框架提供的安全方案进行 web 安全防范。 +下面我们会针对具体的场景,来讲解如何使用框架提供的安全方案进行 Web 安全防范。 ## 安全威胁` XSS `的防范 @@ -88,7 +88,7 @@ XSS 攻击一般分为两类: #### 防范方式 -框架提供了`helper.escape()`方法对字符串进行 XSS 过滤。 +框架提供了 `helper.escape()` 方法对字符串进行 XSS 过滤。 ```js const str = '><'; @@ -98,9 +98,9 @@ console.log(ctx.helper.escape(str)); 当网站需要直接输出用户输入的结果时,请务必使用 `helper.escape()` 包裹起来,如在 [egg-view-nunjucks] 里面就覆盖掉了内置的 `escape`。 -另外一种情况,网站输出的内容会提供给 JavaScript 来使用。这个时候需要使用`helper.sjs()`来进行过滤。 +另外一种情况,网站输出的内容会提供给 JavaScript 来使用。这个时候需要使用 `helper.sjs()` 来进行过滤。 -`helper.sjs()`用于在 JavaScript(包括 onload 等 event)中输出变量,会对变量中字符进行 JavaScript ENCODE, +`helper.sjs()` 用于在 JavaScript(包括 onload 等 event)中输出变量,会对变量中字符进行 JavaScript ENCODE, 将所有非白名单字符转义为 `\x` 形式,防止 XSS 攻击,也确保在 js 中输出的正确性。使用实例: ```js @@ -115,7 +115,7 @@ console.log(`var foo = "${this.helper.sjs(foo)}";`); // => var foo = "\\x22hello\\x22"; ``` -还有一种情况,有时候我们需要在 JavaScript 中输出 json ,若未做转义,易被利用为 XSS 漏洞。框架提供了 `helper.sjson()` 宏做 json encode,会遍历 json 中的 key ,将 value 的值中,所有非白名单字符转义为`\x`形式,防止 XSS 攻击。同时保持 json 结构不变。 +还有一种情况,有时候我们需要在 JavaScript 中输出 json ,若未做转义,易被利用为 XSS 漏洞。框架提供了 `helper.sjson()` 宏做 json encode,会遍历 json 中的 key ,将 value 的值中,所有非白名单字符转义为 `\x` 形式,防止 XSS 攻击。同时保持 json 结构不变。 若存在模板中输出一个 JSON 字符串给 JavaScript 使用的场景,请使用 `${this.helper.sjson(变量名)}` 进行转义。 **处理过程较复杂,性能损耗较大,请仅在必要时使用。** @@ -134,10 +134,10 @@ console.log(`var foo = "${this.helper.sjs(foo)}";`); #### 防范方式 -框架提供了`helper.shtml()`方法对字符串进行 XSS 过滤。 +框架提供了 `helper.shtml()` 方法对字符串进行 XSS 过滤。 -注意,将富文本(包含 html 代码的文本)当成变量直接在模版里面输出时,需要用到 shtml 来处理。 -使用 shtml 可以输出 html 的 tag,同时执行 XSS 的过滤动作,过滤掉非法的脚本。 +注意,将富文本(包含 HTML 代码的文本)当成变量直接在模版里面输出时,需要用到 shtml 来处理。 +使用 shtml 可以输出 HTML 的 tag,同时执行 XSS 的过滤动作,过滤掉非法的脚本。 **由于是一个非常复杂的安全处理过程,对服务器处理性能一定影响,如果不是输出 HTML,请勿使用。** @@ -173,9 +173,9 @@ options: - `config.helper.shtml.domainWhiteList: []` 可拓展 href 和 src 中允许的域名白名单。 注意,shtml 使用了严格的白名单机制,除了过滤掉 XSS 风险的字符串外, -在 [默认规则](https://github.com/leizongmin/js-xss/blob/master/lib/default.js) 外的 tag 和 attr 都会被过滤掉。 +在[默认规则](https://github.com/leizongmin/js-xss/blob/master/lib/default.js)外的 tag 和 attr 都会被过滤掉。 -例如 html 标签就不在白名单中, +例如 HTML 标签就不在白名单中, ```js const html = ''; @@ -189,19 +189,19 @@ ${helper.shtml($html)} 常见的 `data-xx` 属性由于不在白名单中,所以都会被过滤。 所以,一定要注意 shtml 的适用场景,一般是针对来自用户的富文本输入,切忌滥用,功能既受到限制,又会影响服务端性能。 -此类场景一般是论坛、评论系统等,即便是论坛等如果不支持 html 内容输入,也不要使用此 helper,直接使用 escape 即可。 +此类场景一般是论坛、评论系统等,即便是论坛等如果不支持 HTML 内容输入,也不要使用此 Helper,直接使用 `escape` 即可。 -### jsonp XSS +### JSONP XSS -jsonp 的 callback 参数非常危险,他有两种风险可能导致 XSS +JSONP 的 callback 参数非常危险,他有两种风险可能导致 XSS 1、callback 参数意外截断js代码,特殊字符单引号双引号,换行符均存在风险。 -2、callback 参数恶意添加标签(如 script ),造成 XSS 漏洞。 +2、callback 参数恶意添加标签(如 `