@koa/router 10.1.0 → 11.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +2 -3
- package/README.md +54 -24
- package/lib/layer.js +28 -22
- package/lib/router.js +149 -55
- package/package.json +34 -15
- package/history.md +0 -157
package/LICENSE
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
The MIT License (MIT)
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2015 Alexander C. Mingoia
|
|
4
|
-
Copyright (c) 2019-present Nick Baugh and Koajs contributors
|
|
3
|
+
Copyright (c) 2015 Alexander C. Mingoia and @koajs contributors
|
|
5
4
|
|
|
6
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
6
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -19,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
19
18
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
22
|
-
THE SOFTWARE.
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,55 +1,85 @@
|
|
|
1
1
|
# [@koa/router](https://github.com/koajs/router)
|
|
2
2
|
|
|
3
|
-
> Router middleware for [Koa](https://github.com/koajs/koa).
|
|
3
|
+
> Router middleware for [Koa](https://github.com/koajs/koa). Maintained by [Forward Email][forward-email] and [Lad][].
|
|
4
4
|
|
|
5
|
-
[](https://github.com/koajs/router/actions/workflows/ci.yml)
|
|
6
|
+
[](https://github.com/sindresorhus/xo)
|
|
7
|
+
[](https://github.com/prettier/prettier)
|
|
8
|
+
[](https://lass.js.org)
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## Table of Contents
|
|
13
|
+
|
|
14
|
+
* [Features](#features)
|
|
15
|
+
* [Migrating to 7 / Koa 2](#migrating-to-7--koa-2)
|
|
16
|
+
* [Install](#install)
|
|
17
|
+
* [Typescript Support](#typescript-support)
|
|
18
|
+
* [API Reference](#api-reference)
|
|
19
|
+
* [Contributors](#contributors)
|
|
20
|
+
* [License](#license)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
## Features
|
|
10
24
|
|
|
11
25
|
* Express-style routing (`app.get`, `app.put`, `app.post`, etc.)
|
|
12
26
|
* Named URL parameters
|
|
13
27
|
* Named routes with URL generation
|
|
28
|
+
* Match routes with specific host
|
|
14
29
|
* Responds to `OPTIONS` requests with allowed methods
|
|
15
30
|
* Support for `405 Method Not Allowed` and `501 Not Implemented`
|
|
16
31
|
* Multiple route middleware
|
|
17
32
|
* Multiple and nestable routers
|
|
18
33
|
* `async/await` support
|
|
19
34
|
|
|
35
|
+
|
|
20
36
|
## Migrating to 7 / Koa 2
|
|
21
37
|
|
|
22
|
-
|
|
38
|
+
* The API has changed to match the new promise-based middleware
|
|
23
39
|
signature of koa 2. See the [koa 2.x readme](https://github.com/koajs/koa/tree/2.0.0-alpha.3) for more
|
|
24
40
|
information.
|
|
25
|
-
|
|
41
|
+
* Middleware is now always run in the order declared by `.use()` (or `.get()`,
|
|
26
42
|
etc.), which matches Express 4 API.
|
|
27
43
|
|
|
28
|
-
## Installation
|
|
29
44
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
npm
|
|
33
|
-
|
|
34
|
-
|
|
45
|
+
## Install
|
|
46
|
+
|
|
47
|
+
[npm][]:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
npm install @koa/router
|
|
35
51
|
```
|
|
36
52
|
|
|
37
|
-
## [API Reference](./API.md)
|
|
38
53
|
|
|
39
|
-
##
|
|
54
|
+
## Typescript Support
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
npm install @types/koa__router
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
## API Reference
|
|
62
|
+
|
|
63
|
+
See [API Reference](./API.md) for more documentation.
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
## Contributors
|
|
67
|
+
|
|
68
|
+
| Name |
|
|
69
|
+
| ---------------- |
|
|
70
|
+
| **Alex Mingoia** |
|
|
71
|
+
| **@koajs** |
|
|
40
72
|
|
|
41
|
-
Please submit all issues and pull requests to the [koajs/router](http://github.com/koajs/router) repository!
|
|
42
73
|
|
|
43
|
-
##
|
|
74
|
+
## License
|
|
44
75
|
|
|
45
|
-
|
|
76
|
+
[MIT](LICENSE) © Alex Mingoia
|
|
46
77
|
|
|
47
|
-
## Call for Maintainers
|
|
48
78
|
|
|
49
|
-
|
|
79
|
+
##
|
|
50
80
|
|
|
51
|
-
|
|
81
|
+
[forward-email]: https://forwardemail.net
|
|
52
82
|
|
|
53
|
-
|
|
83
|
+
[lad]: https://lad.js.org
|
|
54
84
|
|
|
55
|
-
[
|
|
85
|
+
[npm]: https//www.npmjs.com
|
package/lib/layer.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const { pathToRegexp, compile, parse } = require('path-to-regexp');
|
|
2
1
|
const { parse: parseUrl, format: formatUrl } = require('url');
|
|
2
|
+
const { pathToRegexp, compile, parse } = require('path-to-regexp');
|
|
3
3
|
|
|
4
4
|
module.exports = Layer;
|
|
5
5
|
|
|
@@ -17,31 +17,33 @@ module.exports = Layer;
|
|
|
17
17
|
* @private
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
function Layer(path, methods, middleware, opts) {
|
|
21
|
-
this.opts = opts
|
|
20
|
+
function Layer(path, methods, middleware, opts = {}) {
|
|
21
|
+
this.opts = opts;
|
|
22
22
|
this.name = this.opts.name || null;
|
|
23
23
|
this.methods = [];
|
|
24
24
|
this.paramNames = [];
|
|
25
25
|
this.stack = Array.isArray(middleware) ? middleware : [middleware];
|
|
26
26
|
|
|
27
|
-
for (
|
|
28
|
-
const l = this.methods.push(
|
|
27
|
+
for (const method of methods) {
|
|
28
|
+
const l = this.methods.push(method.toUpperCase());
|
|
29
29
|
if (this.methods[l - 1] === 'GET') this.methods.unshift('HEAD');
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
// ensure middleware is a function
|
|
33
33
|
for (let i = 0; i < this.stack.length; i++) {
|
|
34
34
|
const fn = this.stack[i];
|
|
35
|
-
const type =
|
|
35
|
+
const type = typeof fn;
|
|
36
36
|
if (type !== 'function')
|
|
37
37
|
throw new Error(
|
|
38
|
-
`${methods.toString()} \`${
|
|
38
|
+
`${methods.toString()} \`${
|
|
39
|
+
this.opts.name || path
|
|
40
|
+
}\`: \`middleware\` must be a function, not \`${type}\``
|
|
39
41
|
);
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
this.path = path;
|
|
43
45
|
this.regexp = pathToRegexp(path, this.paramNames, this.opts);
|
|
44
|
-
}
|
|
46
|
+
}
|
|
45
47
|
|
|
46
48
|
/**
|
|
47
49
|
* Returns whether request `path` matches route.
|
|
@@ -60,18 +62,17 @@ Layer.prototype.match = function (path) {
|
|
|
60
62
|
*
|
|
61
63
|
* @param {String} path
|
|
62
64
|
* @param {Array.<String>} captures
|
|
63
|
-
* @param {Object=}
|
|
65
|
+
* @param {Object=} params
|
|
64
66
|
* @returns {Object}
|
|
65
67
|
* @private
|
|
66
68
|
*/
|
|
67
69
|
|
|
68
|
-
Layer.prototype.params = function (path, captures,
|
|
69
|
-
const params = existingParams || {};
|
|
70
|
-
|
|
70
|
+
Layer.prototype.params = function (path, captures, params = {}) {
|
|
71
71
|
for (let len = captures.length, i = 0; i < len; i++) {
|
|
72
72
|
if (this.paramNames[i]) {
|
|
73
73
|
const c = captures[i];
|
|
74
|
-
if (c && c.length
|
|
74
|
+
if (c && c.length > 0)
|
|
75
|
+
params[this.paramNames[i].name] = c ? safeDecodeURIComponent(c) : c;
|
|
75
76
|
}
|
|
76
77
|
}
|
|
77
78
|
|
|
@@ -110,11 +111,11 @@ Layer.prototype.url = function (params, options) {
|
|
|
110
111
|
let args = params;
|
|
111
112
|
const url = this.path.replace(/\(\.\*\)/g, '');
|
|
112
113
|
|
|
113
|
-
if (typeof params
|
|
114
|
+
if (typeof params !== 'object') {
|
|
114
115
|
args = Array.prototype.slice.call(arguments);
|
|
115
|
-
if (typeof args[args.length - 1]
|
|
116
|
+
if (typeof args[args.length - 1] === 'object') {
|
|
116
117
|
options = args[args.length - 1];
|
|
117
|
-
args = args.slice(0,
|
|
118
|
+
args = args.slice(0, -1);
|
|
118
119
|
}
|
|
119
120
|
}
|
|
120
121
|
|
|
@@ -124,11 +125,11 @@ Layer.prototype.url = function (params, options) {
|
|
|
124
125
|
const tokens = parse(url);
|
|
125
126
|
let replace = {};
|
|
126
127
|
|
|
127
|
-
if (args
|
|
128
|
+
if (Array.isArray(args)) {
|
|
128
129
|
for (let len = tokens.length, i = 0, j = 0; i < len; i++) {
|
|
129
130
|
if (tokens[i].name) replace[tokens[i].name] = args[j++];
|
|
130
131
|
}
|
|
131
|
-
} else if (tokens.some(token => token.name)) {
|
|
132
|
+
} else if (tokens.some((token) => token.name)) {
|
|
132
133
|
replace = params;
|
|
133
134
|
} else if (!options) {
|
|
134
135
|
options = params;
|
|
@@ -144,6 +145,7 @@ Layer.prototype.url = function (params, options) {
|
|
|
144
145
|
replaced.search = undefined;
|
|
145
146
|
replaced.query = options.query;
|
|
146
147
|
}
|
|
148
|
+
|
|
147
149
|
return formatUrl(replaced);
|
|
148
150
|
}
|
|
149
151
|
|
|
@@ -159,7 +161,7 @@ Layer.prototype.url = function (params, options) {
|
|
|
159
161
|
* router
|
|
160
162
|
* .param('user', function (id, ctx, next) {
|
|
161
163
|
* ctx.user = users[id];
|
|
162
|
-
* if (!user) return ctx.status = 404;
|
|
164
|
+
* if (!ctx.user) return ctx.status = 404;
|
|
163
165
|
* next();
|
|
164
166
|
* })
|
|
165
167
|
* .get('/users/:user', function (ctx, next) {
|
|
@@ -174,11 +176,12 @@ Layer.prototype.url = function (params, options) {
|
|
|
174
176
|
*/
|
|
175
177
|
|
|
176
178
|
Layer.prototype.param = function (param, fn) {
|
|
177
|
-
const stack = this
|
|
179
|
+
const { stack } = this;
|
|
178
180
|
const params = this.paramNames;
|
|
179
181
|
const middleware = function (ctx, next) {
|
|
180
182
|
return fn.call(this, ctx.params[param], ctx, next);
|
|
181
183
|
};
|
|
184
|
+
|
|
182
185
|
middleware.param = param;
|
|
183
186
|
|
|
184
187
|
const names = params.map(function (p) {
|
|
@@ -212,7 +215,10 @@ Layer.prototype.param = function (param, fn) {
|
|
|
212
215
|
|
|
213
216
|
Layer.prototype.setPrefix = function (prefix) {
|
|
214
217
|
if (this.path) {
|
|
215
|
-
this.path =
|
|
218
|
+
this.path =
|
|
219
|
+
this.path !== '/' || this.opts.strict === true
|
|
220
|
+
? `${prefix}${this.path}`
|
|
221
|
+
: prefix;
|
|
216
222
|
this.paramNames = [];
|
|
217
223
|
this.regexp = pathToRegexp(this.path, this.paramNames, this.opts);
|
|
218
224
|
}
|
|
@@ -232,7 +238,7 @@ Layer.prototype.setPrefix = function (prefix) {
|
|
|
232
238
|
function safeDecodeURIComponent(text) {
|
|
233
239
|
try {
|
|
234
240
|
return decodeURIComponent(text);
|
|
235
|
-
} catch
|
|
241
|
+
} catch {
|
|
236
242
|
return text;
|
|
237
243
|
}
|
|
238
244
|
}
|
package/lib/router.js
CHANGED
|
@@ -5,12 +5,15 @@
|
|
|
5
5
|
* @link https://github.com/alexmingoia/koa-router
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
const
|
|
8
|
+
const { debuglog } = require('util');
|
|
9
|
+
|
|
9
10
|
const compose = require('koa-compose');
|
|
10
11
|
const HttpError = require('http-errors');
|
|
11
12
|
const methods = require('methods');
|
|
12
|
-
const Layer = require('./layer');
|
|
13
13
|
const { pathToRegexp } = require('path-to-regexp');
|
|
14
|
+
const Layer = require('./layer');
|
|
15
|
+
|
|
16
|
+
const debug = debuglog('koa-router');
|
|
14
17
|
|
|
15
18
|
/**
|
|
16
19
|
* @module koa-router
|
|
@@ -43,14 +46,16 @@ module.exports = Router;
|
|
|
43
46
|
*
|
|
44
47
|
* @alias module:koa-router
|
|
45
48
|
* @param {Object=} opts
|
|
49
|
+
* @param {Boolean=false} opts.exclusive only run last matched route's controller when there are multiple matches
|
|
46
50
|
* @param {String=} opts.prefix prefix router paths
|
|
51
|
+
* @param {String|RegExp=} opts.host host for router match
|
|
47
52
|
* @constructor
|
|
48
53
|
*/
|
|
49
54
|
|
|
50
|
-
function Router(opts) {
|
|
55
|
+
function Router(opts = {}) {
|
|
51
56
|
if (!(this instanceof Router)) return new Router(opts);
|
|
52
57
|
|
|
53
|
-
this.opts = opts
|
|
58
|
+
this.opts = opts;
|
|
54
59
|
this.methods = this.opts.methods || [
|
|
55
60
|
'HEAD',
|
|
56
61
|
'OPTIONS',
|
|
@@ -60,10 +65,12 @@ function Router(opts) {
|
|
|
60
65
|
'POST',
|
|
61
66
|
'DELETE'
|
|
62
67
|
];
|
|
68
|
+
this.exclusive = Boolean(this.opts.exclusive);
|
|
63
69
|
|
|
64
70
|
this.params = {};
|
|
65
71
|
this.stack = [];
|
|
66
|
-
|
|
72
|
+
this.host = this.opts.host;
|
|
73
|
+
}
|
|
67
74
|
|
|
68
75
|
/**
|
|
69
76
|
* Create `router.verb()` methods, where *verb* is one of the HTTP verbs such
|
|
@@ -72,7 +79,7 @@ function Router(opts) {
|
|
|
72
79
|
* Match URL patterns to callback functions or controller actions using `router.verb()`,
|
|
73
80
|
* where **verb** is one of the HTTP verbs such as `router.get()` or `router.post()`.
|
|
74
81
|
*
|
|
75
|
-
*
|
|
82
|
+
* Additionally, `router.all()` can be used to match against all methods.
|
|
76
83
|
*
|
|
77
84
|
* ```javascript
|
|
78
85
|
* router
|
|
@@ -178,6 +185,24 @@ function Router(opts) {
|
|
|
178
185
|
* The [path-to-regexp](https://github.com/pillarjs/path-to-regexp) module is
|
|
179
186
|
* used to convert paths to regular expressions.
|
|
180
187
|
*
|
|
188
|
+
*
|
|
189
|
+
* ### Match host for each router instance
|
|
190
|
+
*
|
|
191
|
+
* ```javascript
|
|
192
|
+
* const router = new Router({
|
|
193
|
+
* host: 'example.domain' // only match if request host exactly equal `example.domain`
|
|
194
|
+
* });
|
|
195
|
+
*
|
|
196
|
+
* ```
|
|
197
|
+
*
|
|
198
|
+
* OR host cloud be a regexp
|
|
199
|
+
*
|
|
200
|
+
* ```javascript
|
|
201
|
+
* const router = new Router({
|
|
202
|
+
* host: /.*\.?example\.domain$/ // all host end with .example.domain would be matched
|
|
203
|
+
* });
|
|
204
|
+
* ```
|
|
205
|
+
*
|
|
181
206
|
* @name get|put|post|patch|delete|del
|
|
182
207
|
* @memberof module:koa-router.prototype
|
|
183
208
|
* @param {String} path
|
|
@@ -186,10 +211,10 @@ function Router(opts) {
|
|
|
186
211
|
* @returns {Router}
|
|
187
212
|
*/
|
|
188
213
|
|
|
189
|
-
for (
|
|
214
|
+
for (const method_ of methods) {
|
|
190
215
|
function setMethodVerb(method) {
|
|
191
|
-
Router.prototype[method] = function(name, path, middleware) {
|
|
192
|
-
if (typeof path ===
|
|
216
|
+
Router.prototype[method] = function (name, path, middleware) {
|
|
217
|
+
if (typeof path === 'string' || path instanceof RegExp) {
|
|
193
218
|
middleware = Array.prototype.slice.call(arguments, 2);
|
|
194
219
|
} else {
|
|
195
220
|
middleware = Array.prototype.slice.call(arguments, 1);
|
|
@@ -197,17 +222,27 @@ for (let i = 0; i < methods.length; i++) {
|
|
|
197
222
|
name = null;
|
|
198
223
|
}
|
|
199
224
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
225
|
+
// Sanity check to ensure we have a viable path candidate (eg: string|regex|non-empty array)
|
|
226
|
+
if (
|
|
227
|
+
typeof path !== 'string' &&
|
|
228
|
+
!(path instanceof RegExp) &&
|
|
229
|
+
(!Array.isArray(path) || path.length === 0)
|
|
230
|
+
)
|
|
231
|
+
throw new Error(
|
|
232
|
+
`You have to provide a path when adding a ${method} handler`
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
this.register(path, [method], middleware, { name });
|
|
203
236
|
|
|
204
237
|
return this;
|
|
205
238
|
};
|
|
206
239
|
}
|
|
207
|
-
|
|
240
|
+
|
|
241
|
+
setMethodVerb(method_);
|
|
208
242
|
}
|
|
209
243
|
|
|
210
244
|
// Alias for `router.delete()` because delete is a reserved word
|
|
245
|
+
// eslint-disable-next-line dot-notation
|
|
211
246
|
Router.prototype.del = Router.prototype['delete'];
|
|
212
247
|
|
|
213
248
|
/**
|
|
@@ -247,9 +282,8 @@ Router.prototype.use = function () {
|
|
|
247
282
|
|
|
248
283
|
// support array of paths
|
|
249
284
|
if (Array.isArray(middleware[0]) && typeof middleware[0][0] === 'string') {
|
|
250
|
-
|
|
251
|
-
for (
|
|
252
|
-
const p = arrPaths[i];
|
|
285
|
+
const arrPaths = middleware[0];
|
|
286
|
+
for (const p of arrPaths) {
|
|
253
287
|
router.use.apply(router, [p].concat(middleware.slice(1)));
|
|
254
288
|
}
|
|
255
289
|
|
|
@@ -259,12 +293,15 @@ Router.prototype.use = function () {
|
|
|
259
293
|
const hasPath = typeof middleware[0] === 'string';
|
|
260
294
|
if (hasPath) path = middleware.shift();
|
|
261
295
|
|
|
262
|
-
for (
|
|
263
|
-
const m = middleware[i];
|
|
296
|
+
for (const m of middleware) {
|
|
264
297
|
if (m.router) {
|
|
265
|
-
const cloneRouter = Object.assign(
|
|
266
|
-
|
|
267
|
-
|
|
298
|
+
const cloneRouter = Object.assign(
|
|
299
|
+
Object.create(Router.prototype),
|
|
300
|
+
m.router,
|
|
301
|
+
{
|
|
302
|
+
stack: [...m.router.stack]
|
|
303
|
+
}
|
|
304
|
+
);
|
|
268
305
|
|
|
269
306
|
for (let j = 0; j < cloneRouter.stack.length; j++) {
|
|
270
307
|
const nestedLayer = cloneRouter.stack[j];
|
|
@@ -282,18 +319,21 @@ Router.prototype.use = function () {
|
|
|
282
319
|
if (router.params) {
|
|
283
320
|
function setRouterParams(paramArr) {
|
|
284
321
|
const routerParams = paramArr;
|
|
285
|
-
for (
|
|
286
|
-
const key = routerParams[j];
|
|
322
|
+
for (const key of routerParams) {
|
|
287
323
|
cloneRouter.param(key, router.params[key]);
|
|
288
324
|
}
|
|
289
325
|
}
|
|
326
|
+
|
|
290
327
|
setRouterParams(Object.keys(router.params));
|
|
291
328
|
}
|
|
292
329
|
} else {
|
|
293
330
|
const keys = [];
|
|
294
331
|
pathToRegexp(router.opts.prefix || '', keys);
|
|
295
332
|
const routerPrefixHasParam = router.opts.prefix && keys.length;
|
|
296
|
-
router.register(path || '([
|
|
333
|
+
router.register(path || '([^/]*)', [], m, {
|
|
334
|
+
end: false,
|
|
335
|
+
ignoreCaptures: !hasPath && !routerPrefixHasParam
|
|
336
|
+
});
|
|
297
337
|
}
|
|
298
338
|
}
|
|
299
339
|
|
|
@@ -335,9 +375,15 @@ Router.prototype.prefix = function (prefix) {
|
|
|
335
375
|
Router.prototype.routes = Router.prototype.middleware = function () {
|
|
336
376
|
const router = this;
|
|
337
377
|
|
|
338
|
-
|
|
378
|
+
const dispatch = function dispatch(ctx, next) {
|
|
339
379
|
debug('%s %s', ctx.method, ctx.path);
|
|
340
380
|
|
|
381
|
+
const hostMatched = router.matchHost(ctx.host);
|
|
382
|
+
|
|
383
|
+
if (!hostMatched) {
|
|
384
|
+
return next();
|
|
385
|
+
}
|
|
386
|
+
|
|
341
387
|
const path = router.opts.routerPath || ctx.routerPath || ctx.path;
|
|
342
388
|
const matched = router.match(path, ctx.method);
|
|
343
389
|
let layerChain;
|
|
@@ -352,23 +398,30 @@ Router.prototype.routes = Router.prototype.middleware = function () {
|
|
|
352
398
|
|
|
353
399
|
if (!matched.route) return next();
|
|
354
400
|
|
|
355
|
-
const matchedLayers = matched.pathAndMethod
|
|
356
|
-
const mostSpecificLayer = matchedLayers[matchedLayers.length - 1]
|
|
401
|
+
const matchedLayers = matched.pathAndMethod;
|
|
402
|
+
const mostSpecificLayer = matchedLayers[matchedLayers.length - 1];
|
|
357
403
|
ctx._matchedRoute = mostSpecificLayer.path;
|
|
358
404
|
if (mostSpecificLayer.name) {
|
|
359
405
|
ctx._matchedRouteName = mostSpecificLayer.name;
|
|
360
406
|
}
|
|
361
407
|
|
|
362
|
-
layerChain =
|
|
363
|
-
|
|
408
|
+
layerChain = (
|
|
409
|
+
router.exclusive ? [mostSpecificLayer] : matchedLayers
|
|
410
|
+
).reduce(function (memo, layer) {
|
|
411
|
+
memo.push(function (ctx, next) {
|
|
364
412
|
ctx.captures = layer.captures(path, ctx.captures);
|
|
365
|
-
ctx.params = ctx.request.params = layer.params(
|
|
413
|
+
ctx.params = ctx.request.params = layer.params(
|
|
414
|
+
path,
|
|
415
|
+
ctx.captures,
|
|
416
|
+
ctx.params
|
|
417
|
+
);
|
|
366
418
|
ctx.routerPath = layer.path;
|
|
367
419
|
ctx.routerName = layer.name;
|
|
368
420
|
ctx._matchedRoute = layer.path;
|
|
369
421
|
if (layer.name) {
|
|
370
422
|
ctx._matchedRouteName = layer.name;
|
|
371
423
|
}
|
|
424
|
+
|
|
372
425
|
return next();
|
|
373
426
|
});
|
|
374
427
|
return memo.concat(layer.stack);
|
|
@@ -425,12 +478,11 @@ Router.prototype.routes = Router.prototype.middleware = function () {
|
|
|
425
478
|
* @returns {Function}
|
|
426
479
|
*/
|
|
427
480
|
|
|
428
|
-
Router.prototype.allowedMethods = function (options) {
|
|
429
|
-
options = options || {};
|
|
481
|
+
Router.prototype.allowedMethods = function (options = {}) {
|
|
430
482
|
const implemented = this.methods;
|
|
431
483
|
|
|
432
484
|
return function allowedMethods(ctx, next) {
|
|
433
|
-
return next().then(function() {
|
|
485
|
+
return next().then(function () {
|
|
434
486
|
const allowed = {};
|
|
435
487
|
|
|
436
488
|
if (!ctx.status || ctx.status === 404) {
|
|
@@ -446,25 +498,27 @@ Router.prototype.allowedMethods = function (options) {
|
|
|
446
498
|
|
|
447
499
|
if (!~implemented.indexOf(ctx.method)) {
|
|
448
500
|
if (options.throw) {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
501
|
+
const notImplementedThrowable =
|
|
502
|
+
typeof options.notImplemented === 'function'
|
|
503
|
+
? options.notImplemented() // set whatever the user returns from their function
|
|
504
|
+
: new HttpError.NotImplemented();
|
|
452
505
|
|
|
453
506
|
throw notImplementedThrowable;
|
|
454
507
|
} else {
|
|
455
508
|
ctx.status = 501;
|
|
456
509
|
ctx.set('Allow', allowedArr.join(', '));
|
|
457
510
|
}
|
|
458
|
-
} else if (allowedArr.length) {
|
|
511
|
+
} else if (allowedArr.length > 0) {
|
|
459
512
|
if (ctx.method === 'OPTIONS') {
|
|
460
513
|
ctx.status = 200;
|
|
461
514
|
ctx.body = '';
|
|
462
515
|
ctx.set('Allow', allowedArr.join(', '));
|
|
463
516
|
} else if (!allowed[ctx.method]) {
|
|
464
517
|
if (options.throw) {
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
518
|
+
const notAllowedThrowable =
|
|
519
|
+
typeof options.methodNotAllowed === 'function'
|
|
520
|
+
? options.methodNotAllowed() // set whatever the user returns from their function
|
|
521
|
+
: new HttpError.MethodNotAllowed();
|
|
468
522
|
|
|
469
523
|
throw notAllowedThrowable;
|
|
470
524
|
} else {
|
|
@@ -486,7 +540,6 @@ Router.prototype.allowedMethods = function (options) {
|
|
|
486
540
|
* @param {Function=} middleware You may also pass multiple middleware.
|
|
487
541
|
* @param {Function} callback
|
|
488
542
|
* @returns {Router}
|
|
489
|
-
* @private
|
|
490
543
|
*/
|
|
491
544
|
|
|
492
545
|
Router.prototype.all = function (name, path, middleware) {
|
|
@@ -498,6 +551,14 @@ Router.prototype.all = function (name, path, middleware) {
|
|
|
498
551
|
name = null;
|
|
499
552
|
}
|
|
500
553
|
|
|
554
|
+
// Sanity check to ensure we have a viable path candidate (eg: string|regex|non-empty array)
|
|
555
|
+
if (
|
|
556
|
+
typeof path !== 'string' &&
|
|
557
|
+
!(path instanceof RegExp) &&
|
|
558
|
+
(!Array.isArray(path) || path.length === 0)
|
|
559
|
+
)
|
|
560
|
+
throw new Error('You have to provide a path when adding an all handler');
|
|
561
|
+
|
|
501
562
|
this.register(path, methods, middleware, { name });
|
|
502
563
|
|
|
503
564
|
return this;
|
|
@@ -529,12 +590,21 @@ Router.prototype.all = function (name, path, middleware) {
|
|
|
529
590
|
|
|
530
591
|
Router.prototype.redirect = function (source, destination, code) {
|
|
531
592
|
// lookup source route by name
|
|
532
|
-
if (source[0] !== '/')
|
|
593
|
+
if (typeof source === 'symbol' || source[0] !== '/') {
|
|
594
|
+
source = this.url(source);
|
|
595
|
+
if (source instanceof Error) throw source;
|
|
596
|
+
}
|
|
533
597
|
|
|
534
598
|
// lookup destination route by name
|
|
535
|
-
if (
|
|
599
|
+
if (
|
|
600
|
+
typeof destination === 'symbol' ||
|
|
601
|
+
(destination[0] !== '/' && !destination.includes('://'))
|
|
602
|
+
) {
|
|
603
|
+
destination = this.url(destination);
|
|
604
|
+
if (destination instanceof Error) throw destination;
|
|
605
|
+
}
|
|
536
606
|
|
|
537
|
-
return this.all(source, ctx => {
|
|
607
|
+
return this.all(source, (ctx) => {
|
|
538
608
|
ctx.redirect(destination);
|
|
539
609
|
ctx.status = code || 301;
|
|
540
610
|
});
|
|
@@ -550,16 +620,13 @@ Router.prototype.redirect = function (source, destination, code) {
|
|
|
550
620
|
* @private
|
|
551
621
|
*/
|
|
552
622
|
|
|
553
|
-
Router.prototype.register = function (path, methods, middleware, opts) {
|
|
554
|
-
opts = opts || {};
|
|
555
|
-
|
|
623
|
+
Router.prototype.register = function (path, methods, middleware, opts = {}) {
|
|
556
624
|
const router = this;
|
|
557
|
-
const stack = this
|
|
625
|
+
const { stack } = this;
|
|
558
626
|
|
|
559
627
|
// support array of paths
|
|
560
628
|
if (Array.isArray(path)) {
|
|
561
|
-
for (
|
|
562
|
-
const curPath = path[i];
|
|
629
|
+
for (const curPath of path) {
|
|
563
630
|
router.register.call(router, curPath, methods, middleware, opts);
|
|
564
631
|
}
|
|
565
632
|
|
|
@@ -572,7 +639,7 @@ Router.prototype.register = function (path, methods, middleware, opts) {
|
|
|
572
639
|
name: opts.name,
|
|
573
640
|
sensitive: opts.sensitive || this.opts.sensitive || false,
|
|
574
641
|
strict: opts.strict || this.opts.strict || false,
|
|
575
|
-
prefix: opts.prefix || this.opts.prefix ||
|
|
642
|
+
prefix: opts.prefix || this.opts.prefix || '',
|
|
576
643
|
ignoreCaptures: opts.ignoreCaptures
|
|
577
644
|
});
|
|
578
645
|
|
|
@@ -603,7 +670,7 @@ Router.prototype.register = function (path, methods, middleware, opts) {
|
|
|
603
670
|
Router.prototype.route = function (name) {
|
|
604
671
|
const routes = this.stack;
|
|
605
672
|
|
|
606
|
-
for (let len = routes.length, i=0; i<len; i++) {
|
|
673
|
+
for (let len = routes.length, i = 0; i < len; i++) {
|
|
607
674
|
if (routes[i].name && routes[i].name === name) return routes[i];
|
|
608
675
|
}
|
|
609
676
|
|
|
@@ -653,7 +720,7 @@ Router.prototype.url = function (name, params) {
|
|
|
653
720
|
return route.url.apply(route, args);
|
|
654
721
|
}
|
|
655
722
|
|
|
656
|
-
return new Error(`No route found for name: ${name}`);
|
|
723
|
+
return new Error(`No route found for name: ${String(name)}`);
|
|
657
724
|
};
|
|
658
725
|
|
|
659
726
|
/**
|
|
@@ -680,12 +747,13 @@ Router.prototype.match = function (path, method) {
|
|
|
680
747
|
|
|
681
748
|
debug('test %s %s', layer.path, layer.regexp);
|
|
682
749
|
|
|
750
|
+
// eslint-disable-next-line unicorn/prefer-regexp-test
|
|
683
751
|
if (layer.match(path)) {
|
|
684
752
|
matched.path.push(layer);
|
|
685
753
|
|
|
686
754
|
if (layer.methods.length === 0 || ~layer.methods.indexOf(method)) {
|
|
687
755
|
matched.pathAndMethod.push(layer);
|
|
688
|
-
if (layer.methods.length) matched.route = true;
|
|
756
|
+
if (layer.methods.length > 0) matched.route = true;
|
|
689
757
|
}
|
|
690
758
|
}
|
|
691
759
|
}
|
|
@@ -693,6 +761,32 @@ Router.prototype.match = function (path, method) {
|
|
|
693
761
|
return matched;
|
|
694
762
|
};
|
|
695
763
|
|
|
764
|
+
/**
|
|
765
|
+
* Match given `input` to allowed host
|
|
766
|
+
* @param {String} input
|
|
767
|
+
* @returns {boolean}
|
|
768
|
+
*/
|
|
769
|
+
|
|
770
|
+
Router.prototype.matchHost = function (input) {
|
|
771
|
+
const { host } = this;
|
|
772
|
+
|
|
773
|
+
if (!host) {
|
|
774
|
+
return true;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
if (!input) {
|
|
778
|
+
return false;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
if (typeof host === 'string') {
|
|
782
|
+
return input === host;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
if (typeof host === 'object' && host instanceof RegExp) {
|
|
786
|
+
return host.test(input);
|
|
787
|
+
}
|
|
788
|
+
};
|
|
789
|
+
|
|
696
790
|
/**
|
|
697
791
|
* Run middleware for named route parameters. Useful for auto-loading or
|
|
698
792
|
* validation.
|
|
@@ -723,7 +817,7 @@ Router.prototype.match = function (path, method) {
|
|
|
723
817
|
* @returns {Router}
|
|
724
818
|
*/
|
|
725
819
|
|
|
726
|
-
Router.prototype.param = function(param, middleware) {
|
|
820
|
+
Router.prototype.param = function (param, middleware) {
|
|
727
821
|
this.params[param] = middleware;
|
|
728
822
|
for (let i = 0; i < this.stack.length; i++) {
|
|
729
823
|
const route = this.stack[i];
|
package/package.json
CHANGED
|
@@ -1,32 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@koa/router",
|
|
3
|
-
"description": "Router middleware for koa.
|
|
4
|
-
"version": "
|
|
3
|
+
"description": "Router middleware for koa. Maintained by Forward Email and Lad.",
|
|
4
|
+
"version": "11.0.1",
|
|
5
5
|
"author": "Alex Mingoia <talk@alexmingoia.com>",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/koajs/router/issues",
|
|
8
8
|
"email": "niftylettuce@gmail.com"
|
|
9
9
|
},
|
|
10
|
+
"contributors": [
|
|
11
|
+
"Alex Mingoia <talk@alexmingoia.com>",
|
|
12
|
+
"@koajs"
|
|
13
|
+
],
|
|
10
14
|
"dependencies": {
|
|
11
|
-
"
|
|
12
|
-
"http-errors": "^1.7.3",
|
|
15
|
+
"http-errors": "^2.0.0",
|
|
13
16
|
"koa-compose": "^4.1.0",
|
|
14
17
|
"methods": "^1.1.2",
|
|
15
|
-
"path-to-regexp": "^6.1
|
|
18
|
+
"path-to-regexp": "^6.2.1"
|
|
16
19
|
},
|
|
17
20
|
"devDependencies": {
|
|
18
|
-
"@
|
|
21
|
+
"@commitlint/cli": "^17.0.3",
|
|
22
|
+
"@commitlint/config-conventional": "^17.0.3",
|
|
23
|
+
"@ladjs/env": "^3.0.0",
|
|
24
|
+
"ava": "^4.3.0",
|
|
25
|
+
"cross-env": "^7.0.3",
|
|
26
|
+
"eslint": "^8.19.0",
|
|
27
|
+
"eslint-config-xo-lass": "^2.0.1",
|
|
19
28
|
"expect.js": "^0.3.1",
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
29
|
+
"fixpack": "^4.0.0",
|
|
30
|
+
"husky": "^8.0.1",
|
|
31
|
+
"jsdoc-to-markdown": "^7.1.1",
|
|
32
|
+
"koa": "^2.13.4",
|
|
33
|
+
"lint-staged": "^13.0.3",
|
|
34
|
+
"mocha": "^10.0.0",
|
|
35
|
+
"nyc": "^15.1.0",
|
|
36
|
+
"remark-cli": "^11.0.0",
|
|
37
|
+
"remark-preset-github": "^4.0.4",
|
|
24
38
|
"should": "^13.2.3",
|
|
25
|
-
"supertest": "^
|
|
26
|
-
"wrk": "^1.2.
|
|
39
|
+
"supertest": "^6.2.4",
|
|
40
|
+
"wrk": "^1.2.1",
|
|
41
|
+
"xo": "^0.50.0"
|
|
27
42
|
},
|
|
28
43
|
"engines": {
|
|
29
|
-
"node": ">=
|
|
44
|
+
"node": ">= 12"
|
|
30
45
|
},
|
|
31
46
|
"files": [
|
|
32
47
|
"lib"
|
|
@@ -45,9 +60,13 @@
|
|
|
45
60
|
"url": "https://github.com/koajs/router.git"
|
|
46
61
|
},
|
|
47
62
|
"scripts": {
|
|
63
|
+
"bench": "make -C bench",
|
|
64
|
+
"coverage": "nyc npm run test",
|
|
48
65
|
"docs": "NODE_ENV=test jsdoc2md -t ./lib/API_tpl.hbs --src ./lib/*.js >| API.md",
|
|
66
|
+
"lint": "xo --fix && remark . -qfo && fixpack",
|
|
67
|
+
"prepare": "husky install",
|
|
68
|
+
"pretest": "npm run lint",
|
|
49
69
|
"test": "mocha test/**/*.js",
|
|
50
|
-
"
|
|
51
|
-
"bench": "make -C bench"
|
|
70
|
+
"test:watch": "mocha test/**/*.js --watch"
|
|
52
71
|
}
|
|
53
72
|
}
|
package/history.md
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
9.0.0 / 2020-04-09
|
|
2
|
-
==================
|
|
3
|
-
|
|
4
|
-
- Update `path-to-regexp`. Migration path: change usage of `'*'` in routes to `(.*)` or `:splat*`.
|
|
5
|
-
- Example: `router.get('*', ....)` becomes `router.get('(.*)') ....)`
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
8.0.0 / 2019-06-16
|
|
9
|
-
==================
|
|
10
|
-
|
|
11
|
-
**others**
|
|
12
|
-
* [[`b5dd5e8`](http://github.com/koajs/koa-router/commit/b5dd5e8f00e841b7061a62ab6228cbe96a999470)] - chore: rename to @koa/router (dead-horse <<dead_horse@qq.com>>)
|
|
13
|
-
|
|
14
|
-
-------------
|
|
15
|
-
|
|
16
|
-
# Changelogs inherit from koa-router.
|
|
17
|
-
|
|
18
|
-
## 7.4.0
|
|
19
|
-
|
|
20
|
-
- Fix router.url() for multiple nested routers [#407](https://github.com/alexmingoia/koa-router/pull/407)
|
|
21
|
-
- `layer.name` added to `ctx` at `ctx.routerName` during routing [#412](https://github.com/alexmingoia/koa-router/pull/412)
|
|
22
|
-
- Router.use() was erroneously settings `(.*)` as a prefix to all routers nested with .use that did not pass an explicit prefix string as the first argument. This resulted in routes being matched that should not have been, included the running of multiple route handlers in error. [#369](https://github.com/alexmingoia/koa-router/issues/369) and [#410](https://github.com/alexmingoia/koa-router/issues/410) include information on this issue.
|
|
23
|
-
|
|
24
|
-
## 7.3.0
|
|
25
|
-
|
|
26
|
-
- Router#url() now accepts query parameters to add to generated urls [#396](https://github.com/alexmingoia/koa-router/pull/396)
|
|
27
|
-
|
|
28
|
-
## 7.2.1
|
|
29
|
-
|
|
30
|
-
- Respond to CORS preflights with 200, 0 length body [#359](https://github.com/alexmingoia/koa-router/issues/359)
|
|
31
|
-
|
|
32
|
-
## 7.2.0
|
|
33
|
-
|
|
34
|
-
- Fix a bug in Router#url and append Router object to ctx. [#350](https://github.com/alexmingoia/koa-router/pull/350)
|
|
35
|
-
- Adds `_matchedRouteName` to context [#337](https://github.com/alexmingoia/koa-router/pull/337)
|
|
36
|
-
- Respond to CORS preflights with 200, 0 length body [#359](https://github.com/alexmingoia/koa-router/issues/359)
|
|
37
|
-
|
|
38
|
-
## 7.1.1
|
|
39
|
-
|
|
40
|
-
- Fix bug where param handlers were run out of order [#282](https://github.com/alexmingoia/koa-router/pull/282)
|
|
41
|
-
|
|
42
|
-
## 7.1.0
|
|
43
|
-
|
|
44
|
-
- Backports: merge 5.4 work into the 7.x upstream. See 5.4.0 updates for more details.
|
|
45
|
-
|
|
46
|
-
## 7.0.1
|
|
47
|
-
|
|
48
|
-
- Fix: allowedMethods should be ctx.method not this.method [#215](https://github.com/alexmingoia/koa-router/pull/215)
|
|
49
|
-
|
|
50
|
-
## 7.0.0
|
|
51
|
-
|
|
52
|
-
- The API has changed to match the new promise-based middleware
|
|
53
|
-
signature of koa 2. See the
|
|
54
|
-
[koa 2.x readme](https://github.com/koajs/koa/tree/2.0.0-alpha.3) for more
|
|
55
|
-
information.
|
|
56
|
-
- Middleware is now always run in the order declared by `.use()` (or `.get()`,
|
|
57
|
-
etc.), which matches Express 4 API.
|
|
58
|
-
|
|
59
|
-
## 5.4.0
|
|
60
|
-
|
|
61
|
-
- Expose matched route at `ctx._matchedRoute`.
|
|
62
|
-
|
|
63
|
-
## 5.3.0
|
|
64
|
-
|
|
65
|
-
- Register multiple routes with array of paths [#203](https://github.com/alexmingoia/koa-router/issue/143).
|
|
66
|
-
- Improved router.url() [#143](https://github.com/alexmingoia/koa-router/pull/143)
|
|
67
|
-
- Adds support for named routes and regular expressions
|
|
68
|
-
[#152](https://github.com/alexmingoia/koa-router/pull/152)
|
|
69
|
-
- Add support for custom throw functions for 405 and 501 responses [#206](https://github.com/alexmingoia/koa-router/pull/206)
|
|
70
|
-
|
|
71
|
-
## 5.2.3
|
|
72
|
-
|
|
73
|
-
- Fix for middleware running twice when nesting routes [#184](https://github.com/alexmingoia/koa-router/issues/184)
|
|
74
|
-
|
|
75
|
-
## 5.2.2
|
|
76
|
-
|
|
77
|
-
- Register routes without params before those with params [#183](https://github.com/alexmingoia/koa-router/pull/183)
|
|
78
|
-
- Fix for allowed methods [#182](https://github.com/alexmingoia/koa-router/issues/182)
|
|
79
|
-
|
|
80
|
-
## 5.2.0
|
|
81
|
-
|
|
82
|
-
- Add support for async/await. Resolves [#130](https://github.com/alexmingoia/koa-router/issues/130).
|
|
83
|
-
- Add support for array of paths by Router#use(). Resolves [#175](https://github.com/alexmingoia/koa-router/issues/175).
|
|
84
|
-
- Inherit param middleware when nesting routers. Fixes [#170](https://github.com/alexmingoia/koa-router/issues/170).
|
|
85
|
-
- Default router middleware without path to root. Fixes [#161](https://github.com/alexmingoia/koa-router/issues/161), [#155](https://github.com/alexmingoia/koa-router/issues/155), [#156](https://github.com/alexmingoia/koa-router/issues/156).
|
|
86
|
-
- Run nested router middleware after parent's. Fixes [#156](https://github.com/alexmingoia/koa-router/issues/156).
|
|
87
|
-
- Remove dependency on koa-compose.
|
|
88
|
-
|
|
89
|
-
## 5.1.1
|
|
90
|
-
|
|
91
|
-
- Match routes in order they were defined. Fixes #131.
|
|
92
|
-
|
|
93
|
-
## 5.1.0
|
|
94
|
-
|
|
95
|
-
- Support mounting router middleware at a given path.
|
|
96
|
-
|
|
97
|
-
## 5.0.1
|
|
98
|
-
|
|
99
|
-
- Fix bug with missing parameters when nesting routers.
|
|
100
|
-
|
|
101
|
-
## 5.0.0
|
|
102
|
-
|
|
103
|
-
- Remove confusing API for extending koa app with router methods. Router#use()
|
|
104
|
-
does not have the same behavior as app#use().
|
|
105
|
-
- Add support for nesting routes.
|
|
106
|
-
- Remove support for regular expression routes to achieve nestable routers and
|
|
107
|
-
enable future trie-based routing optimizations.
|
|
108
|
-
|
|
109
|
-
## 4.3.2
|
|
110
|
-
|
|
111
|
-
- Do not send 405 if route matched but status is 404. Fixes #112, closes #114.
|
|
112
|
-
|
|
113
|
-
## 4.3.1
|
|
114
|
-
|
|
115
|
-
- Do not run middleware if not yielded to by previous middleware. Fixes #115.
|
|
116
|
-
|
|
117
|
-
## 4.3.0
|
|
118
|
-
|
|
119
|
-
- Add support for router prefixes.
|
|
120
|
-
- Add MIT license.
|
|
121
|
-
|
|
122
|
-
## 4.2.0
|
|
123
|
-
|
|
124
|
-
- Fixed issue with router middleware being applied even if no route was
|
|
125
|
-
matched.
|
|
126
|
-
- Router.url - new static method to generate url from url pattern and data
|
|
127
|
-
|
|
128
|
-
## 4.1.0
|
|
129
|
-
|
|
130
|
-
Private API changed to separate context parameter decoration from route
|
|
131
|
-
matching. `Router#match` and `Route#match` are now pure functions that return
|
|
132
|
-
an array of routes that match the URL path.
|
|
133
|
-
|
|
134
|
-
For modules using this private API that need to determine if a method and path
|
|
135
|
-
match a route, `route.methods` must be checked against the routes returned from
|
|
136
|
-
`router.match()`:
|
|
137
|
-
|
|
138
|
-
```javascript
|
|
139
|
-
var matchedRoute = router.match(path).filter(function (route) {
|
|
140
|
-
return ~route.methods.indexOf(method);
|
|
141
|
-
}).shift();
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
## 4.0.0
|
|
145
|
-
|
|
146
|
-
405, 501, and OPTIONS response handling was moved into separate middleware
|
|
147
|
-
`router.allowedMethods()`. This resolves incorrect 501 or 405 responses when
|
|
148
|
-
using multiple routers.
|
|
149
|
-
|
|
150
|
-
### Breaking changes
|
|
151
|
-
|
|
152
|
-
4.x is mostly backwards compatible with 3.x, except for the following:
|
|
153
|
-
|
|
154
|
-
- Instantiating a router with `new` and `app` returns the router instance,
|
|
155
|
-
whereas 3.x returns the router middleware. When creating a router in 4.x, the
|
|
156
|
-
only time router middleware is returned is when creating using the
|
|
157
|
-
`Router(app)` signature (with `app` and without `new`).
|