原文

安装(Installation)

Koa requires node v4.0.0 or higher for (partial) ES2015 support.

Koa要求node v4.0.0或以上版本,以满足对ES2015规范的部分要求。

You can quickly install a supposed version of node with your favorite version manager:

可以使用自己偏爱的版本管理器快速安装指定的Node版本:

$ nvm install v4.0.0
$ npm i koa
$ node my-koa-app.js

基于Babel的异步方法(Async Functions with Babel)

To use async functions in Koa, we recommend using babel’s require hook.

建议通过babel的require钩子以在Koa中启用async形式的异步方法。

require('babel-core/register');
// require the rest of the app that needs to be transpiled after the hook
// 引用应用中需要转换的其他模块必须放置在这个钩子之后
const app = require('./app');

To parse and transpile async functions, you should at a minimum have the transform-async-to-generator or transform-async-to-module-method plugins. For example, in your .babelrc file, you should have:

为了正确解析和转换异步方法,至少要引用transform-async-to-generator或者transform-async-to-module-method插件。比如,在.babelrc文件中应该包含:

{
  "plugins": ["transform-async-to-generator"]
}

You can also use the stage-3 preset instead.

也可以用stage-3预置集合替代上述插件的引用。

(编者按:在Node.js 7.0中启用–harmony-async-await标记即可可代替Babel)。

应用(Application)

A Koa application is an object containing an array of middleware functions which are composed and executed in a stack-like manner upon request. Koa is similar to many other middleware systems that you may have encountered such as Ruby’s Rack, Connect, and so on - however a key design decision was made to provide high level “sugar” at the otherwise low-level middleware layer. This improves interoperability, robustness, and makes writing middleware much more enjoyable.

Koa应用是一个包含一组中间件函数的对象,其组织和执行的形式类似于堆栈,以对请求进行处理。 Koa和常见的中间件系统相似,比如Ruby的Rack框架、Connect等等,特色设计是在底层中间件中提供了高级的“糖”。这使得互操作性更强,鲁棒性更高,同时让中间件的编写更简洁轻松。

This includes methods for common tasks like content-negotiation, cache freshness, proxy support, and redirection among others. Despite supplying a reasonably large number of helpful methods Koa maintains a small footprint, as no middleware are bundled.

其中包含了如内容协商、缓存新鲜度、代理支持和重定向等常规任务的处理方法。尽管Koa提供了很多有用的方法,但框架中不绑定任何插件,以维持一个小巧的状态。

The obligatory hello world application:

经典的的Hello World程序如下:

const Koa = require('koa');
const app = new Koa();

app.use(ctx => {
  ctx.body = 'Hello World';
});

app.listen(3000);

级联(Cascading)

Koa middleware cascade in a more traditional way as you may be used to with similar tools - this was previously difficult to make user friendly with node’s use of callbacks. However with async functions we can achieve “true” middleware. Contrasting Connect’s implementation which simply passes control through series of functions until one returns, Koa invoke “downstream”, then control flows back “upstream”.

Koa中间件的级联方式跟传统级联实现方式一致,可能你使用过类似工具,这种中间件形式跟之前Node中常用的回调方式差异较大,会有一定的上手难度。 但是通过异步函数在Koa中可以实现真正的中间件。 Connect中级联的实现方式是:通过简单的传递控制函数序列并通过返回标识级联结束,在Koa中级联的实现方式与其完全不同,Koa首先调用中间件的“下行流“处理,然后控制中间件的反向“上行流”处理。

The following example responds with “Hello World”, however first the request flows through the x-response-time and logging middleware to mark when the request started, then continue to yield control through the response middleware. When a middleware invokes next() the function suspends and passes control to the next middleware defined. After there are no more middleware to execute downstream, the stack will unwind and each middleware is resumed to perform its upstream behaviour.

以下面“Hello World”的为例,请求依次通过x-response-timelogging中间件处理,再由响应中间件处理。 当中间件调用next()方法的时候,会暂停当前中间件的执行,同时下一个被定义的中间件会被调用执行。 当下行流中的中间件都被执行过的时候,中间件的调用堆栈会依次弹出执行完成上行流中的程序处理。

const Koa = require('koa');
const app = new Koa();

// x-response-time

app.use(async function (ctx, next) {
  const start = new Date();
  await next();
  const ms = new Date() - start;
  ctx.set('X-Response-Time', `${ms}ms`);
});

// logger

app.use(async function (ctx, next) {
  const start = new Date();
  await next();
  const ms = new Date() - start;
  console.log(`${ctx.method} ${ctx.url} - ${ms}`);
});

// response

app.use(ctx => {
  ctx.body = 'Hello World';
});

app.listen(3000);

设置(Settings)

Application settings are properties on the app instance, currently the following are supported:

  • app.name optionally give your application a name
  • app.env defaulting to the NODE_ENV or “development”
  • app.proxy when true proxy header fields will be trusted
  • app.subdomainOffset offset of .subdomains to ignore [2]

设置应用程序只需要为app实例对象属性赋值即可,目前支持下列设置项:

  • app.name 为应用程序附加设置一个名字;
  • app.env 默认同NODE_ENV值,或者是“development”;
  • app.proxytrue时,则代理头字段会被信任(编者按:此处不太准确,后续用到的时候进行修正);
  • app.subdomainOffset 忽略.subdomains的偏移。

