docs: update history and docs for v2

This commit is contained in:
jongleberry 2016-03-22 11:03:10 -07:00
parent daf688bf69
commit 2b094eb895
6 changed files with 140 additions and 95 deletions

View file

@ -1,4 +1,33 @@
2.0.0 /
==================
* merge v1.1.2 and v1.2.0 changes
* include `koa-convert` so that generator functions still work
* NOTE: generator functions are deprecated in v2 and will be removed in v3
* improve linting
* improve docs
1.2.0 / 2016-03-03
==================
* add support for `err.headers` in `ctx.onerror()`
- see: https://github.com/koajs/koa/pull/668
- note: you should set these headers in your custom error handlers as well
- docs: https://github.com/koajs/koa/blob/master/docs/error-handling.md
* fix `cookies`' detection of http/https
- see: https://github.com/koajs/koa/pull/614
* deprecate `app.experimental = true`. Koa v2 does not use this signature.
* add a code of conduct
* test against the latest version of node
* add a lot of docs
1.1.2 / 2015-11-05
==================
* ensure parseurl always working as expected
* fix Application.inspect() missing .proxy value.
2.0.0-alpha.3 / 2015-11-05
==================

View file

@ -8,14 +8,14 @@
which would force middleware to re-implement this common functionality.
A `Context` is created _per_ request, and is referenced in middleware
as the receiver, or the `this` identifier, as shown in the following
as the receiver, or the `ctx` identifier, as shown in the following
snippet:
```js
app.use(function * () {
this; // is the Context
this.request; // is a koa Request
this.response; // is a koa Response
app.use(async (ctx, next) => {
ctx; // is the Context
ctx.request; // is a koa Request
ctx.response; // is a koa Response
});
```
@ -55,7 +55,7 @@ app.use(function * () {
The recommended namespace for passing information through middleware and to your frontend views.
```js
this.state.user = yield User.find(id);
ctx.state.user = yield User.find(id);
```
### ctx.app
@ -90,13 +90,13 @@ koa uses the [cookies](https://github.com/jed/cookies) module where options are
The following combinations are allowed:
```js
this.throw(403);
this.throw('name required', 400);
this.throw(400, 'name required');
this.throw('something exploded');
ctx.throw(403);
ctx.throw('name required', 400);
ctx.throw(400, 'name required');
ctx.throw('something exploded');
```
For example `this.throw('name required', 400)` is equivalent to:
For example `ctx.throw('name required', 400)` is equivalent to:
```js
const err = new Error('name required');
@ -113,8 +113,8 @@ throw err;
You may optionally pass a `properties` object which is merged into the error as-is, useful for decorating machine-friendly errors which are reported to the requester upstream.
```js
this.throw(401, 'access_denied', { user: user });
this.throw('access_denied', { user: user });
ctx.throw(401, 'access_denied', { user: user });
ctx.throw('access_denied', { user: user });
```
koa uses [http-errors](https://github.com/jshttp/http-errors) to create errors.
@ -126,14 +126,14 @@ koa uses [http-errors](https://github.com/jshttp/http-errors) to create errors.
method.
```js
this.assert(this.state.user, 401, 'User not found. Please login!');
ctx.assert(ctx.state.user, 401, 'User not found. Please login!');
```
koa uses [http-assert](https://github.com/jshttp/http-assert) for assertions.
### ctx.respond
To bypass Koa's built-in response handling, you may explicitly set `this.respond = false;`. Use this if you want to write to the raw `res` object instead of letting Koa handle the response for you.
To bypass Koa's built-in response handling, you may explicitly set `ctx.respond = false;`. Use this if you want to write to the raw `res` object instead of letting Koa handle the response for you.
Note that using this is __not__ supported by Koa. This may break intended functionality of Koa middleware and Koa itself. Using this property is considered a hack and is only a convenience to those wishing to use traditional `fn(req, res)` functions and middleware within Koa.

View file

@ -179,7 +179,7 @@ app.keys = new KeyGrip(['im a newer secret', 'i like turtle'], 'sha256');
with the `{ signed: true }` option:
```js
this.cookies.set('name', 'tobi', { signed: true });
ctx.cookies.set('name', 'tobi', { signed: true });
```
## app.context

View file

@ -44,7 +44,7 @@
Get origin of URL, include `protocol` and `host`.
```js
this.request.origin
ctx.request.origin
// => http://example.com
```
@ -53,7 +53,7 @@ this.request.origin
Get full request URL, include `protocol`, `host` and `url`.
```js
this.request.href
ctx.request.href
// => http://example.com/foo/bar?q=1
```
@ -96,7 +96,7 @@ this.request.href
Get request `Content-Type` void of parameters such as "charset".
```js
const ct = this.request.type
const ct = ctx.request.type
// => "image/png"
```
@ -105,7 +105,7 @@ const ct = this.request.type
Get request charset when present, or `undefined`:
```js
this.request.charset
ctx.request.charset
// => "utf-8"
```
@ -130,7 +130,7 @@ this.request.charset
setter does _not_ support nested objects.
```js
this.query = { next: '/login' }
ctx.query = { next: '/login' }
```
### request.fresh
@ -140,18 +140,18 @@ this.query = { next: '/login' }
```js
// freshness check requires status 20x or 304
this.status = 200;
this.set('ETag', '123');
ctx.status = 200;
ctx.set('ETag', '123');
// cache is ok
if (this.fresh) {
this.status = 304;
if (ctx.fresh) {
ctx.status = 304;
return;
}
// cache is stale
// fetch new data
this.body = yield db.find('something');
ctx.body = yield db.find('something');
```
### request.stale
@ -165,7 +165,7 @@ this.body = yield db.find('something');
### request.secure
Shorthand for `this.protocol == "https"` to check if a request was
Shorthand for `ctx.protocol == "https"` to check if a request was
issued via TLS.
### request.ip
@ -188,8 +188,8 @@ this.body = yield db.find('something');
parts of the host. This can be changed by setting `app.subdomainOffset`.
For example, if the domain is "tobi.ferrets.example.com":
If `app.subdomainOffset` is not set, this.subdomains is `["ferrets", "tobi"]`.
If `app.subdomainOffset` is 3, this.subdomains is `["tobi"]`.
If `app.subdomainOffset` is not set, `ctx.subdomains` is `["ferrets", "tobi"]`.
If `app.subdomainOffset` is 3, `ctx.subdomains` is `["tobi"]`.
### request.is(types...)
@ -201,26 +201,26 @@ this.body = yield db.find('something');
```js
// With Content-Type: text/html; charset=utf-8
this.is('html'); // => 'html'
this.is('text/html'); // => 'text/html'
this.is('text/*', 'text/html'); // => 'text/html'
ctx.is('html'); // => 'html'
ctx.is('text/html'); // => 'text/html'
ctx.is('text/*', 'text/html'); // => 'text/html'
// When Content-Type is application/json
this.is('json', 'urlencoded'); // => 'json'
this.is('application/json'); // => 'application/json'
this.is('html', 'application/*'); // => 'application/json'
ctx.is('json', 'urlencoded'); // => 'json'
ctx.is('application/json'); // => 'application/json'
ctx.is('html', 'application/*'); // => 'application/json'
this.is('html'); // => false
ctx.is('html'); // => false
```
For example if you want to ensure that
only images are sent to a given route:
```js
if (this.is('image/*')) {
if (ctx.is('image/*')) {
// process
} else {
this.throw(415, 'images only!');
ctx.throw(415, 'images only!');
}
```
@ -247,45 +247,45 @@ In the case of missing accept headers where any type is acceptable, the first ty
```js
// Accept: text/html
this.accepts('html');
ctx.accepts('html');
// => "html"
// Accept: text/*, application/json
this.accepts('html');
ctx.accepts('html');
// => "html"
this.accepts('text/html');
ctx.accepts('text/html');
// => "text/html"
this.accepts('json', 'text');
ctx.accepts('json', 'text');
// => "json"
this.accepts('application/json');
ctx.accepts('application/json');
// => "application/json"
// Accept: text/*, application/json
this.accepts('image/png');
this.accepts('png');
ctx.accepts('image/png');
ctx.accepts('png');
// => false
// Accept: text/*;q=.5, application/json
this.accepts(['html', 'json']);
this.accepts('html', 'json');
ctx.accepts(['html', 'json']);
ctx.accepts('html', 'json');
// => "json"
// No Accept header
this.accepts('html', 'json');
ctx.accepts('html', 'json');
// => "html"
this.accepts('json', 'html');
ctx.accepts('json', 'html');
// => "json"
```
You may call `this.accepts()` as many times as you like,
You may call `ctx.accepts()` as many times as you like,
or use a switch:
```js
switch (this.accepts('json', 'html', 'text')) {
switch (ctx.accepts('json', 'html', 'text')) {
case 'json': break;
case 'html': break;
case 'text': break;
default: this.throw(406, 'json, html, or text only');
default: ctx.throw(406, 'json, html, or text only');
}
```
@ -295,10 +295,10 @@ switch (this.accepts('json', 'html', 'text')) {
```js
// Accept-Encoding: gzip
this.acceptsEncodings('gzip', 'deflate', 'identity');
ctx.acceptsEncodings('gzip', 'deflate', 'identity');
// => "gzip"
this.acceptsEncodings(['gzip', 'deflate', 'identity']);
ctx.acceptsEncodings(['gzip', 'deflate', 'identity']);
// => "gzip"
```
@ -307,7 +307,7 @@ this.acceptsEncodings(['gzip', 'deflate', 'identity']);
```js
// Accept-Encoding: gzip, deflate
this.acceptsEncodings();
ctx.acceptsEncodings();
// => ["gzip", "deflate", "identity"]
```
@ -320,10 +320,10 @@ this.acceptsEncodings();
```js
// Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5
this.acceptsCharsets('utf-8', 'utf-7');
ctx.acceptsCharsets('utf-8', 'utf-7');
// => "utf-8"
this.acceptsCharsets(['utf-7', 'utf-8']);
ctx.acceptsCharsets(['utf-7', 'utf-8']);
// => "utf-8"
```
@ -332,7 +332,7 @@ this.acceptsCharsets(['utf-7', 'utf-8']);
```js
// Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5
this.acceptsCharsets();
ctx.acceptsCharsets();
// => ["utf-8", "utf-7", "iso-8859-1"]
```
@ -343,10 +343,10 @@ this.acceptsCharsets();
```js
// Accept-Language: en;q=0.8, es, pt
this.acceptsLanguages('es', 'en');
ctx.acceptsLanguages('es', 'en');
// => "es"
this.acceptsLanguages(['en', 'es']);
ctx.acceptsLanguages(['en', 'es']);
// => "es"
```
@ -355,7 +355,7 @@ this.acceptsLanguages(['en', 'es']);
```js
// Accept-Language: en;q=0.8, es, pt
this.acceptsLanguages();
ctx.acceptsLanguages();
// => ["es", "pt", "en"]
```

View file

@ -104,7 +104,7 @@ so you can make a correction.
### response.length
Return response Content-Length as a number when present, or deduce
from `this.body` when possible, or `undefined`.
from `ctx.body` when possible, or `undefined`.
### response.body
@ -149,7 +149,7 @@ If `response.status` has not been set, Koa will automatically set the status to
const PassThrough = require('stream').PassThrough;
app.use(function * (next) {
this.body = someHTTPStream.on('error', this.onerror).pipe(PassThrough());
ctx.body = someHTTPStream.on('error', ctx.onerror).pipe(PassThrough());
});
```
@ -162,7 +162,7 @@ app.use(function * (next) {
Get a response header field value with case-insensitive `field`.
```js
const etag = this.get('ETag');
const etag = ctx.get('ETag');
```
### response.set(field, value)
@ -170,14 +170,14 @@ const etag = this.get('ETag');
Set response header `field` to `value`:
```js
this.set('Cache-Control', 'no-cache');
ctx.set('Cache-Control', 'no-cache');
```
### response.append(field, value)
Append additional header `field` with value `val`.
```js
this.append('Link', '<http://127.0.0.1/>');
ctx.append('Link', '<http://127.0.0.1/>');
```
### response.set(fields)
@ -185,7 +185,7 @@ this.append('Link', '<http://127.0.0.1/>');
Set several response header `fields` with an object:
```js
this.set({
ctx.set({
'Etag': '1234',
'Last-Modified': date
});
@ -200,7 +200,7 @@ this.set({
Get response `Content-Type` void of parameters such as "charset".
```js
const ct = this.type;
const ct = ctx.type;
// => "image/png"
```
@ -209,10 +209,10 @@ const ct = this.type;
Set response `Content-Type` via mime string or file extension.
```js
this.type = 'text/plain; charset=utf-8';
this.type = 'image/png';
this.type = '.png';
this.type = 'png';
ctx.type = 'text/plain; charset=utf-8';
ctx.type = 'image/png';
ctx.type = '.png';
ctx.type = 'png';
```
Note: when appropriate a `charset` is selected for you, for
@ -222,7 +222,7 @@ this.type = 'png';
### response.is(types...)
Very similar to `this.request.is()`.
Very similar to `ctx.request.is()`.
Check whether the response type is one of the supplied types.
This is particularly useful for creating middleware that
manipulate responses.
@ -236,13 +236,13 @@ const minify = require('html-minifier');
app.use(function * minifyHTML(next) {
yield next;
if (!this.response.is('html')) return;
if (!ctx.response.is('html')) return;
let body = this.body;
let body = ctx.body;
if (!body || body.pipe) return;
if (Buffer.isBuffer(body)) body = body.toString();
this.body = minify(body);
ctx.body = minify(body);
});
```
@ -255,19 +255,19 @@ app.use(function * minifyHTML(next) {
is not present `alt` or "/" is used.
```js
this.redirect('back');
this.redirect('back', '/index.html');
this.redirect('/login');
this.redirect('http://google.com');
ctx.redirect('back');
ctx.redirect('back', '/index.html');
ctx.redirect('/login');
ctx.redirect('http://google.com');
```
To alter the default status of `302`, simply assign the status
before or after this call. To alter the body, assign it after this call:
```js
this.status = 301;
this.redirect('/cart');
this.body = 'Redirecting to shopping cart';
ctx.status = 301;
ctx.redirect('/cart');
ctx.body = 'Redirecting to shopping cart';
```
### response.attachment([filename])
@ -291,7 +291,7 @@ this.body = 'Redirecting to shopping cart';
You can either set it as a `Date` or date string.
```js
this.response.lastModified = new Date();
ctx.response.lastModified = new Date();
```
### response.etag=
@ -300,7 +300,7 @@ this.response.lastModified = new Date();
Note that there is no corresponding `response.etag` getter.
```js
this.response.etag = crypto.createHash('md5').update(this.body).digest('hex');
ctx.response.etag = crypto.createHash('md5').update(ctx.body).digest('hex');
```
### response.vary(field)

View file

@ -2,16 +2,16 @@
## Try-Catch
Using generators means that you can try-catch `next`. For example,
this example prepends all error messages with "Error: "
Using async functions means that you can try-catch `next`.
This example adds a `.status` to all errors:
```js
app.use(async (ctx, next) => {
try {
await next();
} catch (error) {
error.message = 'Error: ' + error.message;
throw error;
} catch (err) {
err.status = err.statusCode || err.status || 500;
throw err;
}
});
```
@ -32,13 +32,29 @@
will then be set. You can use a try-catch, as specified
above, to add a header to this list.
Here is an example of creating your own error handler:
```js
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
// will only respond with JSON
this.status = err.statusCode || err.status || 500;
this.body = {
message: err.message
};
}
})
```
## The Error Event
Error handlers can be specified with `app.on('error')`.
If no error handler is specified, a default error handler
is used. Error handlers recieve all errors that make their
Error event listeners can be specified with `app.on('error')`.
If no error listener is specified, a default error listener
is used. Error listener receive all errors that make their
way back through the middleware chain, if an error is caught
and not thrown again, it will not be handled by the error
handler. If not error event handler is specified, then
and not thrown again, it will not be passed to the error
listener. If no error event listener is specified, then
`app.onerror` will be used, which simply log the error if
`error.expose` is true and `app.silent` is false.