diff --git a/History.md b/History.md
index 5057dc6..f188cfd 100644
--- a/History.md
+++ b/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
==================
diff --git a/docs/api/context.md b/docs/api/context.md
index 2cac9bf..a92d062 100644
--- a/docs/api/context.md
+++ b/docs/api/context.md
@@ -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.
diff --git a/docs/api/index.md b/docs/api/index.md
index c0a5fc8..b966c5a 100644
--- a/docs/api/index.md
+++ b/docs/api/index.md
@@ -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
diff --git a/docs/api/request.md b/docs/api/request.md
index 9b2ca10..a930bfb 100644
--- a/docs/api/request.md
+++ b/docs/api/request.md
@@ -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"]
```
diff --git a/docs/api/response.md b/docs/api/response.md
index 338ea7e..b50a70f 100644
--- a/docs/api/response.md
+++ b/docs/api/response.md
@@ -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', '');
+ctx.append('Link', '');
```
### response.set(fields)
@@ -185,7 +185,7 @@ this.append('Link', '');
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)
diff --git a/docs/error-handling.md b/docs/error-handling.md
index 32b8b26..6fc579d 100644
--- a/docs/error-handling.md
+++ b/docs/error-handling.md
@@ -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.