-
Notifications
You must be signed in to change notification settings - Fork 568
/
middlewareService.ts
160 lines (151 loc) · 4.97 KB
/
middlewareService.ts
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import { Provide, Scope } from '../decorator';
import {
CommonMiddleware,
IMiddleware,
IMidwayContainer,
FunctionMiddleware,
IMidwayApplication,
ScopeEnum,
} from '../interface';
import { MidwayCommonError, MidwayParameterError } from '../error';
import { isIncludeProperty, pathMatching } from '../util';
import { Types } from '../util/types';
import { debuglog } from 'util';
const debug = debuglog('midway:debug');
@Provide()
@Scope(ScopeEnum.Singleton)
export class MidwayMiddlewareService<T, R, N = unknown> {
constructor(readonly applicationContext: IMidwayContainer) {}
async compose(
middleware: Array<CommonMiddleware<T, R, N> | string>,
app: IMidwayApplication,
name?: string
) {
if (!Array.isArray(middleware)) {
throw new MidwayParameterError('Middleware stack must be an array');
}
const newMiddlewareArr = [];
for (let fn of middleware) {
if (Types.isClass(fn) || typeof fn === 'string') {
if (
typeof fn === 'string' &&
!this.applicationContext.hasDefinition(fn)
) {
throw new MidwayCommonError(
`Middleware definition of "${fn}" not found in midway container`
);
}
const classMiddleware = await this.applicationContext.getAsync<
IMiddleware<T, R, N>
>(fn as any);
if (classMiddleware) {
fn = await classMiddleware.resolve(app);
if (!fn) {
// for middleware enabled
continue;
}
if (!classMiddleware.match && !classMiddleware.ignore) {
if (!fn.name) {
(fn as any)._name = classMiddleware.constructor.name;
}
// just got fn
newMiddlewareArr.push(fn);
} else {
// wrap ignore and match
const mw = fn;
const match = pathMatching({
match: classMiddleware.match,
ignore: classMiddleware.ignore,
thisResolver: classMiddleware,
});
(fn as any) = (ctx, next, options) => {
if (!match(ctx)) return next();
return mw(ctx, next, options);
};
(fn as any)._name = classMiddleware.constructor.name;
newMiddlewareArr.push(fn);
}
} else {
throw new MidwayCommonError('Middleware must have resolve method!');
}
} else {
newMiddlewareArr.push(fn);
}
}
/**
* @param {Object} context
* @param next
* @return {Promise}
* @api public
*/
const composeFn = (context: T, next?) => {
const supportBody = isIncludeProperty(context, 'body');
// last called middleware #
let index = -1;
return dispatch(0);
function dispatch(i) {
if (i <= index)
return Promise.reject(
new MidwayCommonError('next() called multiple times')
);
index = i;
let fn = (newMiddlewareArr as Array<FunctionMiddleware<T, R, N>>)[i];
if (i === newMiddlewareArr.length) fn = next;
if (!fn) return Promise.resolve();
const middlewareName = `${name ? `${name}.` : ''}${index} ${
(fn as any)._name || fn.name || 'anonymous'
}`;
const startTime = Date.now();
debug(`[middleware]: in ${middlewareName} +0`);
try {
if (supportBody) {
return Promise.resolve(
fn(context, dispatch.bind(null, i + 1), {
index,
} as any)
).then(result => {
/**
* 1、return 和 ctx.body,return 的优先级更高
* 2、如果 result 有值(非 undefined),则不管什么情况,都会覆盖当前 body,注意,这里有可能赋值 null,导致 status 为 204,会在中间件处进行修正
* 3、如果 result 没值,且 ctx.body 已经赋值,则向 result 赋值
*/
if (result !== undefined) {
context['body'] = result;
} else if (context['body'] !== undefined) {
result = context['body'];
}
debug(
`[middleware]: out ${middlewareName} +${
Date.now() - startTime
} with body`
);
return result;
});
} else {
return Promise.resolve(
fn(context, dispatch.bind(null, i + 1), {
index,
} as any)
).then(result => {
debug(
`[middleware]: out ${middlewareName} +${Date.now() - startTime}`
);
return result;
});
}
} catch (err) {
debug(
`[middleware]: out ${middlewareName} +${
Date.now() - startTime
} with err ${err.message}`
);
return Promise.reject(err);
}
}
};
if (name) {
composeFn._name = name;
}
return composeFn;
}
}