likes
comments
collection
share

快速入门 Nestjs(一)

作者站长头像
站长
· 阅读数 33

介绍

  • Nestjs 是一个用于构建高效可扩展的一个基于 Nodejs 服务端应用程序开发框架。完全支持 ts ,结合了 AOP 面向切面的编程方式
  • 英文官网 中文网站
  • 内置框架 Express(默认),维二内置框架 Fastify

设计模式:IOC 控制反转 DI 依赖注入

控制反转(IOC)

  • 控制反转是一种设计原则,目的是将对象的创建和依赖关系的管理从代码中分离出来。传统的编程方式是对象主动去获取它所需要的依赖,而控制反转则是由外部容器来管理对象的创建和依赖的注入,这样可以使代码更加模块化、可测试和可维护

依赖注入(DI)

  • 依赖注入是实现控制反转的一种具体方式。通过依赖注入,组件所需的依赖对象由外部提供,而不是组件自己创建。依赖注入可以通过构造函数注入、属性注入或者方法注入来实现
// 未使用控制反转和依赖注入的代码
class A {
    name: string
    constructor(name: string) {
        this.name = name
    }
}
class B {
    age:number
    entity:A
    constructor (age:number) {
        this.age = age;
        this.entity = new A('小满')
    }
}
 
const c = new B(18)
c.entity.name
// 使用了 IOC 容器
class A {
    name: string
    constructor(name: string) {
        this.name = name
    }
}
 
 
class C {
    name: string
    constructor(name: string) {
        this.name = name
    }
}
//中间件用来收集依赖,用于解耦
class Container {
    modeuls: any
    constructor() {
        this.modeuls = {}
    }
    provide(key: string, modeuls: any) {
        this.modeuls[key] = modeuls
    }
    get(key) {
        return this.modeuls[key]
    }
}
 
const mo = new Container()
mo.provide('a', new A('小满1'))
mo.provide('c', new C('小满2'))
 
class B {
    a: any
    c: any
    constructor(container: Container) {
        this.a = container.get('a')
        this.c = container.get('c')
    }
}
 
new B(mo)

前置知识:装饰器

装饰器是一种特殊的类型声明(一个函数),他可以附加在类,方法,属性,参数上面

  • 类装饰器:把构造函数传入到装饰器的第一个参数 target
function decotators (target:any) {
    target.prototype.name = '小满'
}
 
@decotators
class Xiaoman {
    constructor () {
    }
}
 
const xiaoman:any = new Xiaoman()
console.log(xiaoman.name) // 小满
  • 属性装饰器:返回两个参数: 原型对象、属性的名称
const currency: PropertyDecorator = (target: any, key: string | symbol) => {
    console.log(target, key) // {} name
}
 
class Xiaoman {
    @currency
    public name: string
    constructor() {
        this.name = ''
    }
    getName() {
        return this.name
    }
}
  • 参数装饰器:返回三个参数:原型对象、方法的名称、参数的位置(从0开始)
const currency = (target: any, key: string | symbol,index:number) => {
    console.log(target, key,index) // {} getName 1
}
 
class Xiaoman {
    public name: string
    constructor() {
        this.name = ''
    }
    getName(name:string, @currency age:number) {
        return this.name
    }
}
  • 方法装饰器:返回三个参数:原型对象、方法的名称、属性描述符(可写对应writable,可枚举对应enumerable,可配置对应configurable)
const currency: MethodDecorator = (target: any, key: string | symbol, descriptor:any) => {
    // {} getName {
    //   value: [Function: getName],  value 即是对应的方法 getName
    //   writable: true,
    //   enumerable: false,
    //   configurable: true
    // }
    console.log(target, key, descriptor)
}
 
class Xiaoman {
    public name: string
    constructor() {
        this.name = ''
    }
    @currency
    getName(name:string,age:number) {
        return this.name
    }
}

装饰器:实现一个 get 请求

  • 简单了解下,后续 nestjs 写法和这类似
import axios from 'axios'
 
const Get = (url: string): MethodDecorator => {
    return (target, key, descriptor: PropertyDescriptor) => {
        const fnc = descriptor.value;
        axios.get(url).then(res => {
            fnc(res, {
                status: 200,
            })
        }).catch(e => {
            fnc(e, {
                status: 500,
            })
        })
    }
}
 
//定义控制器
class Controller {
    constructor() {
 
    }
    @Get('https://api.apiopen.top/api/getHaoKanVideo?page=0&size=10')
    getList (res: any, status: any) {
        console.log(res.data.result.list, status)
    }
  
}

Nestjs 脚手架

  • 通过 cli 创建 Nestjs 项目
npm i -g @nestjs/cli

nest new [项目名称]
  • 使用npm run start:dev启动,具备热更新,简单访问地址http://localhost:3000/

目录介绍

快速入门 Nestjs(一)

  • dist 文件夹是运行时就会打包生成的

  • .spec.ts 是测试用的文件

  • .controller.ts 是控制器,类似 vue 的路由

    • private readonly appService: AppService 这一行代码就是依赖注入不需要实例化,appService 内部会自己实例化
  • .module.ts 是模块文件,Nestjs 使用模块打包特定功能,每个模块是高度封装的,只暴露必要的接口,它可以包含一些组件,如控制器、服务等

  • .service.ts 是实现业务逻辑的文件,当然也可以放在控制器文件里实现,但拿出来是为了实现复用

  • main.ts 入口文件

    • 通过 NestFactory.create(AppModule) 创建一个app
    • app.listen(3000) 监听一个端口

Nestjs cli 常用命令

  • nest --help 可以查看 Nestjs 所有命令

快速入门 Nestjs(一)

// 生成 controller.ts
nest g co user

// 生成 module.ts
nest g mo user

// 生成 service.ts
nest g s user

// 直接生成一个 CURD
nest g res user
  • 生成的文件结构如下
  • dto 文件是用来定义数据传输对象的,用于验证请求体中的数据或者控制返回的数据格式
  • entities 文件是后续连接数据库后用来表示数据库中的表的结构

快速入门 Nestjs(一)

RESTful 风格设计

RESTful 是一种软件架构风格、设计风格,也是一种开发规范,其核心是面向资源(Resource)进行设计

HTTP 方法(GET、POST、PUT、DELETE,etc.)作为通用接口方法,被用来对资源进行操作,可以表示对资源的增删改查。

例如一个用户资源的 CRUD 操作的 RESTful 设计可能是:

  • 创建用户:POST /users
  • 获取用户:GET /users/{id}
  • 更新用户:PUT /users/{id}
  • 删除用户:DELETE /users/{id}

即用一个接口完成对资源的增删改查,只是通过不同的请求方式来区分。上面生成的 user.controller.ts 文件中就是这样实现的

RESTful 也可以实现版本控制,在接口路由前加上 v1 标识,例如http://localhost:3000/v1/user

当然,这仅是一种设计风格,开发中可以不按照这个风格来写,大部分服务端接口还是 get post 请求,定义不同的接口来实现 CRUD

Nestjs 控制器

在编程中,控制器是一种设计模式,通常在实现模型-视图-控制器(MVC)架构时使用

在NestJS框架中,控制器主要负责接收特定路由的请求,根据请求进行处理,然后返回响应。主要表现是@Controller()修饰的类

快速入门 Nestjs(一)

常见的控制器(我自己理解就是一类功能的组合):

  • CRUD控制器:处理基本的Create、Read、Update和Delete操作请求
  • Auth控制器:负责处理与用户身份验证相关的请求
  • User控制器:管理与用户账号相关的请求,比如获取用户信息、更新用户信息等
  • File控制器:处理与文件上传、下载相关的请求
  • Admin控制器:管理与管理员用户相关的请求,例如管理员登陆、管理用户账号等

控制器中常见的参数装饰器

装饰器能力
@Request()req
@Response()res
@Next()next
@Session()req.session
@Param(key?: string)req.params / req.params[key]
@Body(key?: string)req.body / req.body[key]
@Query(key?: string)req.query / req.query[key]
@Headers(name?: string)req.headers / req.headers[name]
@HttpCode控制接口返回的状态码
import { Controller, Get, Post, Param, Request, Body } from '@nestjs/common';
import { UserService } from './user.service';

@Controller('user')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Get()
  findAll(@Request() req) {
    console.log(req); // 输出的是req对象,里面有query参数
    return { code: 200, messge: 'get请求' };
  }

  @Get(':id')
  findOne(@Param('id') id) {
    console.log(id); // 读取的动态id值
    return { code: 200 };
  }

  @Post()
  create(@Body() body) {
    console.log(body);  // post 请求的body信息,类似 { id: 11111, name: '小阿' }
    return { code: 200, messge: 'post请求' };
  }
}

Session 实例

跳转博客

Nestjs 提供者

Provider 只是一个用 @Injectable() 装饰器注释的类

快速入门 Nestjs(一)

  • 基本用法:在模块 .module.ts 文件中引入 service,在 providers 注入

快速入门 Nestjs(一)

  • 在 .controller.ts 文件就可以使用注入好的 service

快速入门 Nestjs(一)

service 第二种用法(自定义名称)

  • 上面那种写法是预发糖
  • 第二种写法
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
 
@Module({
  controllers: [UserController],
  providers: [{
    provide: "Xiaoman", // 自定义名称
    useClass: UserService
  }]
})
export class UserModule { }
  • 自定义名称后,需要用对应的 Inject 取用,不然找不到
import { Controller, Inject } from '@nestjs/common';
import { UserService } from './user.service';

@Controller('user')
export class UserController {
  constructor(@Inject('Xiaoman') private readonly userService: UserService) {}
}
  • 自定义注入值
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
 
@Module({
  controllers: [UserController],
  providers: [{
    provide: "Xiaoman",
    useClass: UserService
  }, {
    provide: "JD",
    useValue: ['TB', 'PDD', 'JD']
  }]
})
export class UserModule { }
import { Controller, Inject } from '@nestjs/common';
import { UserService } from './user.service';

@Controller('user')
export class UserController {
  constructor(
      @Inject('Xiaoman') private readonly userService: UserService,
      @Inject('JD') private shopList: string[]
  ) {}
}

工厂模式

  • 如果服务之间有相互的依赖或者逻辑处理,可以使用 useFactory
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserService2 } from './user.service2';
import { UserService3 } from './user.service3';
import { UserController } from './user.controller';
 
@Module({
  controllers: [UserController],
  providers: [{
    provide: "Xiaoman",
    useClass: UserService
  }, {
    provide: "JD",
    useValue: ['TB', 'PDD', 'JD']
  },
    UserService2,
  {
    provide: "Test",
    inject: [UserService2],
    useFactory(UserService2: UserService2) {
      return new UserService3(UserService2)
    }
  }
  ]
})
export class UserModule { }
import { Controller, Inject } from '@nestjs/common';
import { UserService } from './user.service';

@Controller('user')
export class UserController {
  constructor(
      @Inject('Xiaoman') private readonly userService: UserService,
      @Inject('JD') private shopList: string[],
      @Inject('Test') private readonly Test: any,
  ) {}
}

异步模式

import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserService2 } from './user.service2';
import { UserService3 } from './user.service3';
import { UserController } from './user.controller';
 
@Module({
  controllers: [UserController],
  providers: [{
    provide: "Xiaoman",
    useClass: UserService
  }, {
    provide: "JD",
    useValue: ['TB', 'PDD', 'JD']
  },
    UserService2,
  {
    provide: "Test",
    inject: [UserService2],
    useFactory(UserService2: UserService2) {
      return new UserService3(UserService2)
    }
  },
  {
    provide: "sync",
    async useFactory() {
      return await  new Promise((r) => {
        setTimeout(() => {
          r('sync')
        }, 3000)
      })
    }
  }
  ]
})
export class UserModule { }

Nestjs 模块

每个 Nest 应用程序至少有一个模块,即根模块。根模块是 Nest 开始安排应用程序树的地方

基本用法

  • 当使用 nest g res user创建一个新的CURD模块时,nestjs 会自动帮我们引入模块

快速入门 Nestjs(一)

共享模块

  • 例如 user 的 Service 想暴露给其他模块使用就可以使用 exports 导出该服务
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';

@Module({
  controllers: [UserController],
  providers: [UserService],
  exports: [UserService],
})
export class UserModule {}
  • 由于 App.moudles 已经引入过该模块,就可以直接使用了
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
import { UserService } from './user/user.service';

@Controller()
export class AppController {
  constructor(
    private readonly appService: AppService,
    private readonly userService: UserService,
  ) {}

  @Get()
  getHello(): string {
        return this.userService.findAll(); // 自动生成的模块里有这个方法
  }
}

全局模块

  • 给 user 模块添加 @Global() 他便注册为全局模块
