docs: update history and docs for v2
This commit is contained in:
parent
daf688bf69
commit
2b094eb895
6 changed files with 140 additions and 95 deletions
29
History.md
29
History.md
|
@ -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
|
||||
==================
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"]
|
||||
```
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue