docs: update docs for koa v2
update readme and request, response toJSON method update readme update readme update readme update readme update readme update readme update docs update doc pretty readme update docs fix then callback
This commit is contained in:
parent
d134fff9e8
commit
1e38b13a94
7 changed files with 277 additions and 297 deletions
241
Readme.md
241
Readme.md
|
@ -5,7 +5,7 @@
|
||||||
[![build status][travis-image]][travis-url]
|
[![build status][travis-image]][travis-url]
|
||||||
[![Test coverage][coveralls-image]][coveralls-url]
|
[![Test coverage][coveralls-image]][coveralls-url]
|
||||||
|
|
||||||
Expressive HTTP middleware for node.js to make web applications and APIs more enjoyable to write. Koa's middleware stack flows in a stack-like manner, allowing you to perform actions downstream then filter and manipulate the response upstream. Koa's use of generators also greatly increases the readability and robustness of your application.
|
Expressive HTTP middleware framework for node.js to make web applications and APIs more enjoyable to write. Koa's middleware stack flows in a stack-like manner, allowing you to perform actions downstream then filter and manipulate the response upstream.
|
||||||
|
|
||||||
Only methods that are common to nearly all HTTP servers are integrated directly into Koa's small ~570 SLOC codebase. This
|
Only methods that are common to nearly all HTTP servers are integrated directly into Koa's small ~570 SLOC codebase. This
|
||||||
includes things like content negotiation, normalization of node inconsistencies, redirection, and a few others.
|
includes things like content negotiation, normalization of node inconsistencies, redirection, and a few others.
|
||||||
|
@ -13,12 +13,130 @@
|
||||||
Koa is not bundled with any middleware.
|
Koa is not bundled with any middleware.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
Koa requires __node v4.0.0__ or higher for (partial) ES2015 support.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ npm install koa
|
$ npm install koa@next
|
||||||
```
|
```
|
||||||
|
|
||||||
Koa requires __node v4.0.0__ or higher for (partial) ES2015 support.
|
## Hello koa
|
||||||
|
|
||||||
|
```js
|
||||||
|
const Koa = require('koa');
|
||||||
|
const app = new Koa();
|
||||||
|
|
||||||
|
// response
|
||||||
|
app.use(ctx => {
|
||||||
|
ctx.body = 'Hello Koa';
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(3000);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
- [Kick-Off-Koa](https://github.com/koajs/kick-off-koa) - An intro to koa via a set of self-guided workshops.
|
||||||
|
- [Workshop](https://github.com/koajs/workshop) - A workshop to learn the basics of koa, Express' spiritual successor.
|
||||||
|
- [Introduction Screencast](http://knowthen.com/episode-3-koajs-quickstart-guide/) - An introduction to installing and getting started with Koa
|
||||||
|
|
||||||
|
|
||||||
|
## Middleware
|
||||||
|
Koa is an middleware framework, it can take 3 different kind function as middleware:
|
||||||
|
|
||||||
|
* common function
|
||||||
|
* async function
|
||||||
|
* generatorFunction
|
||||||
|
|
||||||
|
Here we write an logger middleware with different function.
|
||||||
|
|
||||||
|
### Common function
|
||||||
|
```js
|
||||||
|
|
||||||
|
// Middleware normally take two parameters (ctx, next), ctx is the context for one request,
|
||||||
|
// next is an function that is invoked to execute the downstream middleware. It returns a Promise with a then function for running code after completion.
|
||||||
|
|
||||||
|
app.use((ctx, next) => {
|
||||||
|
const start = new Date;
|
||||||
|
return next().then(() => {
|
||||||
|
const ms = new Date - start;
|
||||||
|
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### ___async___ functions (Babel required)
|
||||||
|
|
||||||
|
```js
|
||||||
|
|
||||||
|
app.use(async (ctx, next) => {
|
||||||
|
const start = new Date;
|
||||||
|
await next();
|
||||||
|
const ms = new Date - start;
|
||||||
|
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
|
||||||
|
});
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### GeneratorFunction
|
||||||
|
|
||||||
|
To use generator functions, you must use a wrapper such as [co](https://github.com/tj/co) that is no longer supplied with Koa.
|
||||||
|
|
||||||
|
```js
|
||||||
|
|
||||||
|
app.use(co.wrap(function *(ctx, next){
|
||||||
|
const start = new Date;
|
||||||
|
yield next();
|
||||||
|
const ms = new Date - start;
|
||||||
|
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
|
||||||
|
}));
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Old signature middleware (v1.x)
|
||||||
|
|
||||||
|
If you want to use old signature or be compatible with old middleware, you must use [koa-convert](https://github.com/gyson/koa-convert) to convert legacy generator middleware to promise middleware.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const convert = require('koa-convert')
|
||||||
|
|
||||||
|
app.use(convert(function *(next){
|
||||||
|
const start = new Date;
|
||||||
|
yield next;
|
||||||
|
const ms = new Date - start;
|
||||||
|
console.log(`${this.method} ${this.url} - ${ms}ms`);
|
||||||
|
}));
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Babel setup
|
||||||
|
For Node 4.0 and Babel 6.0 you can setup like this
|
||||||
|
|
||||||
|
```bash
|
||||||
|
// install babel and required presets
|
||||||
|
$ npm install babel-core --save
|
||||||
|
$ npm install babel-preset-es2015-node5 --save
|
||||||
|
$ npm install babel-preset-stage-3 --save
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
// set babel in entry file
|
||||||
|
require("babel-core/register")({
|
||||||
|
presets: ['es2015-node5', 'stage-3']
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Running tests
|
||||||
|
|
||||||
|
```
|
||||||
|
$ make test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Authors
|
||||||
|
|
||||||
|
See [AUTHORS](AUTHORS).
|
||||||
|
|
||||||
## Community
|
## Community
|
||||||
|
|
||||||
|
@ -35,123 +153,6 @@ $ npm install koa
|
||||||
- [中文文档](https://github.com/turingou/koa-guide)
|
- [中文文档](https://github.com/turingou/koa-guide)
|
||||||
- __[#koajs]__ on freenode
|
- __[#koajs]__ on freenode
|
||||||
|
|
||||||
## Getting started
|
|
||||||
|
|
||||||
- [Kick-Off-Koa](https://github.com/koajs/kick-off-koa) - An intro to koa via a set of self-guided workshops.
|
|
||||||
- [Workshop](https://github.com/koajs/workshop) - A workshop to learn the basics of koa, Express' spiritual successor.
|
|
||||||
- [Introduction Screencast](http://knowthen.com/episode-3-koajs-quickstart-guide/) - An introduction to installing and getting started with Koa
|
|
||||||
|
|
||||||
## Example
|
|
||||||
```js
|
|
||||||
const Koa = require('koa');
|
|
||||||
const app = new Koa();
|
|
||||||
|
|
||||||
// logger
|
|
||||||
|
|
||||||
app.use((ctx, next) => {
|
|
||||||
const start = new Date;
|
|
||||||
return next().then(() => {
|
|
||||||
const ms = new Date - start;
|
|
||||||
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// response
|
|
||||||
|
|
||||||
app.use(ctx => {
|
|
||||||
ctx.body = 'Hello World';
|
|
||||||
});
|
|
||||||
|
|
||||||
app.listen(3000);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example with ___async___ functions (Babel required)
|
|
||||||
|
|
||||||
```js
|
|
||||||
const Koa = require('koa');
|
|
||||||
const app = new Koa();
|
|
||||||
|
|
||||||
// logger
|
|
||||||
|
|
||||||
app.use(async (ctx, next) => {
|
|
||||||
const start = new Date;
|
|
||||||
await next();
|
|
||||||
const ms = new Date - start;
|
|
||||||
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
|
|
||||||
});
|
|
||||||
|
|
||||||
// response
|
|
||||||
|
|
||||||
app.use(ctx => {
|
|
||||||
ctx.body = 'Hello World';
|
|
||||||
});
|
|
||||||
|
|
||||||
app.listen(3000);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example with generator
|
|
||||||
|
|
||||||
To use generator functions, you must use a wrapper such as [co](https://github.com/tj/co) that is no longer supplied with Koa.
|
|
||||||
|
|
||||||
```js
|
|
||||||
const Koa = require('koa');
|
|
||||||
const app = new Koa();
|
|
||||||
const co = require('co');
|
|
||||||
|
|
||||||
// logger
|
|
||||||
|
|
||||||
app.use(co.wrap(function *(ctx, next){
|
|
||||||
const start = new Date;
|
|
||||||
yield next();
|
|
||||||
const ms = new Date - start;
|
|
||||||
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
|
|
||||||
}));
|
|
||||||
|
|
||||||
// response
|
|
||||||
|
|
||||||
app.use(ctx => {
|
|
||||||
ctx.body = 'Hello World';
|
|
||||||
});
|
|
||||||
|
|
||||||
app.listen(3000);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example with old signature
|
|
||||||
|
|
||||||
If you want to use old signature or be compatible with old middleware, you must use [koa-convert](https://github.com/gyson/koa-convert) to convert legacy generator middleware to promise middleware.
|
|
||||||
|
|
||||||
```js
|
|
||||||
const Koa = require('koa');
|
|
||||||
const app = new Koa();
|
|
||||||
const convert = require('koa-convert')
|
|
||||||
|
|
||||||
// logger
|
|
||||||
|
|
||||||
app.use(convert(function *(next){
|
|
||||||
const start = new Date;
|
|
||||||
yield next;
|
|
||||||
const ms = new Date - start;
|
|
||||||
console.log(`${this.method} ${this.url} - ${ms}ms`);
|
|
||||||
}));
|
|
||||||
|
|
||||||
// response
|
|
||||||
|
|
||||||
app.use(ctx => {
|
|
||||||
ctx.body = 'Hello World';
|
|
||||||
});
|
|
||||||
|
|
||||||
app.listen(3000);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Running tests
|
|
||||||
|
|
||||||
```
|
|
||||||
$ make test
|
|
||||||
```
|
|
||||||
|
|
||||||
## Authors
|
|
||||||
|
|
||||||
See [AUTHORS](AUTHORS).
|
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
|
|
|
@ -1,90 +1,15 @@
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
Koa is supported in all versions of [iojs](https://iojs.org) without any flags.
|
Koa requires __node v4.0.0__ or higher for (partial) ES2015 support.
|
||||||
|
|
||||||
To use Koa with node, you must be running __node 0.11.16__ or higher for generator and promise support, and must run node(1)
|
You can quickly install a supposed version of node with your favorite version manager:
|
||||||
with the `--harmony-generators` or `--harmony` flag.
|
|
||||||
|
|
||||||
You can quickly install a supposed version of node/iojs with your favorite version manager:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ nvm install iojs
|
$ nvm install v4.0.0
|
||||||
$ npm i koa
|
$ npm i koa
|
||||||
$ node my-koa-app.js
|
$ node my-koa-app.js
|
||||||
```
|
```
|
||||||
|
|
||||||
# Application
|
|
||||||
|
|
||||||
A Koa application is an object containing an array of middleware generator 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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
The obligatory hello world application:
|
|
||||||
|
|
||||||
```js
|
|
||||||
const Koa = require('koa');
|
|
||||||
const app = new Koa();
|
|
||||||
|
|
||||||
app.use(function *(){
|
|
||||||
this.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 generators we can achieve "true" middleware. Contrasting Connect's implementation which
|
|
||||||
simply passes control through series of functions until one returns, Koa yields "downstream", then
|
|
||||||
control flows back "upstream".
|
|
||||||
|
|
||||||
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 `yield 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.
|
|
||||||
|
|
||||||
```js
|
|
||||||
const Koa = require('koa');
|
|
||||||
const app = new Koa();
|
|
||||||
|
|
||||||
// x-response-time
|
|
||||||
|
|
||||||
app.use(function *(next){
|
|
||||||
const start = new Date;
|
|
||||||
yield next;
|
|
||||||
const ms = new Date - start;
|
|
||||||
this.set('X-Response-Time', `${ms}ms`);
|
|
||||||
});
|
|
||||||
|
|
||||||
// logger
|
|
||||||
|
|
||||||
app.use(function *(next){
|
|
||||||
const start = new Date;
|
|
||||||
yield next;
|
|
||||||
const ms = new Date - start;
|
|
||||||
console.log(`${this.method} ${this.url} - ${ms}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
// response
|
|
||||||
|
|
||||||
app.use(function *(){
|
|
||||||
this.body = 'Hello World';
|
|
||||||
});
|
|
||||||
|
|
||||||
app.listen(3000);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Async Functions with Babel
|
## Async Functions with Babel
|
||||||
|
|
||||||
To use `async` functions in Koa, we recommend using [babel's require hook](http://babeljs.io/docs/usage/require/).
|
To use `async` functions in Koa, we recommend using [babel's require hook](http://babeljs.io/docs/usage/require/).
|
||||||
|
@ -106,7 +31,79 @@ For example, in your `.babelrc` file, you should have:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Currently, you could also use the [stage-3 preset](http://babeljs.io/docs/plugins/preset-stage-3/).
|
You can also use the [stage-3 preset](http://babeljs.io/docs/plugins/preset-stage-3/) instead.
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
The obligatory hello world application:
|
||||||
|
|
||||||
|
```js
|
||||||
|
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".
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
```js
|
||||||
|
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
|
## Settings
|
||||||
|
|
||||||
|
|
14
docs/faq.md
14
docs/faq.md
|
@ -16,16 +16,10 @@
|
||||||
## Does Koa replace Connect?
|
## Does Koa replace Connect?
|
||||||
|
|
||||||
No, just a different take on similar functionality
|
No, just a different take on similar functionality
|
||||||
now that generators allow us to write code with less
|
now that async functions allow us to write code with less
|
||||||
callbacks. Connect is equally capable, and some may still prefer it,
|
callbacks. Connect is equally capable, and some may still prefer it,
|
||||||
it's up to what you prefer.
|
it's up to what you prefer.
|
||||||
|
|
||||||
## Do generators decrease performance?
|
|
||||||
|
|
||||||
Barely - check out the benchmarks in our readme, the numbers
|
|
||||||
are more than fine, and there's no substitute for proper
|
|
||||||
horizontal scaling.
|
|
||||||
|
|
||||||
## Does Koa include routing?
|
## Does Koa include routing?
|
||||||
|
|
||||||
No - out of the box Koa has no form of routing, however
|
No - out of the box Koa has no form of routing, however
|
||||||
|
@ -41,15 +35,15 @@
|
||||||
|
|
||||||
## What custom properties do the Koa objects have?
|
## What custom properties do the Koa objects have?
|
||||||
|
|
||||||
Koa uses its own custom objects: `this`, `this.request`, and `this.response`.
|
Koa uses its own custom objects: `ctx`, `ctx.request`, and `ctx.response`.
|
||||||
These objects abstract node's `req` and `res` objects with convenience methods and getters/setters.
|
These objects abstract node's `req` and `res` objects with convenience methods and getters/setters.
|
||||||
Generally, properties added to these objects must obey the following rules:
|
Generally, properties added to these objects must obey the following rules:
|
||||||
|
|
||||||
- They must be either very commonly used and/or must do something useful
|
- They must be either very commonly used and/or must do something useful
|
||||||
- If a property exists as a setter, then it will also exist as a getter, but not vice versa
|
- If a property exists as a setter, then it will also exist as a getter, but not vice versa
|
||||||
|
|
||||||
Many of `this.request` and `this.response`'s properties are delegated to `this`.
|
Many of `ctx.request` and `ctx.response`'s properties are delegated to `ctx`.
|
||||||
If it's a getter/setter, then both the getter and the setter will strictly
|
If it's a getter/setter, then both the getter and the setter will strictly
|
||||||
correspond to either `this.request` or `this.response`.
|
correspond to either `ctx.request` or `ctx.response`.
|
||||||
|
|
||||||
Please think about these rules before suggesting additional properties.
|
Please think about these rules before suggesting additional properties.
|
||||||
|
|
137
docs/guide.md
137
docs/guide.md
|
@ -1,51 +1,39 @@
|
||||||
|
|
||||||
# Guide
|
# Guide
|
||||||
|
|
||||||
This guide covers Koa topics are not directly API related, such as best practices for writing middleware,
|
This guide covers Koa topics that are not directly API related, such as best practices for writing middleware, application structure suggestions, here we use async function as middleware you can also you commonFunction or generatorFunction which will be a little different.
|
||||||
application structure suggestions.
|
|
||||||
|
|
||||||
## Writing Middleware
|
## Writing Middleware
|
||||||
|
|
||||||
Koa middleware are simple functions which return a `GeneratorFunction`, and accept another. When
|
Koa middleware are simple functions which return a `MiddlewareFunction` with signature (ctx, next). When
|
||||||
the middleware is run by an "upstream" middleware, it must manually `yield` to the "downstream" middleware.
|
the middleware is run by an "upstream" middleware, it must manually invoke `next()` to run the "downstream" middleware.
|
||||||
|
|
||||||
For example if you wanted to track how long it takes for a request to propagate through Koa by adding an
|
For example if you wanted to track how long it takes for a request to propagate through Koa by adding an
|
||||||
`X-Response-Time` header field the middleware would look like the following:
|
`X-Response-Time` header field the middleware would look like the following:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
function *responseTime(next) {
|
async function responseTime(ctx, next) {
|
||||||
const start = new Date;
|
const start = new Date;
|
||||||
yield next;
|
await next();
|
||||||
const ms = new Date - start;
|
const ms = new Date - start;
|
||||||
this.set('X-Response-Time', `${ms}ms`);
|
ctx.set('X-Response-Time', `${ms}ms`);
|
||||||
}
|
}
|
||||||
|
|
||||||
app.use(responseTime);
|
app.use(responseTime);
|
||||||
```
|
```
|
||||||
|
|
||||||
Here's another way to write the same thing, inline:
|
If you're a front-end developer you can think any code before `next();` as the "capture" phase,
|
||||||
|
while any code after is the "bubble" phase. This crude gif illustrates how async function allow us
|
||||||
```js
|
|
||||||
app.use(function *(next){
|
|
||||||
const start = new Date;
|
|
||||||
yield next;
|
|
||||||
const ms = new Date - start;
|
|
||||||
this.set('X-Response-Time', `${ms}ms`);
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
If you're a front-end developer you can think any code before `yield next;` as the "capture" phase,
|
|
||||||
while any code after is the "bubble" phase. This crude gif illustrates how ES6 generators allow us
|
|
||||||
to properly utilize stack flow to implement request and response flows:
|
to properly utilize stack flow to implement request and response flows:
|
||||||
|
|
||||||
![koa middleware](/docs/middleware.gif)
|
![koa middleware](/docs/middleware.gif)
|
||||||
|
|
||||||
1. Create a date to track duration
|
1. Create a date to track duration
|
||||||
2. Yield control to the next middleware
|
2. Await control to the next middleware
|
||||||
3. Create another date to track response time
|
3. Create another date to track response time
|
||||||
4. Yield control to the next middleware
|
4. Await control to the next middleware
|
||||||
5. Yield immediately since `contentLength` only works with responses
|
5. Await immediately since `contentLength` only works with responses
|
||||||
6. Yield upstream to Koa's noop middleware
|
6. Await upstream to Koa's noop middleware
|
||||||
7. Ignore setting the body unless the path is "/"
|
7. Ignore setting the body unless the path is "/"
|
||||||
8. Set the response to "Hello World"
|
8. Set the response to "Hello World"
|
||||||
9. Ignore setting `Content-Length` when no body is present
|
9. Ignore setting `Content-Length` when no body is present
|
||||||
|
@ -55,18 +43,18 @@ app.use(function *(next){
|
||||||
13. Hand off to Koa to handle the response
|
13. Hand off to Koa to handle the response
|
||||||
|
|
||||||
|
|
||||||
Note that the final middleware (step __6__) yields to what looks to be nothing - it's actually
|
Note that the final middleware (step __6__) await to what looks to be nothing - it's actually
|
||||||
yielding to a no-op generator within Koa. This is so that every middleware can conform with the
|
yielding to a no-op promise within Koa. This is so that every middleware can conform with the
|
||||||
same API, and may be placed before or after others. If you removed `yield next;` from the furthest
|
same API, and may be placed before or after others. If you removed `next();` from the furthest
|
||||||
"downstream" middleware everything would function appropriately, however it would no longer conform
|
"downstream" middleware everything would function appropriately, however it would no longer conform
|
||||||
to this behaviour.
|
to this behaviour.
|
||||||
|
|
||||||
For example this would be fine:
|
For example this would be fine:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
app.use(function *response(){
|
app.use(async function response(ctx, next){
|
||||||
if ('/' != this.url) return;
|
if ('/' != this.url) return;
|
||||||
this.body = 'Hello World';
|
ctx.body = 'Hello World';
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -91,14 +79,14 @@ app.use(function *response(){
|
||||||
function logger(format) {
|
function logger(format) {
|
||||||
format = format || ':method ":url"';
|
format = format || ':method ":url"';
|
||||||
|
|
||||||
return function *(next){
|
return async function (ctx, next){
|
||||||
const str = format
|
const str = format
|
||||||
.replace(':method', this.method)
|
.replace(':method', ctx.method)
|
||||||
.replace(':url', this.url);
|
.replace(':url', ctx.url);
|
||||||
|
|
||||||
console.log(str);
|
console.log(str);
|
||||||
|
|
||||||
yield next;
|
await next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,121 +100,120 @@ app.use(logger(':method :url'));
|
||||||
|
|
||||||
```js
|
```js
|
||||||
function logger(format) {
|
function logger(format) {
|
||||||
return function *logger(next){
|
return async function logger(ctx, next){
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Combining multiple middleware
|
### Combining multiple middleware with koa-compose
|
||||||
|
|
||||||
Sometimes you want to "compose" multiple middleware into a single middleware for easy re-use or exporting. To do so, you may chain them together with `.call(this, next)`s, then return another function that yields the chain.
|
Sometimes you want to "compose" multiple middleware into a single middleware for easy re-use or exporting. You can use [koa-compose](https://github.com/koajs/compose)
|
||||||
|
|
||||||
```js
|
```js
|
||||||
function *random(next) {
|
const compose = require('koa-compose');
|
||||||
|
|
||||||
|
async function random(ctx, next) {
|
||||||
if ('/random' == this.path) {
|
if ('/random' == this.path) {
|
||||||
this.body = Math.floor(Math.random()*10);
|
ctx.body = Math.floor(Math.random()*10);
|
||||||
} else {
|
} else {
|
||||||
yield next;
|
await next();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function *backwards(next) {
|
async function backwards(ctx, next) {
|
||||||
if ('/backwards' == this.path) {
|
if ('/backwards' == this.path) {
|
||||||
this.body = 'sdrawkcab';
|
ctx.body = 'sdrawkcab';
|
||||||
} else {
|
} else {
|
||||||
yield next;
|
await next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function *pi(next) {
|
async function pi(ctx, next) {
|
||||||
if ('/pi' == this.path) {
|
if ('/pi' == this.path) {
|
||||||
this.body = String(Math.PI);
|
ctx.body = String(Math.PI);
|
||||||
} else {
|
} else {
|
||||||
yield next;
|
await next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function *all(next) {
|
const all = compose([random, backwards, pi])
|
||||||
yield random.call(this, backwards.call(this, pi.call(this, next)));
|
|
||||||
}
|
|
||||||
|
|
||||||
app.use(all);
|
app.use(all);
|
||||||
```
|
```
|
||||||
|
|
||||||
This is exactly what [koa-compose](https://github.com/koajs/compose) does, which Koa internally uses to create and dispatch the middleware stack.
|
|
||||||
|
|
||||||
### Response Middleware
|
### Response Middleware
|
||||||
|
|
||||||
Middleware that decide to respond to a request and wish to bypass downstream middleware may
|
Middleware that decide to respond to a request and wish to bypass downstream middleware may
|
||||||
simply omit `yield next`. Typically this will be in routing middleware, but this can be performed by
|
simply omit `next()`. Typically this will be in routing middleware, but this can be performed by
|
||||||
any. For example the following will respond with "two", however all three are executed, giving the
|
any. For example the following will respond with "two", however all three are executed, giving the
|
||||||
downstream "three" middleware a chance to manipulate the response.
|
downstream "three" middleware a chance to manipulate the response.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
app.use(function *(next){
|
app.use(async function (ctx, next){
|
||||||
console.log('>> one');
|
console.log('>> one');
|
||||||
yield next;
|
await next();
|
||||||
console.log('<< one');
|
console.log('<< one');
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(function *(next){
|
app.use(async function (ctx, next){
|
||||||
console.log('>> two');
|
console.log('>> two');
|
||||||
this.body = 'two';
|
ctx.body = 'two';
|
||||||
yield next;
|
await next();
|
||||||
console.log('<< two');
|
console.log('<< two');
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(function *(next){
|
app.use(async function (ctx, next){
|
||||||
console.log('>> three');
|
console.log('>> three');
|
||||||
yield next;
|
await next();
|
||||||
console.log('<< three');
|
console.log('<< three');
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
The following configuration omits `yield next` in the second middleware, and will still respond
|
The following configuration omits `next()` in the second middleware, and will still respond
|
||||||
with "two", however the third (and any other downstream middleware) will be ignored:
|
with "two", however the third (and any other downstream middleware) will be ignored:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
app.use(function *(next){
|
app.use(async function (ctx, next){
|
||||||
console.log('>> one');
|
console.log('>> one');
|
||||||
yield next;
|
await next();
|
||||||
console.log('<< one');
|
console.log('<< one');
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(function *(next){
|
app.use(async function (ctx, next){
|
||||||
console.log('>> two');
|
console.log('>> two');
|
||||||
this.body = 'two';
|
ctx.body = 'two';
|
||||||
console.log('<< two');
|
console.log('<< two');
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(function *(next){
|
app.use(async function (ctx, next){
|
||||||
console.log('>> three');
|
console.log('>> three');
|
||||||
yield next;
|
await next();
|
||||||
console.log('<< three');
|
console.log('<< three');
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
When the furthest downstream middleware executes `yield next;` it's really yielding to a noop
|
When the furthest downstream middleware executes `next();`, it's really yielding to a noop
|
||||||
function, allowing the middleware to compose correctly anywhere in the stack.
|
function, allowing the middleware to compose correctly anywhere in the stack.
|
||||||
|
|
||||||
## Async operations
|
## Async operations
|
||||||
|
|
||||||
The [Co](https://github.com/visionmedia/co) forms Koa's foundation for generator delegation, allowing
|
Async function and promise forms Koa's foundation, allowing
|
||||||
you to write non-blocking sequential code. For example this middleware reads the filenames from `./docs`,
|
you to write non-blocking sequential code. For example this middleware reads the filenames from `./docs`,
|
||||||
and then reads the contents of each markdown file in parallel before assigning the body to the joint result.
|
and then reads the contents of each markdown file in parallel before assigning the body to the joint result.
|
||||||
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const fs = require('co-fs');
|
const fs = require('fs-promise');
|
||||||
|
|
||||||
app.use(function *(){
|
app.use(async function (ctx, next){
|
||||||
const paths = yield fs.readdir('docs');
|
const paths = await fs.readdir('docs');
|
||||||
|
const files = await Promise.all(paths.map(path => fs.readFile(`docs/${path}`, 'utf8')));
|
||||||
|
|
||||||
const files = yield paths.map(path => fs.readFile(`docs/${path}`, 'utf8'));
|
ctx.type = 'markdown';
|
||||||
|
ctx.body = files.join('');
|
||||||
this.type = 'markdown';
|
|
||||||
this.body = files.join('');
|
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ THIS DOCUMENT IS IN PROGRESS. THIS PARAGRAPH SHALL BE REMOVED WHEN THIS DOCUMENT
|
||||||
|
|
||||||
|
|
||||||
Thus, if you'd like to be closer to node.js and traditional node.js-style coding, you probably want to stick to Connect/Express or similar frameworks.
|
Thus, if you'd like to be closer to node.js and traditional node.js-style coding, you probably want to stick to Connect/Express or similar frameworks.
|
||||||
If you want to dive into the land of generators, use Koa.
|
If you want to get rid of callbacks, use Koa.
|
||||||
|
|
||||||
As result of this different philosophy is that traditional node.js "middleware", i.e. functions of the form `(req, res, next)`, are incompatible with Koa. Your application will essentially have to be rewritten from the ground, up.
|
As result of this different philosophy is that traditional node.js "middleware", i.e. functions of the form `(req, res, next)`, are incompatible with Koa. Your application will essentially have to be rewritten from the ground, up.
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ const parse = require('parseurl');
|
||||||
const qs = require('querystring');
|
const qs = require('querystring');
|
||||||
const typeis = require('type-is');
|
const typeis = require('type-is');
|
||||||
const fresh = require('fresh');
|
const fresh = require('fresh');
|
||||||
|
const only = require('only');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prototype.
|
* Prototype.
|
||||||
|
@ -618,10 +619,10 @@ module.exports = {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
toJSON() {
|
toJSON() {
|
||||||
return {
|
return only(this, [
|
||||||
method: this.method,
|
'method',
|
||||||
url: this.url,
|
'url',
|
||||||
header: this.header
|
'header'
|
||||||
};
|
]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,9 +15,9 @@ const typeis = require('type-is').is;
|
||||||
const statuses = require('statuses');
|
const statuses = require('statuses');
|
||||||
const destroy = require('destroy');
|
const destroy = require('destroy');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const path = require('path');
|
const extname = require('path').extname;
|
||||||
const vary = require('vary');
|
const vary = require('vary');
|
||||||
const extname = path.extname;
|
const only = require('only');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prototype.
|
* Prototype.
|
||||||
|
@ -516,10 +516,10 @@ module.exports = {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
toJSON() {
|
toJSON() {
|
||||||
return {
|
return only(this, [
|
||||||
status: this.status,
|
'status',
|
||||||
message: this.message,
|
'message',
|
||||||
header: this.header
|
'header'
|
||||||
};
|
]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue