本网站(662p.com)打包出售,且带程序代码数据,662p.com域名,程序内核采用TP框架开发,需要联系扣扣:2360248666 /wx:lianweikj
精品域名一口价出售:1y1m.com(350元) ,6b7b.com(400元) , 5k5j.com(380元) , yayj.com(1800元), jiongzhun.com(1000元) , niuzen.com(2800元) , zennei.com(5000元)
需要联系扣扣:2360248666 /wx:lianweikj
NestJS-01:providers自定义、动态生成
我是陈晓 · 117浏览 · 发布于2023-07-12 +关注

前言

最近在学nestjs,涉及基础概念的东西记录一下,顺便分享下来;大部分内容来自于nestjs官网,加上自己的实践和部分理解,希望对大家有帮助。

话不多说,直接开整!

一、providers是什么、怎么用

一般node后端的项目结构会这么划分几个文件夹,如controller、service、router;

constroller:对应路由的处理器,比如定义遇到了/user的get请求,我将请求分发给谁处理;这里的谁通常指的是service兄弟,那可以直接把逻辑写在controller里面吗,可以!但不建议;

service:字面意思服务,一般对应各种单独的业务服务,比如用户相关的作为一个service,里面获取所有用户信息作为service的一个方法,比如前面的/user获取所有用户信息,要调用对应的service.getAllUser()

router:设置请求路径指向哪个控制器

回到文章,我们的providers正对应中间的service,每一个provider通常对应一个service作为服务的提供者;当然provider也不一定就是service,也可以是一个函数,是一个对象;

在nestjs中,定义一个provider用@Injectable装饰器,如下代码,此时我的CatService就是一个provider,你看形式,是不是很像service;

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

@Injectable()
export class CatService {
  findAll() {
    return [{ name: 'cat service' }];
  }
}


二、标准的provider

我们先创建项目,在终端nest new project-name,然后cd project-name,最后运行yarn start:dev;通过nest-cli创建的项目,会帮我们生成好app模块;然后我们在src目录下里面新建cat.controller.ts、cat.service.ts,代码如下

cat.controller.ts文件如下

import { Controller, Get } from '@nestjs/common';
import { CatService } from './cat.service';

@Controller('/cat')
export class CatController {
  constructor(private readonly catService: CatService) {}
  @Get()
  getAll() {
    return this.catService.findAll();
  }
}

提一嘴,构造器那里是ts的一个语法糖,具体参考这个例子

class SampleClass {
    private foo: string

    constructor(_foo: string) {
        this.foo = _foo;
    }
}

class SampleClass {
    constructor(private foo: string) {}
}


cat.service.ts如下

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

@Injectable()
export class CatService {
  findAll() {
    return [{ name: 'cat service' }];
  }
}

最后在app模块里面的controller和providers引用我们的cat控制器和provider,然后启动项目,访问http://localhost:3000/cat 就能看到浏览器正常输出了

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CatModule } from './cat/cat.module';

@Module({
  imports: [],
  controllers: [AppController, CatController],
  providers: [AppService, CatService],
})
export class AppModule {}


这样是可以了,但是如果来cat2、cat3、cat4、每个都这么引用,是不是不太好,所以再改进,我们使用@Module维护功能类似的controller、provider作为一个模块,然后引用整个模块;

新建一个文件夹cat,将我们上面创建的cat.controller.ts、cat.service.ts放进cat文件夹中,然后新建cat.module.ts并写入下面代码

cat.module.ts

import { Injectable, Module } from '@nestjs/common';
import { CatController } from './cat.controller';
import { CatService } from './cat.service';

@Module({
  controllers: [CatController],
  providers: [CatService],
})
export class CatModule {}

最后修改app.module.ts的代码,引用我们的cat模块;重新启动项目后,发现效果是一样的,但代码的可维护性变高了!

