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 2.0.0-alpha.3 / 2015-11-05
================== ==================

View file

@ -8,14 +8,14 @@
which would force middleware to re-implement this common functionality. which would force middleware to re-implement this common functionality.
A `Context` is created _per_ request, and is referenced in middleware 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: snippet:
```js ```js
app.use(function * () { app.use(async (ctx, next) => {
this; // is the Context ctx; // is the Context
this.request; // is a koa Request ctx.request; // is a koa Request
this.response; // is a koa Response 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. The recommended namespace for passing information through middleware and to your frontend views.
```js ```js
this.state.user = yield User.find(id); ctx.state.user = yield User.find(id);
``` ```
### ctx.app ### ctx.app
@ -90,13 +90,13 @@ koa uses the [cookies](https://github.com/jed/cookies) module where options are
The following combinations are allowed: The following combinations are allowed:
```js ```js
this.throw(403); ctx.throw(403);
this.throw('name required', 400); ctx.throw('name required', 400);
this.throw(400, 'name required'); ctx.throw(400, 'name required');
this.throw('something exploded'); 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 ```js
const err = new Error('name required'); 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. 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 ```js
this.throw(401, 'access_denied', { user: user }); ctx.throw(401, 'access_denied', { user: user });
this.throw('access_denied', { user: user }); ctx.throw('access_denied', { user: user });
``` ```
koa uses [http-errors](https://github.com/jshttp/http-errors) to create errors. 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. method.
```js ```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. koa uses [http-assert](https://github.com/jshttp/http-assert) for assertions.
### ctx.respond ### 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. 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: with the `{ signed: true }` option:
```js ```js
this.cookies.set('name', 'tobi', { signed: true }); ctx.cookies.set('name', 'tobi', { signed: true });
``` ```
## app.context ## app.context

View file

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

View file

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

View file

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