import { Global, Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';

@Global()
@Module({
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}
  • 在其他模块使用时无须在module文件 import 导入
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
import { UserService } from './user/user.service';

@Controller()
export class AppController {
  constructor(
    private readonly appService: AppService,
    private readonly userService: UserService,
  ) {}

  @Get()
  getHello(): string {
    return this.userService.findAll();
  }
}

动态模块

动态模块主要就是为了给模块传递参数 可以给该模块添加一个静态方法用来接受参数

  • 创建一个 config.module.ts
import { Module, DynamicModule, Global } from '@nestjs/common'
 
interface Options {
    path: string
}
 
@Global()
@Module({
})
export class ConfigModule {
    static forRoot(options: Options): DynamicModule {
        return {
            module: ConfigModule,
            providers: [
                {
                    provide: "Config",
                    useValue: { baseApi: "/api" + options.path }
                }
            ],
            exports: [
                {
                    provide: "Config",
                    useValue: { baseApi: "/api" + options.path }
                }
            ]
        }
    }
} 
  • 在 app.module.ts 文件引入
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './user/user.module';
import { ListModule } from './list/list.module';
import { ConfigModule } from './config/config.module';

@Module({
  imports: [UserModule, ListModule, ConfigModule.forRoot({
    path: '/xiaoman'
  })],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
  • 因为是全局的动态模块,可以在任意模块使用
import { Controller, Get, Inject } from '@nestjs/common';
import { UserService } from './user.service';

@Controller('user')
export class UserController {
  constructor(
    private readonly userService: UserService,
    @Inject('Config') private readonly base: any,
  ) {}

  @Get()
  findAll() {
    return this.base; // 接口请求返回 {"baseApi": "/api/Xiaoman"}
  }
}

Nestjs 中间件

中间件是在路由处理程序之前调用的函数,中间件函数可以访问请求和响应对象

中间件函数可以执行以下任务:

  • 执行任何代码。
  • 对请求和响应对象进行更改。
  • 结束请求-响应周期。
  • 调用堆栈中的下一个中间件函数。
  • 如果当前的中间件函数没有结束请求-响应周期, 它必须调用 next() 将控制传递给下一个中间件函数。否则, 请求将被挂起。

创建一个依赖注入中间件

import {Injectable,NestMiddleware } from '@nestjs/common'
import {Request,Response,NextFunction} from 'express'
 
@Injectable()
export class Logger implements NestMiddleware{
  use (req:Request,res:Response,next:NextFunction) {
    console.log(req)
    next()
  }
}
  • 使用方法在模块里面,实现configure返回一个消费者consumer,通过apply注册中间件,通过forRoutes指定Controller路由
import { Module,NestModule,MiddlewareConsumer } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { Logger } from 'src/middleware';
@Module({
  controllers: [UserController],
  providers: [UserService],
  exports:[UserService]
})
export class UserModule implements NestModule{
  configure (consumer:MiddlewareConsumer) {
    consumer.apply(Logger).forRoutes('user')
  }
}
  • 也可以指定拦截的方法,比如拦截GET POST 等 forRoutes 使用对象配置
consumer.apply(Logger).forRoutes({path:'user',method:RequestMethod.GET})
  • 甚至可以直接把 UserController 塞进去
consumer.apply(Logger).forRoutes(UserController)

全局中间件

  • 全局中间件只能使用函数模式,案例可以做白名单拦截之类的
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

const whiteList = ['/list']
 
function middleWareAll  (req,res,next) {
   
     console.log(req.originalUrl,'我收全局的')
 
     if(whiteList.includes(req.originalUrl)){
         next()
     }else{
         res.send('小黑子露出鸡脚了吧')
     }
}
 
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.use(middleWareAll)
  await app.listen(3000);
}
bootstrap();

接入第三方中间件

  • 例如 cors 处理跨域
npm install cors
npm install @types/cors -D
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as cors from 'cors'
 
const whiteList = ['/list']
 
function middleWareAll  (req,res,next) {
   
     console.log(req.originalUrl,'我收全局的')
 
     if(whiteList.includes(req.originalUrl)){
         next()
     }else{
         res.send({code:200})
     }     
}
 
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.use(cors())
  app.use(middleWareAll)
  await app.listen(3000);
}
bootstrap();
转载自:https://juejin.cn/post/7371312967780827146
评论
请登录