app.listen(…)

A Koa application is not a 1-to-1 representation of a HTTP server. One or more Koa applications may be mounted together to form larger applications with a single HTTP server.

Koa应用跟HTTP服务器不局限于一对一的关系,可以将一个或多个Koa应用集成在同一个HTTP服务器上以构成更复杂的应用。

Create and return an HTTP server, passing the given arguments to Server#listen(). These arguments are documented on nodejs.org. The following is a useless Koa application bound to port 3000:

实例化并返回一个HTTP服务器,并将其传递给Server#listen()方法。可以在nodejs.org上查看参数使用的具体文档。下面是一个绑定在3000端口号上的没有实际意义的Koa应用。

const Koa = require('koa');
const app = new Koa();
app.listen(3000);

The app.listen(...) method is simply sugar for the following:

app.listen(...)方法是下面代码的简化形式:

const http = require('http');
const Koa = require('koa');
const app = new Koa();
http.createServer(app.callback()).listen(3000);

This means you can spin up the same application as both HTTP and HTTPS or on multiple addresses:

这就意味着同一个应用程同时应用HTTP和HTTPS协议,也可以用多个地址同时提供服务:

const http = require('http');
const Koa = require('koa');
const app = new Koa();
http.createServer(app.callback()).listen(3000);
http.createServer(app.callback()).listen(3001);

app.callback()

Return a callback function suitable for the http.createServer() method to handle a request. You may also use this callback function to mount your koa app in a Connect/Express app.

返回一个适用于http.createServer()方法的回调函数,用来处理请求。 也可以通过该回调函数将Koa应用集成到Connect或Express应用中。

app.use(function)

Add the given middleware function to this application. See Middleware for more information.

添加给定的中间件函数到当前应用中。去Middleware查看更多的信息。

app.keys=

Set signed cookie keys.

设置Cookie签名密钥。

These are passed to KeyGrip, however you may also pass your own KeyGrip instance. For example the following are acceptable:

这个密钥会传递给KeyGrip使用,也可以将KeyGrip的实例对象直接赋值作为密钥。下面两种写法都是正确的:

app.keys = ['im a newer secret', 'i like turtle'];
app.keys = new KeyGrip(['im a newer secret', 'i like turtle'], 'sha256');

These keys may be rotated and are used when signing cookies with the { signed: true } option:

当设置cookie的设置为{signed: true}时,该密钥会用来对该cookie进行加密和解密。

ctx.cookies.set('name', 'tobi', { signed: true });

app.context

app.context is the prototype from which ctx is created from.

You may add additional properties to ctx by editing app.context. This is useful for adding properties or methods to ctx to be used across your entire app, which may be more performant (no middleware) and/or easier (fewer require()s) at the expense of relying more on ctx, which could be considered an anti-pattern.

For example, to add a reference to your database from ctx:

app.context是创建ctx对象的原型对象(编者按:此处尚未验证,结合下面的表述,原型对象最省资源)。

可以对app.context对象进行编辑以将属性附加到ctx对象上。可以将需要在整个应用中使用的属性和方法用这种方式添加到ctx对象上,优点是:性能更好(不依赖于中间件),实现更简单(不需要额外的require());同时要注意这是一种反模式。

app.context.db = db();

app.use(async (ctx) => {
  console.log(ctx.db);
});

Note:

  • Many properties on ctx are defined using getters, setters, and Object.defineProperty(). You can only edit these properties (not recommended) by using Object.defineProperty() on app.context. See https://github.com/koajs/koa/issues/652.
  • Mounted apps currently use its parent’s ctx and settings. Thus, mounted apps are really just groups of middleware.

备注:

  • ctx中的很多内置属性是使用getter,setter和Object.defineProperty()方式定义的,要修改(不推荐)这些属性只能通过Object.defineProperty()方法在app.context上修改。可以参考
  • 在挂载的应用中会直接使用其被挂载的父级应用的ctx对象和设置。总之,挂载的应用仅仅是一组中间件。

错误处理(Error Handling)

By default outputs all errors to stderr unless app.silent is true. The default error handler also won’t outputs errors when err.status is 404 or err.expose is true. To perform custom error-handling logic such as centralized logging you can add an “error” event listener:

默认情况下会将所有的错误信息输出到stderr中,除非app.slient设置为true。 当err.status404或者err.exposetrue时,默认的错误处理器不会把这些错误输出。

app.on('error', err =>
  log.error('server error', err)
);

If an error is in the req/res cycle and it is not possible to respond to the client, the Context instance is also passed:

如果在请求处理或回复处理循环中出现错误,这个错误能被发送到客户端,当前错误的Context实例应该被记录到日志中:

app.on('error', (err, ctx) =>
  log.error('server error', err, ctx)
);

When an error occurs and it is still possible to respond to the client, aka no data has been written to the socket, Koa will respond appropriately with a 500 “Internal Server Error”. In either case an app-level “error” is emitted for logging purposes.

当错误发生并且可以响应客户端时,由于没有数据写入套接字,Koa将响应“Internal Server Error”。 另外在任何情况下,当应用级“错误”发生时都需要将其记录在日志中。