先前用Nest.js写了一个crud demo,为了更加深入的去学习nest.js,还是要把官方文档走一遍的,今天就先看看控制器,服务和module。
目录
控制器
.env
提供者
依赖注入
控制器可以说是上一个nest.js项目用的最多的一部分了,很多东西我们已经见过了,那这个例子来说:
@Get('ab*cd')
@HttpCode(204)
@Header('Cache-Control', 'none')
findAll() {
return 'This route uses a wildcard';
}
我们已经知道了@Get,这里也一样,*代表通配符,使用正则来定义路由,然后是@HttpCode来添加任意状态码,@Header来定义响应头。
下面这个demo是关于动态路由和拿到动态路由参数的方法:
@Get(':id')
findOne(@Param('id') id): string {
return `This action returns a #${id} cat`;
}
':id'是我们定义动态路由的方法,我们在这里拿到了标记的ID值,同时还把ID通过@Param的方式赋予给了id这个变量。同时我们要注意,这个:id的动态特性是可选的,也就是说我们如果有不需要ID的动态请求,一定要把哪个请求放在这个ID的前面,否则哪个请求可能永远都不会执行。
关于数据对象模型,我们在demo中其实有好好讲过了,这里不再重复,应该知道这是接受客户端参数的工具.
讲到这儿,控制器的东西其实说的差不多了,讲一个之前做小demo一直没注意到的一个东西,就是一些常量,或者是环境变量的问题.
我们可以通过安装:“dotenv”的方式来配置:
$ npm i --save dotenv
在根目录下创建.env,写入我们想要的常量,比如端口:
PORT=3000
import 'dotenv/config'
...
const port = process.env.PORT
...
await app.listen(port);
同时还有个小问题就是我们的运行日志上看不到在哪个端口运行的,我们可以通过添加Logger输出:
....
[Nest] 12028 - 2019-10-01 6:34:18 PM
[RouterExplorer] Mapped {/:id, PUT} route +4ms
[Nest] 12028 - 2019-10-01 6:34:18 PM
[RouterExplorer] Mapped {/:id, DELETE}
route +30ms
[Nest] 12028 - 2019-10-01 6:34:18 PM
[RoutesResolver] InstgramController {/instgram}: +3ms
[Nest] 12028 - 2019-10-01 6:34:18 PM
[RouterExplorer] Mapped {/, GET} route
+16ms
[Nest] 12028 - 2019-10-01 6:34:18 PM
[NestApplication] Nest application successfully started +134ms
[Nest] 12028 - 2019-10-01 6:34:18 PM
App run in http://localhost:3000 +59ms
这个翻译的名字是有点别扭的,但其实也很好懂:
提供者是纯粹的 JavaScript 类,其上方有
@Injectable()
装饰器
比如我们demo中的service:
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}
可以看触,其实provider/service和chontroller都是不难理解的,如果你觉得难的话,那么问题可能就集中在一个东西上了:"Injectable()"
因为这个设计模式的大火是从angular开始的,所以我们用angular的代码来分析一下,不会angular?别着急,我也不会,但是代码还是很简单的。
export class Car {
public engine: Engine;
public tires: Tires;
public description = 'No DI';
constructor() {
this.engine = new Engine();
this.tires = new Tires();
}
// Method using the engine and tires
drive() {
return `${this.description} car with ` +
`${this.engine.cylinders} cylinders and ${this.tires.make} tires.`;
}
}
这段代码是没有依赖注入的,那么这段代码有什么问题呢?大家主要看constructor函数上,可以看到:
Car
类需要一个引擎 (engine) 和一些轮胎 (tire),它没有去请求现成的实例, 而是在构造函数中用具体的Engine
和Tires
类实例化出自己的副本
但如果我们的引擎类升级了,需要传递一个参数应该怎么办呢?
没办法,我们在重新创建engine的构建函数,这样带来的坏处就是car类被我们破坏了,这样让car类显得十分脆弱。那么有什么办法克服呢?
public description = 'DI';
constructor(public engine: Engine, public tires: Tires) { }
如果我们这样写的话会发生什么呢?其实这里借助 TypeScript 的构造器语法来同时定义参数和属性,而car类里面不需要再重新创建engine等类。
依赖注入其实就是这么个到里,让类从外部源中获得它的依赖,而不必亲自创建它们。
但是这样做的话,使用者会加大了很多工作量,因为他们要必须创建所有这三部分了:Car
、Engine
和 Tires。
这时候我们需要一个大类来处理这三个类:
import { Engine, Tires, Car } from './car';
// BAD pattern!
export class CarFactory {
createCar() {
let car = new Car(this.createEngine(), this.createTires());
car.description = 'Factory';
return car;
}
createEngine() {
return new Engine();
}
createTires() {
return new Tires();
}
}
但这样的话也有一个问题,当方法变得更多时,这里的依赖关系会变成一团乱麻。这时注入器就可以发挥其作用了:
let car = injector.get(Car);
具体的实现大家可以继续学习angular,这里的injectable就是这个原理。