Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(core): MidwayMiddlewareService with dispatch() #4177

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 107 additions & 78 deletions packages/core/src/service/aspectService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,97 +80,126 @@
* @param aspectObject aspect object, before, round, etc.
*/
public interceptPrototypeMethod(
Clz: new (...args) => any,
Clz: new (...args: any[]) => any,
methodName: string,
aspectObject: IMethodAspect | (() => IMethodAspect)
) {
const originMethod = Clz.prototype[methodName];

if (Types.isAsyncFunction(Clz.prototype[methodName])) {
Clz.prototype[methodName] = async function (...args) {
let error, result;
const newProceed = (...args) => {
return originMethod.apply(this, args);
};
const joinPoint = {
Clz.prototype[methodName] = async function (...args: unknown[]): Promise<unknown> {

Check failure on line 90 in packages/core/src/service/aspectService.ts

View workflow job for this annotation

GitHub Actions / lintAndTestLegacy (lts/*, ubuntu-latest)

Replace `...args:·unknown[]` with `⏎········...args:·unknown[]⏎······`
const opts: ProcessOptions = {
args,
methodName,
target: this,
args: args,
proceed: newProceed,
proceedIsAsyncFunction: true,
} as JoinPoint;

if (typeof aspectObject === 'function') {
aspectObject = aspectObject();
}

try {
await aspectObject.before?.(joinPoint);
if (aspectObject.around) {
result = await aspectObject.around(joinPoint);
} else {
result = await originMethod.call(this, ...joinPoint.args);
}
joinPoint.proceed = undefined;
const resultTemp = await aspectObject.afterReturn?.(
joinPoint,
result
);
result = typeof resultTemp === 'undefined' ? result : resultTemp;
return result;
} catch (err) {
joinPoint.proceed = undefined;
error = err;
if (aspectObject.afterThrow) {
await aspectObject.afterThrow(joinPoint, error);
} else {
throw err;
}
} finally {
await aspectObject.after?.(joinPoint, result, error);
}
};
return processAsync(aspectObject, originMethod, opts);
};
} else {
Clz.prototype[methodName] = function (...args) {
let error, result;
const newProceed = (...args) => {
return originMethod.apply(this, args);
};
const joinPoint = {
Clz.prototype[methodName] = function (...args: unknown[]) {
const opts: ProcessOptions = {
args,
methodName,
target: this,
args: args,
proceed: newProceed,
proceedIsAsyncFunction: false,
} as JoinPoint;

if (typeof aspectObject === 'function') {
aspectObject = aspectObject();
}

try {
aspectObject.before?.(joinPoint);
if (aspectObject.around) {
result = aspectObject.around(joinPoint);
} else {
result = originMethod.call(this, ...joinPoint.args);
}
joinPoint.proceed = undefined;
const resultTemp = aspectObject.afterReturn?.(joinPoint, result);
result = typeof resultTemp === 'undefined' ? result : resultTemp;
return result;
} catch (err) {
joinPoint.proceed = undefined;
error = err;
if (aspectObject.afterThrow) {
aspectObject.afterThrow(joinPoint, error);
} else {
throw err;
}
} finally {
aspectObject.after?.(joinPoint, result, error);
}
};
return processSync(aspectObject, originMethod, opts);
};
}
}
}

interface ProcessOptions {
args: unknown[],

Check failure on line 112 in packages/core/src/service/aspectService.ts

View workflow job for this annotation

GitHub Actions / lintAndTestLegacy (lts/*, ubuntu-latest)

Replace `,` with `;`
methodName: string;
target: any

Check failure on line 114 in packages/core/src/service/aspectService.ts

View workflow job for this annotation

GitHub Actions / lintAndTestLegacy (lts/*, ubuntu-latest)

Insert `;`
}
async function processAsync(
aspectObjectInput: IMethodAspect | (() => IMethodAspect),
originMethod: Function,
options: ProcessOptions
): Promise<unknown> {

let error: Error;
let result: unknown;
const newProceed = (...args: unknown[]) => {
return originMethod.apply(this, args);
};
const joinPoint = Object.assign({}, options, {proceed: newProceed, proceedIsAsyncFunction: true}) as JoinPoint;

let aspectObject: IMethodAspect;
if (typeof aspectObjectInput === 'function') {
aspectObject = aspectObjectInput();
} else {
aspectObject = aspectObjectInput;
}

try {
await aspectObject.before?.(joinPoint);
if (aspectObject.around) {
result = await aspectObject.around(joinPoint);
} else {
result = await originMethod.call(this, ...joinPoint.args);
}
joinPoint.proceed = undefined;
const resultTemp = await aspectObject.afterReturn?.(
joinPoint,
result
);
result = typeof resultTemp === 'undefined' ? result : resultTemp;
return result;
} catch (err) {
joinPoint.proceed = undefined;
error = err;
if (aspectObject.afterThrow) {
await aspectObject.afterThrow(joinPoint, error);
} else {
throw err;
}
} finally {
await aspectObject.after?.(joinPoint, result, error);
}
}

function processSync(
aspectObjectInput: IMethodAspect | (() => IMethodAspect),
originMethod: Function,
options: ProcessOptions
): unknown {

let error: Error;
let result: unknown;
const newProceed = (...args: unknown[]) => {
return originMethod.apply(this, args);
};
const joinPoint = Object.assign({}, options, {proceed: newProceed, proceedIsAsyncFunction: false}) as JoinPoint;

let aspectObject: IMethodAspect;
if (typeof aspectObjectInput === 'function') {
aspectObject = aspectObjectInput();
} else {
aspectObject = aspectObjectInput;
}

try {
aspectObject.before?.(joinPoint);
if (aspectObject.around) {
result = aspectObject.around(joinPoint);
} else {
result = originMethod.call(this, ...joinPoint.args);
}
joinPoint.proceed = undefined;
const resultTemp = aspectObject.afterReturn?.(joinPoint, result);
result = typeof resultTemp === 'undefined' ? result : resultTemp;
return result;
} catch (err) {
joinPoint.proceed = undefined;
error = err;
if (aspectObject.afterThrow) {
aspectObject.afterThrow(joinPoint, error);
} else {
throw err;
}
} finally {
aspectObject.after?.(joinPoint, result, error);
}
}
147 changes: 85 additions & 62 deletions packages/core/src/service/middlewareService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,72 +91,95 @@ export class MidwayMiddlewareService<T, R, N = unknown> {
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);
}
}
const opts: DispatchOptions = {
context,
i: 0,
index: -1,
name,
newMiddlewareArr,
next,
supportBody,
};
return dispatch(opts);
};
if (name) {
composeFn._name = name;
}
return composeFn;
}
}

interface DispatchOptions {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

之前放在里面就是希望是内部逻辑,不暴露到外面,其实也不需要定义了。

context: any;
i: number;
index: number;
name: string;
newMiddlewareArr: unknown[];
next: FunctionMiddleware<any, any>;
supportBody: boolean;
}

function dispatch(options: DispatchOptions): Promise<unknown> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

分离出来了的话逻辑就零散了,其实可读性和关联性会降低

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

嗯,这只是想看看性能上是否有明显差距。等 CI 过了你对比下性能

const { i, context, newMiddlewareArr, name, next, supportBody } = options;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里其实本来能复用外部对象的,现在每次得解构了。


if (i <= options.index) {
return Promise.reject(
new MidwayCommonError('next() called multiple times')
);
}
options.index = i;
let fn = (newMiddlewareArr as Array<FunctionMiddleware<any, any>>)[i];
if (i === newMiddlewareArr.length) {
fn = next;
}
if (!fn) return Promise.resolve();
const middlewareName = `${name ? `${name}.` : ''}${options.index} ${
(fn as any)._name || fn.name || 'anonymous'
}`;
const startTime = Date.now();
debug(`[middleware]: in ${middlewareName} +0`);
try {
if (supportBody) {
const opts = { ...options, i: i + 1 };
return Promise.resolve(
fn(context, dispatch.bind(null, opts), {
index: options.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 {
const opts = { ...options, i: i + 1 };
return Promise.resolve(
fn(context, dispatch.bind(null, opts), {
index: options.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);
}
}
Loading