mongoose 跳过异步中间件,在Express中执行下一个路由

a1o7rhls  于 2023-05-07  发布在  Go
关注(0)|答案(1)|浏览(130)

我正在尝试使用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' }

所以我想这里有个问题?也许它没有被完全执行,因此调用下一个中间件?

tvmytwxo

tvmytwxo1#

看起来主要的控制问题是在validateBody.create()中。您正在执行:

this.preventMaliciousBody(this.bodyKeys.create)(req, res, next);

但是,假设它是同步的,因为您在它之后继续执行。相反,直到它调用其实现内部的next()时才完成。您可以通过发送自己的next回调而不是实际的next来使用这样的嵌套中间件。这样,当回调函数被调用时,你就可以看到它实际上是什么时候完成的。

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, (err) => {
                if (err) {
                    next(err);
                    return;
                }
                if (
                    req.body.type === collectionTypes.searchingCollection &&
                    !req.body.searchQuery
                ) {
                    next(createError(400, 'invalid body'));
                } else {
                    next();
                }

            }));

        }
    )
}

moveReqKeyToBody()中也有几个编码错误。
.forEach()中,您可以多次调用next(createError(400, ...));,然后在.forEach()循环之后,再调用next()。您需要对它进行编码,以便next(...)只被调用一次。
虽然我真的不能告诉这个函数应该做什么,或者这是否完全是你所问的问题的原因,你可以从解决上面的两个问题开始,就像这样:

// in the parent Controller class
moveReqKeyToBody(bodyKey: string, ...nestedReqKey: string[]) {
    return function (req: IRequest, res: Response, next: NextFunction) {
        let iterator: any = req;
        for (let key: any of nestedReqKey) {
            if (iterator[key]) {
                iterator = iterator[key];
            } else {
                next(createError(400, `missing item from request: ${nestedReqKey}`));
                return;
            }
        }
        req.body[bodyKey] = iterator;

        next();
    };
}

我还注意到,如果switch操作不是createsendResponse()不会做任何事情。您至少应该有一个default手臂到那个发送响应或触发错误的交换机。如果你总是期望参数是create,那么你甚至不需要switch语句。所以,它应该是一个或另一个。

相关问题