@Module({
  imports: [CatModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}


三、自定义provider注入和使用

自定义provider的几种方式先列一下,主要下面几种

  • useClass:@Injectable修饰的class

  • useValue:直接使用一个我们定义好的变量

  • useFactory:好用!,可以动态返回provider的具体内容

  • useExisting:别名复用已经存在的provider

要想自定义provider,首先我们要换一下Module里面的providers注入写法,由原本的直接CatService,变成一个对象;provide类似一个标识,可以填类,也可以填写字符串;

useClass表示使用哪个service;如果是其他Service,要先注册到privders数组中,

@Module({
    // providers: [CatService]
    providers: [
        {
            provide: CatService,
            useClass: CatService
        }
    ]
})


provide为字符串的情况,要修改controller的注入方式

@Module({
  // providers: [CatService],
  providers: [
      {
          provide: "nest-cat",
          useClass: CatService
      }
  ]
})

// cat.controller.ts文件修改如下
import { Controller, Get, Inject } from '@nestjs/common';
import { CatService } from './cat.service';

@Controller('/cat')
export class CatController {
  constructor(@Inject('nest-cat') private readonly catService: CatService) {}
  @Get()
  getAll() {
    return this.catService.findAll();
  }
}


字符串和类的的区别: 如果是字符串,nestjs这时候并不知道catService对应哪个具体值,需要提前手动用@Inject('token')注入,表示用哪个provide;

如果是类的话,则直接声明catService的类型是CatService类;ts会根据构造器函数的类型,进行依赖注入(relect metadata)

  • 字符串首先需要手动@Inject('对应的表示标识'),

  • 类需要声明对应的类型

useValue,返回任意一个值;比如你嫌弃还要写一个catService,你就直接返回一个对象,包含findAll方法,然后给controller用;也是可以的

{
    provide: CatService,
    useValue: {
        findAll() {
            return [{ name: 'use value' }]
        }
    }
}


useFactory这个就更好用了,可以动态返回provider的具体内容;举个场景,我想根据项目配置,来返回AService或者B Service;inject表示的useFactory需要注入的依赖,按顺序放在函数的参数位置上;

ps:我在我具体的项目用的是,封装一个Redis模块,然后等待连接,返回redis连接实例;

TomCatService,
{
  provide: CatService,
  inject: [TomCatService],
  useFactory: async (tomCatService: TomCatService) => {
    return tomCatService;
    // return {
    //   findAll: () => {
    //     return [{ name: 'use factory' }];
    //   },
    // };
  },
},

// 使用useClass也可以完成类似效果
{
      provide: CatService,
      useClass: process.env.NODE_ENV === 'development' ? CatService : TomCatService,
},


或者异步请求其他数据,然后再返回其他service,提供服务;试下面的例子,会发现每个请求大概要等3s;

{
  provide: CatService,
  // 这里的意思是,每个请求创建不同实例,进来一个等待3s
  scope: Scope.REQUEST,
  useFactory: async () => {
    await new Promise((resolve) => setTimeout(resolve, 3000));
    return new CatService();
  },
},


useExisting:别名注入依赖,用的不多,简单看下用法

{
  provide: CatService,
  useClass: CatService,
},
{
  // 定义表示,然后在controller那里注入
  provide: 'AliasCatService',
  useExisting: CatService,
},

// cat.controller.ts
@Controller('/cat')
export class CatController {
  constructor(
      @Inject('AliasCatService') private readonly catService: CatService,
  ) {}
  @Get()
  getAll() {
    return this.catService.findAll();
  }
}


其他补充:如果用到了其他的provider,需要在当前模块里面的providers先引入,在进行使用;

NestJs的Provider类型

image.png

export type Provider<T = any> = Type<any> | ClassProvider<T> | ValueProvider<T> | 
FactoryProvider<T> | ExistingProvider<T>;

// 对应我们的类构造器
export interface Type<T = any> extends Function {
    new (...args: any[]): T;
}

export interface ClassProvider<T = any> {
    provide: InjectionToken;
    useClass: Type<T>;
    // 对应复用实例 or 每次都创建一个新的实例 or 等请求进来再创建,默认是app启动复用创建
    scope?: Scope;
    inject?: never;
    // 是否惰性生成实例
    durable?: boolean;
}

export interface ValueProvider<T = any> {
   
    provide: InjectionToken;
   
    useValue: T;
  
    inject?: never;
}

export interface FactoryProvider<T = any> {
   
    provide: InjectionToken;
  
    useFactory: (...args: any[]) => T | Promise<T>;
   
    inject?: Array<InjectionToken | OptionalFactoryDependency>;
   
    scope?: Scope;
    
  
    durable?: boolean;
}

export interface ExistingProvider<T = any> {
    provide: InjectionToken;
   
    useExisting: any;
}


小结

这一章主要记录了nestjs的provider的基本使用,自定义用法,还有一些代码类型注释;




相关推荐

PHP实现部分字符隐藏

沙雕mars · 1325浏览 · 2019-04-28 09:47:56
Java中ArrayList和LinkedList区别

kenrry1992 · 908浏览 · 2019-05-08 21:14:54
Tomcat 下载及安装配置

manongba · 970浏览 · 2019-05-13 21:03:56
JAVA变量介绍

manongba · 963浏览 · 2019-05-13 21:05:52
什么是SpringBoot

iamitnan · 1087浏览 · 2019-05-14 22:20:36
加载中

0评论

评论
我是一名在上海一家互联网公司上班,专注技术开发工作等。
小鸟云服务器
扫码进入手机网页