我正在尝试使用express.js实现一个类似zotero的应用程序,我遇到了一个问题。
我实际上并不知道问题出在哪里,但根据我得到的日志,我了解到不知何故,我的中间件没有按照它们设置的顺序执行,而且请求泄露到另一个我为错误设置的路由处理程序中。
这是我的集合的路由处理程序:
router
.route('/')
.post(
controller.addLibraryToBody,
controller.validateBody.create,
controller.createOne,
controller.sendResponse('create'),
controller.debugLog
);
这些是中间件:
// in the parent Controller class
moveReqKeyToBody(bodyKey: string, ...nestedReqKey: string[]) {
return function (req: IRequest, res: Response, next: NextFunction) {
let iterator: any = req;
nestedReqKey.forEach(key => {
if (iterator[key]) iterator = iterator[key];
else
next(createError(400, `missing item from request: ${nestedReqKey}`));
});
req.body[bodyKey] = iterator;
next();
};
}
preventMaliciousBody(bodyValidationKeys: BodyValidationKeys) {
let { mandatory = [], allowed = [] } = bodyValidationKeys;
return function (req: Request, res: Response, next: NextFunction) {
allowed = allowed.concat(mandatory);
if (
mandatory.every(value => req.body[value]) &&
Object.keys(req.body).every(value => allowed.includes(value))
)
next();
else next(createError(400, 'invalid body'));
};
}
createOne = catchAsync(
async (
req: IRemoveFieldsRequest,
res: Response,
next: NextFunction
): Promise<void> => {
const document = await this.model.create(req.body);
if (req.removeFields) {
req.removeFields.forEach(field => {
document[field] = undefined;
});
}
req[this.modelName] = document;
next();
}
);
sendResponse = (operation: CRUD) => {
return (req: IRequest, res: Response, next: NextFunction) => {
switch (operation) {
case 'create':
res.status(201).json({
status: 'success',
data: req[this.modelName]
});
break;
}
};
};
debugLog(req: IRequest, res: Response, next: NextFunction) {
console.log(
`${Date.now()} - ${req.url} - ParamKeys: ${Object.keys(
req.params
)} - BodyKeys: ${Object.keys(req.body)}`
);
next();
}
// in the CollectionController
addLibraryToBody = this.moveReqKeyToBody('parent', 'library', 'id');
validateBody = {
create: catchAsync(
async (req: IRequest, res: Response, next: NextFunction) => {
if (!req.body.type) req.body.type = collectionTypes.collection;
this.preventMaliciousBody(this.bodyKeys.create)(req, res, next);
if (
req.body.type === collectionTypes.searchingCollection &&
!req.body.searchQuery
)
next(createError(400, 'invalid body'));
else next();
}
)
}
这是我的app.js:
app
.use('/api', apiRouter)
.use(
'*',
function (req: Request, res: Response, next: NextFunction) {
// todo fix this weird bug
if (res.headersSent) {
console.log(req.url);
console.log(req.body);
console.log(req.params);
} else next();
},
Controller.unavailable
)
.use(errorHandler);
这是 Postman 在那条路线上的输出:
{
"status": "success"
}
这是服务器的CLI输出:* 我有摩根中间件运行(第一行)*
POST /api/libraries/6447a4c4dc088d6d43204668/collections 201 6.358 ms - 20
1683371139354 - / - ParamKeys: id - BodyKeys: name,parent,type
/
{
name: 'norma coll',
parent: '6447a4c4dc088d6d43204668',
type: 'Collection'
}
{ '0': '/api/libraries/6447a4c4dc088d6d43204668/collections' }
我试着记录一切,并搜索了网络,但不明白出了什么问题。
编辑:我在createOne
方法中添加了2个console.log
:
createOne = catchAsync(
async (
req: IRemoveFieldsRequest,
res: Response,
next: NextFunction
): Promise<void> => {
console.log('started creating...');
const document = await this.model.create(req.body);
if (req.removeFields) {
req.removeFields.forEach(field => {
document[field] = undefined;
});
}
req[this.modelName] = document;
console.log('created');
next();
}
);
而且是在摩根日志前后印的
started creating...
POST /api/libraries/6447a4c4dc088d6d43204668/collections 201 23.049 ms - 20
created
1683387954094 - / - ParamKeys: id - BodyKeys: name,parent,type
/
{
name: 'nor coll',
parent: '6447a4c4dc088d6d43204668',
type: 'Collection'
}
{ '0': '/api/libraries/6447a4c4dc088d6d43204668/collections' }
所以我想这里有个问题?也许它没有被完全执行,因此调用下一个中间件?
1条答案
按热度按时间tvmytwxo1#
看起来主要的控制问题是在
validateBody.create()
中。您正在执行:但是,假设它是同步的,因为您在它之后继续执行。相反,直到它调用其实现内部的
next()
时才完成。您可以通过发送自己的next回调而不是实际的next来使用这样的嵌套中间件。这样,当回调函数被调用时,你就可以看到它实际上是什么时候完成的。在
moveReqKeyToBody()
中也有几个编码错误。在
.forEach()
中,您可以多次调用next(createError(400, ...));
,然后在.forEach()
循环之后,再调用next()
。您需要对它进行编码,以便next(...)
只被调用一次。虽然我真的不能告诉这个函数应该做什么,或者这是否完全是你所问的问题的原因,你可以从解决上面的两个问题开始,就像这样:
我还注意到,如果switch操作不是
create
,sendResponse()
不会做任何事情。您至少应该有一个default
手臂到那个发送响应或触发错误的交换机。如果你总是期望参数是create
,那么你甚至不需要switch
语句。所以,它应该是一个或另一个。