在xxx.controller.ts文件中,我们都会有如下的装饰器,比如app.controller.ts:
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
// ......
}
AppController类通过@Controller装饰器来装饰,表示它可以进行依赖注入,由Nest内部的IoC容器接管。
接着比如app.service.ts:
@Injectable()
export class AppService {
//......
}
AppService类通过@Injectable进行装饰,表示这个类可以被注入,注意@Injectable不单是可以装饰service(服务)类,包括过滤器(Filter)、拦截器(Intercepter)、提供者(Provide)、网关(Gateway)等等,在没有特殊情况下,Nest中所需要依赖注入的模块都可以使用@Injectable进行标记,以便在IoC容器中进行收集和管理。
为什么控制器是单独使用
@Controller来装饰?控制器(Controller)只用于处理请求,不作为依赖对象被其他对象组件注入,我们其实可以把控制器看做是消费者,而服务(Service)和中间件(Middleware)则是提供者
这些组件会在AppModule中进行引入
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
@Module装饰器在Nest中用于定义模块,这些模块包含需要注入的组件。
控制器仅仅只能作为消费者被注入,而提供者(Providers),比如服务(Service),既能作为依赖被注入,也可以注入到其他依赖对象中。
除此之外,在Nest中实现模块化管理非常简单。imports属性用于引入其他模块,这有助于实现功能逻辑的分组和重用。比如我们之前的UserModule,在我们使用了nest g resource user命令之后,在AppModule中就自动引入了UserModule。
@Module({
imports: [UserModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
在User模块中,默认是这样的:
@Module({
imports: [],
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
UserController类中:
@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {}
@Get()
findAll(): string {
return this.userService.findAll();
}
}
UserService类中:
@Injectable()
export class UserService {
findAll(): string {
return 'This action returns all users';
}
}
属性注入
上面的代码,无论是AppController,还是UserController中,都是是通过构造器注入的UserService,除此之外,还能通过属性注入
@Controller('user')
export class UserController {
+ @Inject(UserService)
+ private userService: UserService;
// constructor(private readonly userService: UserService) {}
@Get()
findAll(): string {
console.log('user controller');
return this.userService.findAll();
}
}
provider 注入
前面我们就简单提了一下provider是作为提供者,其实提供的就是可以注入的内容,比如我们前面是这样写的:
@Module({
imports: [UserModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
意思就是AppService可以被注入,这里其实是一种简写,完整的写法应该是下面这个样子:
@Module({
imports: [UserModule],
controllers: [AppController],
providers: [
{
provide: AppService,
useClass: AppService,
},
],
})
export class AppModule {}
通过 provide 指定 token,通过 useClass 指定对象的类,Nest 会自动对它做实例化后用来注入。
AppController 的构造器里参数里声明了 AppService 的依赖,就会自动注入
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
// ......
}
如果不想用构造器注入,也可以属性注入
@Controller('admin')
export class AppController {
@Inject(AppService)
private appService: AppService;
// ......
}
通过 @Inject 指定注入的 provider 的 token 即可。
在构造器参数里指定 AppService 的依赖的时候不是没有指定 token吗?那是因为我们直接把AppService 这个class本身作为了token
这个token只是一个标识,因此,也可以是一个字符串,比如:
@Module({
imports: [UserModule],
controllers: [AppController],
providers: [
{
provide: 'app_service',
useClass: AppService,
},
],
})
export class AppModule {}
那么,无论是构造器参数注入,还是属性注入,我们就必须引入这个字符串的token
@Controller()
export class AppController {
@Inject('app_service')
private appService: AppService;
}
或者:
@Controller()
export class AppController {
constructor(@Inject('app_service') private readonly appService: AppService) {}
}
用字符串或者 class 做 token 的 provider,都可以正确被注入到目标对象。用 class 做 token 可以省去@Inject,比较简便而已。
直接注入值
既然provider可以直接指定一个class,那其实也能指定一个值,比如
@Module({
imports: [UserModule],
controllers: [AppController],
providers: [
{
provide: 'app_service',
useClass: AppService,
},
{
provide: 'car',
useValue: {
brand: 'BYD',
price: 100000,
},
},
],
})
export class AppModule {}
那么,在controller中,我们就可以直接注入这个’car‘
@Controller()
export class AppController {
@Inject('app_service')
private appService: AppService;
@Inject('car')
private car: { brand: string; price: number };
@Get('car')
hello(): string {
return `Hello, I have a ${this.car.brand} car, it's price is ${this.car.price}`;
}
}
动态注入值
provider 的值可能是动态产生的,Nest 也同样支持,通过工厂的方式,其实也就是函数的方式注入
const createRandomFactory = () => Math.random();
@Module({
imports: [UserModule],
controllers: [AppController],
providers: [
{
provide: 'app_service',
useClass: AppService,
},
{
provide: 'car',
useValue: {
brand: 'BYD',
price: 100000,
},
},
{
provide: 'random',
useFactory: createRandomFactory,
},
],
})
export class AppModule {}
这样,在app.controller.ts中,也可以注入这个random
@Controller()
export class AppController {
// ......
@Inject('random')
private random: number;
@Get('random')
randomNum(): number {
return this.random;
}
}
而且,useFactory既然可以是函数,那么当然可以给他传递参数,而且注意一个问题,我们所有的对象都是通过Nest容器帮我们管理的,所以,我们甚至可以写成下面这个样子:
const createRandomFactory = (
car: { brand: string; price: number },
appService: AppService,
) => {
return {
random: Math.random(),
brand: car.brand,
hello: appService.getHello(),
};
};
现在这个createRandomFactory函数有两个参数了,其实意思很明显,需要将具体的值传入到方法中去,但是值从哪里来?不错,可以直接通过容器给我们注入进来,因为car和app_service这两个之前我们都是有注册的。
所以,接下来的写法:
const createRandomFactory = (
car: { brand: string; price: number },
appService: AppService,
) => {
return {
random: Math.random(),
brand: car.brand,
hello: appService.getHello(),
};
};
@Module({
imports: [UserModule],
controllers: [AppController],
providers: [
{
provide: 'app_service',
useClass: AppService,
},
{
provide: 'car',
useValue: {
brand: 'BYD',
price: 100000,
},
},
{
provide: 'random',
useFactory: createRandomFactory,
inject: ['car', 'app_service'],
},
],
})
export class AppModule {}
通过 inject注入了两个token,'car'和 'app_service'这两个我们在之前都已经注册到了容器中




