koa-unless源码解析

介绍

Conditionally skip a middleware when a condition is met.

当条件符合时跳过中间件,npmjs 官方这样介绍。使用时需要将该库挂载到其他中间件上。它相当于被挂载中间件的一个壳,在执行时判断是否 next 跳过被挂载中间件。

demo:

1
2
3
4
5
6
7
var unless = require("koa-unless");
var serve = require("koa-static");

var static = serve(__dirname + "/public");
static.unless = unless;

app.use(static.unless({ method: "OPTIONS" }));

或者在中间件内部挂载。

1
2
3
4
5
6
7
8
9
module.exports = function () {
var mymid = function (ctx, next) {
// Do something
};

mymid.unless = require("koa-unless");

return mymid;
};

源码 koa-unless 1.0.7

为了方便,在这里把需要挂载 unless 的中间件称为宿主。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
var url = require('url');

// 该中间有条件的判断是否中断或放行宿主的执行
module.exports = function (options) {
// 获取宿主对象
var originalMiddleware = this;

// 如果直接传入函数,则转变为options的格式
var opts = typeof options === 'function' ? { custom: options } : options;

// 返回一个中间件,接收next方法,它相当于宿主的壳
return function* (next) {
var requestedUrl = url.parse((opts.useOriginalUrl ? this.originalUrl : this.url) || '', true);

// 如果match匹配则直接next跳过当前宿主对象
if (matchesCustom(this, opts) || matchesPath(requestedUrl, opts) ||
matchesExtension(requestedUrl, opts) || matchesMethod(this.method, opts)) {
return yield* next;
}

// 如果不匹配则调用宿主,传入next
yield* originalMiddleware.call(this, next);
};
};

// 执行传入的自定义函数匹配路由,返回是否匹配的布尔值
function matchesCustom(ctx, opts) {
if (opts.custom) {
return opts.custom.call(ctx);
}
return false;
}

// 利用字符串或者正则判断是否匹配requestUrl
function matchesPath(requestedUrl, opts) {
// 组装成数组形式,兼容数组形式的参数
var paths = !opts.path || Array.isArray(opts.path) ?
opts.path : [opts.path];

if (paths) {
return paths.some(function (p) {
return (typeof p === 'string' && p === requestedUrl.pathname) ||
(p instanceof RegExp && !!p.exec(requestedUrl.pathname));
});
}

return false;
}

// 判断requestUrl是否以指定后缀结尾
function matchesExtension(requestedUrl, opts) {
var exts = !opts.ext || Array.isArray(opts.ext) ?
opts.ext : [opts.ext];

if (exts) {
// 遍历后缀数组,截取pathname的等长后缀进行判断
return exts.some(function (ext) {
return requestedUrl.pathname.substr(ext.length * -1) === ext;
});
}
}

// 判断是否包含指定方法
function matchesMethod(method, opts) {
var methods = !opts.method || Array.isArray(opts.method) ?
opts.method : [opts.method];

if (methods) {
// 奇技淫巧,从索引值转成布尔值
return !!~methods.indexOf(method);
}
}

yield *next

这里不必纠结,koa1.x的中间件写法,实际和 koa2 中的await next()是一个味道,执行到下一个中间件。在此处因为unless是在宿主的外层,先一步执行。所以 match 匹配后执行next会导致跳过宿主,从而达到条件过滤的目的。未命中条件则继续执行宿主中的逻辑。

~运算符

indexOf返回-1和索引值。通过~转成 0 和负数,在通过!!转成布尔值,0变成false,负数变成true

参考链接

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

给阿姨来一杯卡普基诺~

支付宝
微信