Skip to content

Commit 5579ccb

Browse files
committed
fix: pass user middleware chain when calling their error handler
1 parent eb61842 commit 5579ccb

File tree

2 files changed

+87
-2
lines changed

2 files changed

+87
-2
lines changed

src/middleware/propagate_error_to_client_error_handle.ts

+47-2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ export const createPropagateErrorToClientErrorHandleMiddleware = (
4949
): ErrorHandle => {
5050
const userFunctionErrorHandle =
5151
getFirstUserFunctionErrorHandleMiddleware(userFunction);
52+
const nextMiddlewareAfterErrorHandleMiddleware =
53+
getNextMiddlewareAfterErrorMiddleware(userFunction);
5254

5355
return function (
5456
err: Error,
@@ -58,6 +60,16 @@ export const createPropagateErrorToClientErrorHandleMiddleware = (
5860
) {
5961
// Propagate error to user function error handle.
6062
if (userFunctionErrorHandle) {
63+
// If the user has a further middleware stack after the error handle middleware,
64+
// continue the chain, otherwise, propagate to the default error handle.
65+
if (nextMiddlewareAfterErrorHandleMiddleware) {
66+
return userFunctionErrorHandle(
67+
err,
68+
req,
69+
res,
70+
nextMiddlewareAfterErrorHandleMiddleware
71+
);
72+
}
6173
return userFunctionErrorHandle(err, req, res, next);
6274
}
6375

@@ -73,17 +85,50 @@ export const createPropagateErrorToClientErrorHandleMiddleware = (
7385
const getFirstUserFunctionErrorHandleMiddleware = (
7486
userFunction: HandlerFunction
7587
): ErrorHandle | null => {
88+
const errorHandleMiddlewareIndex =
89+
getFirstUserFunctionErrorHandleMiddlewareIndex(userFunction);
90+
if (!errorHandleMiddlewareIndex) {
91+
return null;
92+
}
93+
94+
return (userFunction as Express)._router.stack[errorHandleMiddlewareIndex]
95+
.handle as unknown as ErrorHandle;
96+
};
97+
98+
const getNextMiddlewareAfterErrorMiddleware = (
99+
userFunction: HandlerFunction
100+
): NextFunction | null => {
101+
const errorHandleMiddlewareIndex =
102+
getFirstUserFunctionErrorHandleMiddlewareIndex(userFunction);
103+
if (!errorHandleMiddlewareIndex) {
104+
return null;
105+
}
106+
107+
const nextMiddlewareAfterErrorHandleIndex = errorHandleMiddlewareIndex + 1;
108+
const middlewares: ILayer[] = (userFunction as Express)._router.stack;
109+
if (middlewares.length - 1 < nextMiddlewareAfterErrorHandleIndex) {
110+
return null;
111+
}
112+
113+
return middlewares[nextMiddlewareAfterErrorHandleIndex]
114+
.handle as NextFunction;
115+
};
116+
117+
const getFirstUserFunctionErrorHandleMiddlewareIndex = (
118+
userFunction: HandlerFunction
119+
): number | null => {
76120
if (!isExpressApp(userFunction)) {
77121
return null;
78122
}
79123

80124
const middlewares: ILayer[] = (userFunction as Express)._router.stack;
81-
for (const middleware of middlewares) {
125+
for (let index = 0; index < middlewares.length; index++) {
126+
const middleware = middlewares[index];
82127
if (
83128
middleware.handle &&
84129
middleware.handle.length === EXPRESS_ERROR_HANDLE_PARAM_LENGTH
85130
) {
86-
return middleware.handle as unknown as ErrorHandle;
131+
return index;
87132
}
88133
}
89134

test/middleware/propagate_error_to_client_error_handle.ts

+40
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,46 @@ describe('propagateErrorToClientErrorHandleMiddleware', () => {
4949
assert.strictEqual((next as sinon.SinonSpy).called, false);
5050
});
5151

52+
it('user express app with error handle calls user function app error handle and subsequent middleware', () => {
53+
const app = express();
54+
app.use(
55+
(
56+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
57+
_err: Error,
58+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
59+
_req: Request,
60+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
61+
_res: Response,
62+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
63+
next: NextFunction
64+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
65+
): any => {
66+
next();
67+
}
68+
);
69+
app.use(
70+
(
71+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
72+
_err: Error,
73+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
74+
_req: Request,
75+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
76+
_res: Response,
77+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
78+
_next: NextFunction
79+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
80+
): any => {
81+
errListener();
82+
}
83+
);
84+
85+
const middleware = createPropagateErrorToClientErrorHandleMiddleware(app);
86+
middleware(error, request, response, next);
87+
88+
assert.strictEqual((errListener as sinon.SinonSpy).called, true);
89+
assert.strictEqual((next as sinon.SinonSpy).called, false);
90+
});
91+
5292
it('user express app without error handle calls default express error handle', () => {
5393
const app = express();
5494

0 commit comments

Comments
 (0)