Skip to content

Commit

Permalink
docs(middleware.md): translated & proofread 'middleware.md' (#784)
Browse files Browse the repository at this point in the history
  • Loading branch information
lslxdx authored and popomore committed May 10, 2017
1 parent e55a134 commit 22c9cd9
Showing 1 changed file with 202 additions and 0 deletions.
202 changes: 202 additions & 0 deletions docs/source/en/basics/middleware.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
title: Middleware
---

In [the previous chapter](../intro/egg-and-koa.md), we say that Egg is based on Koa 1, so the form of middleware in Egg is the same as in Koa 1, i.e. they are both based on [the onion model](../intro/egg-and-koa.md#midlleware) of generator function.

## Writing Middleware

### How to write

We begin to see how to write a middleware from a simple gzip example.

```js
const isJSON = require('koa-is-json');
const zlib = require('zlib');

function* gzip(next) {
yield next;

// convert the response body to gzip after the completion of the execution of subsequent middleware
let body = this.body;
if (!body) return;
if (isJSON(body)) body = JSON.stringify(body);

// set gzip body, correct the response header
const stream = zlib.createGzip();
stream.end(body);
this.body = stream;
this.set('Content-Encoding', 'gzip');
}
```

You might find that the middleware's writing style in the framework is exactly the same as in Koa, so any middleware in Koa can be used directly by the framework.

### Configuration

Usually the middleware has its own configuration. In the framework, a complete middleware is including the configuration process. A middleware is a file in `app/middleware` directory by convention, which needs an exports function that take two parameters:

- options: the configuration field of the middleware, `app.config[${middlewareName}]` will be passed in by the framework
- app: the Application instance of current application

We will do a simple optimization to the gzip middleware above, making it do gzip compression only if the body size is greater than a configured threshold. So, we need to create a new file `gzip.js` in `app/middleware` directory.

```js
const isJSON = require('koa-is-json');
const zlib = require('zlib');

module.exports = options => {
return function* gzip(next) {
yield next;

// convert the response body to gzip after the completion of the execution of subsequent middleware
let body = this.body;
if (!body) return;

// support options.threshold
if (options.threshold && this.length < options.threshold) return;

if (isJSON(body)) body = JSON.stringify(body);

// set gzip body, correct the response header
const stream = zlib.createGzip();
stream.end(body);
this.body = stream;
this.set('Content-Encoding', 'gzip');
};
};
```

## Using Middleware in the Application

We can load customized middleware completely by configuration in the application, and decide their order.
If we need to load the gzip middleware in the above,
we can edit `config.default.js` like this:

```js
module.exports = {
// configure the middleware you need, which loads in the order of array
middleware: [ 'gzip' ],

// configure the gzip middleware
gzip: {
threshold: 1024, // skip response body which size is less than 1K
},
};
```

** About Configuration fields and runtime environment configurations, see [Config](./config.md) chapter. **

## Default Framework Middleware

In addition to the application-level middleware is loaded, the framework itself and other plugins will also load many middleware. All the config fields of these built-in middlewares can be modified by modifying the ones with the same name in the config file, for example [Framework Built-in Plugin](https://github.com/eggjs/egg/tree/master/app/middleware) uses a bodyParser middleware(the framework loader will change the file name separated by delimiters into the camel style), and we can add configs below in `config/config.default.js` to modify the bodyParser:

```js
module.exports = {
bodyParser: {
jsonLimit: '10m',
},
};
```
** Note: middleware loaded by the framework and plugins are loaded earlier than those loaded by the application layer, and the application layer cannot overwrite the default framework middleware. If the application layer loads customized middleware that has the same name with default framework middleware, an error will be raised on starting up. **

## Middleware in Router

Both middleware defined by the application layer and the default framework middleware will be loaded by the loader and are mounted to `app.middlewares`(Note: it's plural here since `app.middleware` is used for other purpose in Koa). So middleware defined by the application layer can be loaded by the router other than the config, therefore they only take effect on the corresponding routes.

Again, let's take the gzip middleware above for an example. In order to use this middleware directly in the router, we can write like this in `app/router.js`:

```js
module.exports = app => {
const gzip = app.middlewares.gzip({ threshold: 1024 });
app.get('/needgzip', gzip, app.controller.handler);
};
```

## Use Koa's Middleware

The framework is compatible with all kinds of middleware of Koa 1.x and 2.x, including:
- generator function: `function* (next) {}`
- async function: `async (ctx, next) => {}`
- common function: `(ctx, next) => {}`

All middleware used by Koa can be directly used by the framework, too.

For example, Koa uses [koa-compress](https://github.com/koajs/compress) in this way:

```js
const koa = require('koa');
const compress = require('koa-compress');

const app = koa();

const options = { threshold: 2048 };
app.use(compress(options));
```

We can load the middleware according to the framework specification like this:

```js
// app/middleware/compress.js

// interfaces(`(options) => middleware`) exposed by koa-compress match the framework middleware requirements
module.exports = require('koa-compress');
```

```js
// config/config.default.js

exports.middleware = [ 'compress' ];
exports.compress = {
threshold: 2048,
};
```
## General Configuration

These general config fields are supported by middleware loaded by the application layer or built in by the framework:

- enable: enable the middleware or not
- match: set some rules with which only the request match can go through middleware
- ignore: set some rules with which the request match can't go through this middleware

### enable

If our application does not need default bodyParser to resolve the request body, we can configure enable to close it.

```js
module.exports = {
bodyParser: {
enable: false,
},
};
```

### match and ignore

match and ignore share the same parameter but do the opposite things. match and ignore cannot be configured in the same time.

If we want gzip to be used only by url requests starting with `/static`, the match config field can be set like this:

```js
module.exports = {
gzip: {
match: '/static',
},
};
```
match and ignore support various types of configuration ways:

1. String: when string, it sets the prefix of a url path, and all urls starting with this prefix will match.
2. Regular expression: when regular expression, all urls satisfy this regular expression will match.
3. Function: when function, the request context will be passed to it and what it returns(true/false) determines whether the request matches or not.

```js
module.exports = {
gzip: {
match(ctx) {
// enabled on ios devices
const reg = /iphone|ipad|ipod/i;
return reg.test(ctx.get('user-agent'));
},
},
};
```

0 comments on commit 22c9cd9

Please sign in to comment.