FAQ

    基本上,每个本机(特定于平台的)HTTP 服务器/库实例都包含在 adapter(适配器)中。适配器注册为全局可用的提供程序,可以从应用程序上下文中提取,也可以轻松地注入其他提供程序。

    为了从应用程序上下文外部获取 HttpAdapter 引用,您可以调用 getHttpAdapter() 方法。

    上下文策略

    为了从应用程序上下文中获取HttpAdapterHost 引用,您可以采用与任何其他现有提供程序相同的方式注入它(例如,通过 constructor注入)。

    1. export class CatsService {
    2. constructor(private adapterHost: HttpAdapterHost) {}
    3. }

    !> HttpAdapterHost 需要从 @nestjs/core 导入包。

    HttpAdapterHost 不是真实的 HttpAdapter 。为了获得 HttpAdapter ,只需访问该 httpAdapter 属性。

    1. const adapterHost = app.get(HttpAdapterHost);
    2. const httpAdapter = adapterHost.httpAdapter;

    httpAdapter 是底层框架使用的 HTTP 适配器的实际实例。它可以是 ExpressAdapterFastifyAdapter的实例(两个类都扩展了自AbstractHttpAdapter)。

    每个适配器都公开了几种与 HTTP 服务器交互的有用方法。尽管如此,如果您想直接访问库引用,请调用 getInstance() 方法。

    1. const instance = httpAdapter.getInstance();

    全局路由前缀

    要为应用程序中的每个路由设置前缀, 让我们使用 INestApplication 对象的 setGlobalPrefix() 方法。

    1. const app = await NestFactory.create(ApplicationModule);
    2. const microservice = app.connectMicroservice({
    3. transport: Transport.TCP,
    4. });
    5. await app.startAllMicroservicesAsync();
    6. await app.listen(3001);

    要连接多个微服务实例,要为每个微服务调用connectMicroservice()方法:

    1. const app = await NestFactory.create(AppModule);
    2. // microservice #1
    3. const microserviceTcp = app.connectMicroservice<MicroserviceOptions>({
    4. transport: Transport.TCP,
    5. port: 3001,
    6. },
    7. // microservice #2
    8. const microserviceRedis = app.connectMicroservice<MicroserviceOptions>({
    9. transport: Transport.REDIS,
    10. options: {
    11. url: 'redis://localhost:6379',
    12. },
    13. });
    14. await app.startAllMicroservicesAsync();
    15. await app.listen(3001);

    HTTPS 和多服务器

    HTTPS

    为了创建使用 HTTPS 协议的应用程序,在传递给NestFactorycreate()方法中设置httpsOptions属性:

    1. const httpsOptions = {
    2. key: fs.readFileSync('./secrets/private-key.pem'),
    3. cert: fs.readFileSync('./secrets/public-certificate.pem'),
    4. };
    5. const app = await NestFactory.create(ApplicationModule, {
    6. httpsOptions,
    7. });
    8. await app.listen(3000);

    如果使用FastifyAdapter,则创建应用如下:

    下列方法展示了如何使用Nest应用同时监视多个端口(例如,在非HTTPS端口和HTTPS端口)。

    1. const httpsOptions = {
    2. key: fs.readFileSync('./secrets/private-key.pem'),
    3. cert: fs.readFileSync('./secrets/public-certificate.pem'),
    4. };
    5. const server = express();
    6. const app = await NestFactory.create(
    7. ApplicationModule,
    8. new ExpressAdapter(server),
    9. );
    10. await app.init();
    11. http.createServer(server).listen(3000);
    12. https.createServer(httpsOptions, server).listen(443);

    ?> ExpressAdapter 需要从 @nestjs/platform-express 包导入。httphttps包是原生的Node.js包。

    !> 中该方法无法工作。

    Nest应用程序处理请求并生成回应的过程被称为请求生命周期。使用中间件、管道、守卫和拦截器时,要在请求生命周期中追踪特定的代码片段的执行很困难,尤其是在全局、控制器或者路由的部件中。一般来说,一个请求流经中间件、守卫与拦截器,然后到达管道,并最终回到拦截器中的返回路径中(从而产生响应)。

    中间件

    中间件以特殊的顺序执行。首先,Nest运行全局绑定的中间件(例如app.use中绑定的中间件),然后运行在路径中指定的模块绑定的中间件。中间件以他们绑定的次序顺序执行,这和在Express中的中间件工作原理是类似的。

    守卫

    1. @UseGuards(Guard1, Guard2)
    2. @Controller('cats')
    3. export class CatsController {
    4. constructor(private catsService: CatsService) {}
    5. @Get()
    6. getCats(): Cats[] {
    7. return this.catsService.getCats();
    8. }
    9. }

    Guard1会在Guard2之前执行并且这两者都先于Guard3执行。

    ?> 在提到全局绑定和本地绑定时主要是指守卫(或其他部件)绑定的位置不同。如果你正在使用app.useGlobalGuard()或者通过模块提供一个部件,它就是全局绑定的。否则,当一个装饰器在控制器类之前时,它就是绑定在控制器上的,当装饰器在路径声明之前时它就是绑定在路径上的。

    拦截器在大部分情况下和守卫类似。只有一种情况例外:当拦截器返回的是一个RxJS Observables时,observables是以先进后出的顺序执行的。因此,入站请求是按照标准的全局、控制器和路由层次执行的,但请求的响应侧(例如,当从一个控制器方法的处理器返回时)则是从路由到控制器再到全局。另外,由管道、控制器或者服务抛出的任何错误都可以在拦截器的catchError操作者中被读取。

    管道

    管道按照标准的从全局到控制器再到路由的绑定顺序,遵循先进先出的原则按照@usePipes()参数次序顺序执行。然而,在路由参数层次,如果由多个管道在执行,则会按照自后向前的参数顺序执行,这在路由层面和控制器层面的管道中同样如此,例如,我们有如下控制器:

    1. @UsePipes(GeneralValidationPipe)
    2. @Controller('cats')
    3. export class CatsController {
    4. constructor(private catsService: CatsService) {}
    5. @UsePipes(RouteSpecificPipe)
    6. @Patch(':id')
    7. updateCat(
    8. @Body() body: UpdateCatDTO,
    9. @Param() params: UpdateCatParams,
    10. @Query() query: UpdateCatQuery,
    11. ) {
    12. return this.catsService.updateCat(body, params, query);
    13. }
    14. }

    在这里GeneralValidationPipe会先执行query,然后是params,最后是body对象,接下来在执行RouteSpecificPipe管道时同样按照上述次序执行。如果存在任何参数层的管道,它会在(同样的,按照自后向前的参数顺序)控制器和路由层的管道之后执行。

    过滤器

    过滤器是唯一一个不按照全局第一顺序执行的组件。而是会从最低层次开始处理,也就是说先从任何路由绑定的过滤器开始,然后是控制器层,最后才是全局过滤器。注意,异常无法从过滤器传递到另一个过滤器;如果一个路由层过滤器捕捉到一个异常,一个控制器或者全局层面的过滤器就捕捉不到这个异常。如果要实现类似的效果可以在过滤器之间使用继承。

    ?> 过滤器仅在请求过程中任何没有捕获的异常发生时执行。捕获的异常如try/catch语句不会触发过滤器。一旦遇到未处理的异常,请求接下来的生命周期会被忽略并直接跳转到过滤器。

    一般来说,请求生命周期大致如下:

    1. 收到请求
    2. 全局绑定的中间件
    3. 模块绑定的中间件
    4. 全局守卫
    5. 控制层守卫
    6. 路由守卫
    7. 全局拦截器(控制器之前)
    8. 控制器层拦截器 (控制器之前)
    9. 路由拦截器 (控制器之前)
    10. 全局管道
    11. 控制器管道
    12. 路由管道
    13. 路由参数管道
    14. 控制器(方法处理器) 15。服务(如果有)
    15. 路由拦截器(请求之后)
    16. 控制器拦截器 (请求之后)
    17. 全局拦截器 (请求之后)
    18. 服务器响应

    实例

    译者署名