/
middlewareService.ts
128 lines (121 loc) 路 3.71 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
import { isClass, Provide, Scope, ScopeEnum } from '@midwayjs/decorator';
import {
CommonMiddleware,
IMiddleware,
IMidwayContainer,
FunctionMiddleware,
IMidwayApplication,
} from '../interface';
import { MidwayCommonError, MidwayParameterError } from '../error';
import { isIncludeProperty, pathMatching } from '../util';
@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 (isClass(fn) || typeof fn === 'string') {
if (
typeof fn === 'string' &&
!this.applicationContext.hasDefinition(fn)
) {
throw new MidwayCommonError(
'Middleware definition not found in midway container'
);
}
const classMiddleware = await this.applicationContext.getAsync<
IMiddleware<T, R, N>
>(fn as any);
if (classMiddleware) {
fn = classMiddleware.resolve(app);
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,
});
(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, 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();
try {
if (supportBody) {
return Promise.resolve(
fn(context, dispatch.bind(null, i + 1), {
index,
} as any)
).then(result => {
// need to set body
if (context.body && !result) {
result = context.body;
} else if (result && context.body !== result) {
context.body = result;
}
return result;
});
} else {
return Promise.resolve(
fn(context, dispatch.bind(null, i + 1), {
index,
} as any)
);
}
} catch (err) {
return Promise.reject(err);
}
}
};
if (name) {
composeFn._name = name;
}
return composeFn;
}
public getMiddlewareName(mw) {
return mw.name ?? mw._name;
}
}