【问题标题】:NestJS How to consume async middleware?NestJS 如何使用异步中间件?
【发布时间】:2019-11-03 17:05:48
【问题描述】:

我使用 NestJS 框架,并且想在我的应用程序中将多个中间件应用于路由。每个中间件都是一个实现NestMiddleware 接口的类。这些中间件之一是异步的,在调用路由处理程序之前不会被使用。有没有办法在处理路由之前解决这个中间件的承诺?

我的代码

异步中间件(page-loader.middleware)

import { Injectable, NestMiddleware } from '@nestjs/common';

@Injectable()

export class PageLoader implements NestMiddleware {

  async use(req: any, res: any, next: () => void) {
    try {
      req.body.html = await req.body.fetcher.fetch();
    } catch (error) {
      throw new Error(error);
    } finally {
      next();
    }
  }

}

控制器 (create-article.controller)

import { Controller, Post, Body } from '@nestjs/common';
import { SaveArticleService } from './save-article.service';
import { CreateArticleDto } from './create-article.dto';

@Controller()

export class CreateArticleController {

  constructor(private readonly saveArticleService: SaveArticleService) {}

  @Post('/create')
  async create(@Body() createArticleDto: CreateArticleDto) {
    return this.saveArticleService.save(createArticleDto);
  }

}

模块 (create-article.module)

import { Module, NestModule, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { CreateArticleController } from './create-article.controller';
import { SaveArticleService } from './save-article.service';

// Another (sync) middleware
import { ExtensionExtractor } from './extension-extractor.middleware'; 

// The async middleware
import { PageLoader } from './page-loader.middleware';

@Module({
  controllers: [CreateArticleController],
  providers: [SaveArticleService],
})

export class CreateArticleModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
    .apply(ExtensionExtractor, PageLoader)
    .forRoutes({ path: 'create', method: RequestMethod.POST});
  }
}

我没有包含控制器中使用的服务的 sn-p,因为它与我的问题无关。

我尝试了什么

这个question 没有帮助我解决问题,因为中间件结构不同。我正在等待中间件方法解决它的承诺,而不是等待中间件内部重用的输入。

这些github issue 的答案不相关,因为 NestJS API 发生了巨大变化。

提前感谢您的帮助!

【问题讨论】:

    标签: async-await middleware nestjs


    【解决方案1】:

    您绝对可以在 Nest 中使用异步中间件;但是,使用.forRoutes({path: 'path', method: method}); 策略存在问题。

    我设置了一个快速的中间件使用者来展示它是如何在不使用 RequestMethod.GET 的情况下工作的。

    UserModule(包括中间件)

    import { MiddlewareConsumer, Module, NestModule, RequestMethod } from '@nestjs/common';
    import { UserController } from './user.controller';
    import { UserService } from './user.service';
    
    function asyncTimeout(milliseconds: number): Promise<string> {
      return new Promise((resolve, reject) => {
        setTimeout(() => resolve('DONE'), milliseconds);
      });
    }
    
    @Module({
      controllers: [UserController],
      providers: [UserService],
      exports: [UserService]
    })
    export class UserModule implements NestModule {
      configure(consumer: MiddlewareConsumer) {
        consumer
          .apply((req, res, next) => {
            console.log('Using forRoutes(path)');
            console.log('syncronous middleware');
            next();
          },
            (async (req, res, next) => {
              console.log('Using forRoutes(path)');
              const start = Date.now();
              const done = await asyncTimeout(5000);
              console.log(done);
              console.log('Time taken:' + (Date.now() - start));
              next();
            })
          )
          .forRoutes('/')
          .apply((req, res, next) => {
            console.log('Using forRoutes({path, method})');
            console.log('syncronous middleware');
            next();
          },
            (async (req, res, next) => {
              console.log('Using forRoutes({path, method})');
              const start = Date.now();
              const done = await asyncTimeout(5000);
              console.log(done);
              console.log('Time taken:' + (Date.now() - start));
              next();
            })
          )
          .forRoutes({path: '/', method: RequestMethod.GET});
      }
    }
    
    
    

    用户控制器(片段)

    import { Controller, Get } from '@nestjs/common';
    import { UserSerivce } from './user.service';
    
    @Controller('user')
    export class UserController {
      constructor(private readonly userService: UserService) {}
    
      @Get('/')
      testFunction() {
        return {greeting: 'hello'};
      }
    }
    

    输出

    [2019-06-20 22:40:48.191] [INFO] | Listening at http://localhost:3333/api
    Using forRoutes(path)
    syncronous middleware
    Using forRoutes(path)
    DONE
    Time taken:5002
    [2019-06-20 22:40:57.346] [INFO] | [Nest] 30511 [Morgan] GET /api/user 200 5014.234 ms - 20
    

    我在两种设置中都使用了相同的中间件函数,但是您可以看到异步中间件在使用 .forRoutes(path) 时按预期响应,而在使用 .forRoutes({path, method}) 时则没有(请原谅我的自定义记录器)。

    这应该作为一个问题在 GitHub 上与 Kamil 一起提出来解决,但您的设置可以正常工作。如果您选择打开一个,请随意使用我在此处的任何代码为该问题设置一个示例存储库。

    【讨论】:

    • 非常感谢您的帮助!我在我的异步中间件中发现了一个错误,导致我的测试失败。我更正了它,并尝试重现您在答案中描述的错误,但使用 .forRoutes({path, method}) 策略对我来说很好......
    • 所以,有趣的是,使用forRoutes('/') 对我的一个中间件有效,但对另一个无效,但将forRoutes('/user') 用于相应的forRoutes(path: '/user', method: RequestMethod.GET) 会使两个中间件段都触发。主要记录这一点,以防其他人遇到类似问题。
    • 感谢您的研究,我会接受您的回答,希望读者在cmets中看看!
    猜你喜欢
    • 1970-01-01
    • 2021-12-14
    • 2019-01-25
    • 2022-11-10
    • 2022-07-14
    • 2019-07-13
    • 1970-01-01
    • 1970-01-01
    • 2018-08-07
    相关资源
    最近更新 更多