Nest.js中的设计模式——上下文

Nest.js中的设计模式——上下文,第1张

Nest的平台不可知论

Nest is a platform-agnostic framework.

上篇文章给大家说过Nest中的模块化思想,模块化是组织业务代码的方式,我们还需要用一个传输层(transport layer)将业务暴露出去得以执行,例如Http服务器的话要定义路由找到执行方法,websocket则是定义一个gateway接收所有事件并进行分发。在我理解,平台不可知论说的正是这种能按照所需的暴露服务方式,灵活地进行开发与管理。这也就会意味着,不同暴露方式则会产生不一样的上下文(Context)。

下面来看看Nest中是如何进行设计的。

应用上下文

我们创建Nest应用,是通过@nestjs/core里面的NestFactory方法进行创建的,先看看代码

export declare class NestFactoryStatic {
    create<T extends INestApplication = INestApplication>(module: any, options?: NestApplicationOptions): Promise<T>;
    create<T extends INestApplication = INestApplication>(module: any, httpAdapter: AbstractHttpAdapter, options?: NestApplicationOptions): Promise<T>;
    createMicroservice<T extends object>(module: any, options?: NestMicroserviceOptions & T): Promise<INestMicroservice>;
    createApplicationContext(module: any, options?: NestApplicationContextOptions): Promise<INestApplicationContext>;
   // 省略部分代码
}

可以看到,这里有几个创建应用的方法:

create是创建一个Http Server App:INestApplicationcreateMicroservice是创建一个微服务应用:INestMicroservicecreateApplicationContext则是创建一个独立的应用,没有任何网络监听。 Standalone App

Standalone App比较简单,是直接暴露入口文件作为执行入口,没有绑定socket的就是独立应用。一般是用在CronJob或者Cli

一般用得比较多的就是select()get()方法,ts定义如下面代码:

export interface INestApplicationContext {
    select<T>(module: Type<T> | DynamicModule): INestApplicationContext;
    get<TInput = any, TResult = TInput>(typeOrToken: Type<TInput> | Abstract<TInput> | string | symbol, options?: {
        strict: boolean;
    }): TResult;
    // 省略部分代码
}

之前将模块化的时候说过,Nest是用了IoC作为模块化管理的,这两个方法就是让我们可以从应用直接找到对应的Provider,也就是业务代码。

get方法定义了从App中获取依赖项(Provider)

const app = await NestFactory.createApplicationContext(AppModule);
const tasksService = app.get(TasksService);

这里默认是从AppModule去查找Provider的,通过select则可以制定模块去找。

const app = await NestFactory.createApplicationContext(AppModule);
const tasksService = app.select(TasksModule).get(TasksService, { strict: true });

独立应用比较简单,可以理解为一般的js脚本,也就是没有transport layer。而需要网络传输的才是重点的使用领域。

Http App和MicroService App

Http App和MicroService App都需要网络服务,每个请求过来时,都会有一个执行上下文。

有两种执行上下文,分别是ArgumentsHostExecutionContext

ArgumentsHost

先看看ArgumentsHost的定义

export declare type ContextType = 'http' | 'ws' | 'rpc';

export interface ArgumentsHost {
    getArgs<T extends Array<any> = any[]>(): T;
    getArgByIndex<T = any>(index: number): T;
    switchToRpc(): RpcArgumentsHost;
    switchToHttp(): HttpArgumentsHost;
    switchToWs(): WsArgumentsHost;
    getType<TContext extends string = ContextType>(): TContext;
}

这个上下文主要定义了不同transport的上下文对象,可以看到,Nest的上下文类型定义了httpwsrpc三种。

http的话只要是request, response, next;ws的话则是定义了getDatagetClient方法rpc定义了getDatagetContext方法

可以通过getType方法获得当前的上下文类型,例如:

if (host.getType() === 'http') {
  // do something that is only important in the context of regular HTTP requests (REST)
} else if (host.getType() === 'rpc') {
  // do something that is only important in the context of Microservice requests
}

当然上下文也可以进行拓展,例如GraphQL的上下文继承了Nest的ExecutionContextHost,然后定义了root, args, context,和info四个属性。

import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host';

export declare class GqlExecutionContext extends ExecutionContextHost implements GraphQLArgumentsHost {
    static create(context: ExecutionContext): GqlExecutionContext;
    getType<TContext extends string = GqlContextType>(): TContext;
    getRoot<T = any>(): T;
    getArgs<T = any>(): T;
    getContext<T = any>(): T;
    getInfo<T = any>(): T;
}
ExecutionContext

ExecutionContext其实是直接继承了ArgumentsHost,然后拓展了获得controller的类和调用的方法。

export interface ExecutionContext extends ArgumentsHost {
    getClass<T = any>(): Type<T>;
    getHandler(): Function;
}
Provider的注入范围(injection scopes)

这里的注入还是指Nest的IoC机制中的注入。
provider的注入范围指的是一种来获得所需的提供者生命周期行为的机制。

有三种:

DEFAULT,默认的注入范围,provider以单例方式在整个应用程序中共享;REQUEST,专门为每个传入请求创建一个新的提供者实例。该实例在请求完成处理后被垃圾回收;TRANSIENT,在注入时都将创建一个新的专用实例。

用法就是在Injectable装饰器作为参数传入进去。

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

@Injectable({ scope: Scope.REQUEST })
export class CatsService {}

在介绍上下文的时候讲到这个注入范围,主要是因为REQUEST范围,有时需要在Provider中获取到请求上下文,这种时候就一定要将Provider定义为REQUEST scope

import { Injectable, Scope, Inject } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { Request } from 'express';

@Injectable({ scope: Scope.REQUEST })
export class CatsService {
  constructor(@Inject(REQUEST) private request: Request) {}
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1321366.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-12
下一篇 2022-06-12

发表评论

登录后才能评论

评论列表(0条)

保存