Nest使用指南
nest是用于构建高效可扩展的一个基于node服务端应用开发框架,完全支持typescript,结合AOP。
nest还是一个springMVC风格,依赖注入、控制反转都借鉴了Angular,底层运用了express和fastify。以下都是在express框架上进行总结。
0、核心概念
控制反转IOC和依赖注入DI主要是解决类与类之间的耦合问题,通过一个Container容器解决问题,在容器里注入类,然后在其他类中使用。
class A {
name:string
constructor(name:string){
this.name = name
}
}
class C{
name:string
constructor(name:string){
this.name = name
}
}
class Container{
mo:any
constructor(name:string){
this.mo = {}
}
provide(key:string,mo:any){
this.mo[key] = mo
}
get(key:string){
return this.mo[key]
}
}
const mo = new Container()
mo.provide("a", new A("xxxx"))
mo.provide("b", new C("yyyy"))
class B {
a:any
c:any
constructor(mo:Container){
this.a = mo.get("a")
this.c = mo.get("c")
}
}
1、基础命令
- nest --help
| 命令 | 目录位置 | 文件名称 |
|---|---|---|
| nest g co demo | /demo/demo.controller.ts | controller.ts |
| nest g mo demo | /demo/demo.module.ts | module.ts |
| nest g s demo | /demo/demo.service.ts | service.ts |
| nest g resource user | /user/user.controller.ts /user/user.module.ts /user/user.service.ts | controller.ts module.ts service.ts |
-
版本控制
- 启用全局版本控制
// main.ts async function bootstrap() { const app = await NestFactory.create(AppModule); app.enableVersioning({ type: VersioningType.URI, }); await app.listen(3000); } //controller.ts @Controller({ version: '2', })- 启用模块版本单个控制
//controller.ts @Get() @Version('3') getHello(): string { return this.appService.getHello(); }
2、控制器Controller
return和@Res()的区别:当使用@Res的装饰器的时候,其实是调用了底层express框架的能力,这个时候不能使用return返回值了。return和@Res()没有本质的区别,但是后期如果转fastify框架时,使用原生的return代码改动量会小。
- Get
@Get()
@function(@Request() req){
req.query //前端参数
}
//Get参数语法糖
@Get()
@function(@Query() query){
query //前端参数
}
- Post
@Post()
@function(@Request() req){
req.body //前端参数
}
//Post参数语法糖
@Post()
@function(@Body() body){
body //前端参数
}
//可以直接指定读取的返回参数
@Post()
@function(@Body("name") body){
body //前端参数,这个body就是name这个值,原来是个对象{name:"xxx"}
}
- Get动态参数
@Get(":id")
@function(@Request() req){
req.params //前端参数
}
//Get动态参数语法糖
@Get(":id")
@function(@Param() params){
params //前端参数
}
3、Session和Cookie
使用session的前提是要有cookie。
//main.ts
import * as session from 'express-session'
app.use(session({secret:"YTH",rolling:true,name:"yth.sid",cookie:{maxAge:999999}}))
@Get()
@function(@Request() req,@Session session){
session.code = "xxx" //设置session的值
console.log(session.code) //读取session的值
}
4、Providers
Providers本质上就是提供出一堆service.ts服务,实现全局或者共享模块。
-
Providers自定义名称
1.在app.modules.ts中,providers参照以下写法
2.在app.controller.ts中,用@Inject修饰名称
// app.modules.ts
@Module({
providers: [AppService],
})
@Module({
providers: [{
provide:"RandomName",
useClass:AppService
}],
exports: ['RandomName'],
})
//app.controller.ts
@Controller()
export class AppController {
constructor(@Inject('RandomName') private readonly appService: AppService) {}
}
注意:providers里的provide如果是字符串"RandomName",exports的时候也是字符串"RandomName",对应在controller里使用Inject("RandomName")。但是还有一种情况,这样就可以直接使用,但要小心在module里的exports和imports的形式。
// app.modules.ts
@Module({
providers: [{
provide:AppService,
useClass:AppService
}],
exports: [AppService],
})
//app.controller.ts
export class AppController {
constructor(private readonly appService: AppService) {}
}
- Providers自定义值
// app.modules.ts
@Module({
providers: [AppService],
})
@Module({
providers: [{
provide:"RandomName",
useValue:["a","b","c"]
}],
})
//app.controller.ts
@Controller()
export class AppController {
constructor(@Inject('RandomName') private readonly abc: string[]) {}
@Get()
getHello(): string {
return this.abc; // ["a","b","c"]
}
}
5、模块Module
- user.service 作为共享模块
1、在user.module exports[user.service]
// user.module.ts
@Module({
controllers: [UserController],
providers: [UserService],
exports: [UserService],
})
2、在list.module imports[user.module]
//list.module.ts
@Module({
imports: [UserModule],
controllers: [ListController],
providers: [ListService],
})
3、在list.controller 依赖注入 user.service
//list.controller.ts
export class ListController {
constructor(
private readonly listService: ListService,
private readonly userService: UserService,
) {}
}
- user.service 作为全局模块
1、在user.module exports[user.service]
2、在user.module 加上@Global装饰器
// user.module.ts
@Global()
@Module({
controllers: [UserController],
providers: [UserService],
exports: [UserService],
})
3、在list.controller 依赖注入 user.service
//list.controller.ts
export class ListController {
constructor(
private readonly listService: ListService,
private readonly userService: UserService,
) {}
}
- user.service 作为动态模块,支持参数传递
下面这个例子就是一个全局动态模块,支持传参数,参数可以在controller和service中使用。核心就是@Inject('UserServiceOptions') private readonly options: UserServiceOptions
//user.service.ts
@Injectable()
export class UserService {
constructor(
@Inject('UserServiceOptions') private readonly options: UserServiceOptions,
) {}
findAll() {
return `${this.options.apiURL} This action returns all list`; //options 就是动态参数
}
}
//user.controller.ts
@Controller('user')
export class UserController {
constructor(
private readonly userService: UserService,
@Inject('UserServiceOptions') private readonly options: UserServiceOptions,
) {}
@Get('list')
async getUsers() {
console.log('options', this.options); //options 就是动态参数
return this.userService.findAll();
}
}
//user.module.ts
@Global()
@Module({})
export class UserModule {
static forRoot(options: UserServiceOptions): DynamicModule {
return {
module: UserModule,
controllers: [UserController],
providers: [
UserService,
{
provide: 'UserServiceOptions',
useValue: options,
},
],
exports: [UserService],
};
}
}
//app.module.ts
@Module({
controllers: [AppController],
providers: [AppService],
imports: [UserModule.forRoot(userServiceOptions), ListModule],
})
6、中间件MiddleWare
- 全局
定义一个全局中间件,在main.ts里进行注册,全局中间件不同于局部中间件是一个class,全局中间件是一个函数。
1.写中间件函数
2.使用app.use(“xxxx”) 注册
//main.ts
function MiddleWareAll(req: Request, res: Response, next: NextFunction) {
next();
}
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(MiddleWareAll);
await app.listen(3000);
}
- 局部
定义一个中间件,在需要使用这个中间件的module.ts文件里进行注册,比如logger中间件我要用在list里,对使用/list的路由进行AOP。
1.注册中间件,最好使用express的类型声明覆盖原来的any
2.在module.ts中将logger引用进来,然后实现configure函数
// logger.middleware.ts
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
next();
}
}
// list.module.ts
import { LoggerMiddleware } from '../logger.middleware';
export class ListModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes('list'); //1.会拦截所有/list的路由
consumer.apply(LoggerMiddleware).forRoutes({ //2.会拦截/list的路由的GET请求
path:"list",
method:RequestMethod.GET
});
consumer.apply(LoggerMiddleware).forRoutes(ListController); //3.会拦截所有/list的路由
}
}
- 图片上传
- 日志
7、其他
- 静态目录
- 管道