likes
comments
collection
share

⚽⚽⚽知道了,这就开始学Nest

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

⚽⚽⚽知道了,这就开始学Nest

阅读本篇文章,你将会学习:

  • middleware(中间件)/guard(守卫)/pipe(管道)/filters(异常过滤器)/interceptor(拦截器)
  • 使用Nest进行typeorm数据库操作
  • 实现增删改查接口并接入校验逻辑
  • 在Nest中操作redis

简介

Nest是一个后端框架,它和koa/express/egg node框架有啥区别?

常用概念

middleware

中间件,一般用于给请求对象增加参数或者token认证

用法示例

  1. 新建nest项目
nest new nest-middleware -p npm
  1. 新建一个middleware Test
nest generate middleware Test

⚽⚽⚽知道了,这就开始学Nest 3. 修改test.middleware.ts

// test.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';

@Injectable()
export class TestMiddleware implements NestMiddleware {
  use(req: any, res: any, next: () => void) {
    console.log('before------------')
    next();
    console.log('after-------------')
  }
}

⚽⚽⚽知道了,这就开始学Nest 4. 在app.module.ts中引入TestMiddleware

import { Module,NestModule,MiddlewareConsumer,RequestMethod } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TestMiddleware } from './test/test.middleware';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule implements NestModule{
  configure(consumer:MiddlewareConsumer){   
    consumer.apply(TestMiddleware).forRoutes('*')
  }
}

forRoutes('*')指的是所有路由都使用TestMiddleware中间件

5.修改app.controller.ts

//app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

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

  @Get('hello')
  getHello(): string {
    console.log('hello')
    return this.appService.getHello();
  }
}

  1. 启动项目
npm run start:dev
  1. 用postman测试

⚽⚽⚽知道了,这就开始学Nest

⚽⚽⚽知道了,这就开始学Nest 可以看到执行顺序是

before------------
hello
after------------

guard

可以配合路由守卫,对用户进行权限验证。守卫决定了路由是否要被处理

执行顺序在middleware之后,interceptor和pipe之前

使用场景:基于ACL的权限认证或基于角色的认证

使用示例

  1. 在上面那个项目里新建guard
nest g guard Auth

⚽⚽⚽知道了,这就开始学Nest 2. 创建一个角色

nest g resource Role

nest g是nest generate的缩写

⚽⚽⚽知道了,这就开始学Nest 3. 在role.controller.ts中启用局部守卫

// role.controller.ts
import { Controller, Get, Post, Body, Patch, Param, Delete,UseGuards } from '@nestjs/common';
import { RoleService } from './role.service';
import { CreateRoleDto } from './dto/create-role.dto';
import { UpdateRoleDto } from './dto/update-role.dto';
import { AuthGuard } from 'src/auth/auth.guard';

@Controller('role')
@UseGuards(new AuthGuard())
export class RoleController {
  ...
}

@UseGurards传入的是AuthGuard类型,而不是实例,这样做就可以把实例化的工作交给Nest并实现依赖注入(DI),由此我们在调用/role开头的接口都会经过AuthGuard处理

上面开启的是局部守卫,全局守卫怎么开启?

so easy~ 直接在main.ts中通过app.useGlobalGurards开启

// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
+++ import { AuthGuard } from './auth/auth.guard';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  +++ app.useGlobalGuards(new AuthGuard())
  await app.listen(3000);
}
bootstrap();

全局守卫和局部守卫的区别就是:全局守卫针对所有接口路由都启用守卫,局部守卫只对应用了对应路由的接口启用守卫

  1. 查看auth.guard.ts文件
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    return true;
  }
}

每个守卫都必须实现一个canActivate方法,返回值是boolean,它的返回值决定当前请求是否被允许(返回值为true就是允许,false是拒绝)

pipe

管道,用于对请求参数进行验证和转换

⚽⚽⚽知道了,这就开始学Nest 如官方文档所示,常用的pipe有以下几种:

  • ValidationPipe 一般和class-validator库一起使用,用于验证请求参数
  • ParseIntPipe
  • ParseBooleanPipe
  • ParseArrayPipe
  • ParseUUIDPipe

使用示例

1.新建nest项目

 nest new nest-pipe-test -p npm

nest cli常用命令汇总

nest new XXX 用于新建一个nest项目,增加-p就是指定包管理工具,可选值有npm/pnpm/yarn

nest generate module/service/controller XXX 用于生成module/service/controller

如果我想直接生成一个包含module,service,controller的文件夹要怎么办?

nest generate resource Test 

⚽⚽⚽知道了,这就开始学Nest

nest build 用于构建项目

  1. 安装依赖
npm i --save class-validator class-transformer
  1. 在main.ts文件中开启全局自动校验
// main.ts
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe());
  await app.listen(3000);
}
bootstrap();

ValidationPipe是从@nestjs/common中引入的,在boostrap中通过app.useGlobalPipes开启全局自动校验

  1. 在app.controller.ts中增加一个Post请求
// app.controller.ts
import { Body, Controller, Get,Post } from '@nestjs/common';
import { AppService } from './app.service';
import { CreateTestDto } from './test/dto/create-test.dto';

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

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

  @Post()
  create(@Body() createTestDto:CreateTestDto){
    return 'This action returns a new test'
  }
}


  1. 在create-test.dto.ts中增加pipe校验
// create-test.dto.ts
import { IsEmail,IsNotEmpty } from "class-validator";
export class CreateTestDto {
    @IsEmail()
    email:string;

    @IsNotEmpty()
    password:string;
}

新增Test增加了两个校验字段,校验装饰器是从class-validator中引入的,@IsEmail()代表的是校验email字段是否符合邮箱格式,@IsNotEmpty代表校验password字段不为空

⚽⚽⚽知道了,这就开始学Nest 6. 启动项目

npm run start:dev
  1. 用postman测试接口

⚽⚽⚽知道了,这就开始学Nest

⚽⚽⚽知道了,这就开始学Nest 可以看到测试成功

filters

一般用于请求响应的异常处理,它是所有抛出异常的统一处理方案,系统有很多内置异常类(对应的状态码也不同):

  • BadRequestException 400
  • UnauthorizedException 401
  • ForbiddenException 403
  • NotFoundException 404
  • NotAcceptableException 406
  • RequestTimeoutException 408
  • ConflictException 409
  • GoneException 410
  • PayloadTooLargeException 413
  • UnsupportedMediaTypeException 415
  • UnprocessableEntityException 422
  • InternalServerErrorException 500
  • NotImplementedException 501
  • BadGatewayException 502
  • ServiceUnavailableException 503
  • GatewayTimeoutException 504

除了内置异常类,还可以自定义异常类

interceptor

用过axios的对它都不陌生,请求拦截器和响应拦截器分别用于拦截请求和响应

响应拦截器可以做的:

  • 根据状态码进行不同的操作,比如404跳转找不到页面,401跳转登录页,500报错服务器内部错误等

整体的流程如下,以用户登录为例,请求user/login接口时,会依次处理middleware->guard->interceptors->pipe->执行方法->interceptors->filters

⚽⚽⚽知道了,这就开始学Nest

typeorm

typeorm是个关系型数据库,后端数据一般都存在这上面,和mysql配合,不会mysql的同学自行查阅文档(相信科班毕业的同学都是会的)

typeorm支持的数据库类型有mysql/oracle/sqllite/postgres等

连接数据库后进行的常用操作无非是增删改查

一般和Nest搭配使用的库是@nestjs/typeorm

具体使用

  1. 创建nest项目
nest new nest-demo -p npm //创建nest项目

项目名为nest-demo,-p npm指的是使用npm作为包管理工具

  1. 安装typeorm依赖
cd nest-demo
npm install @nestjs/typeorm typeorm mysql2 -D
  1. 新建一个User模块
nest g resource User

nest g是nest generate的缩写,用于新建resource

⚽⚽⚽知道了,这就开始学Nest

  1. 在app.module.ts引入User模块和typeorm依赖

此处需要安装mysql,推荐使用mysql-workbench,请自行查找学习使用mysql workbench安装教程

// 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 { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user/entities/user.entity';

@Module({
  imports: [UserModule,TypeOrmModule.forRoot({
    type:'mysql',
    host:'localhost',
    port:3306,
    username:'你的数据库连接用户名',
    password:'你的数据库连接用户密码',
    database:'typeorm_test', // 数据库名
    synchronize:true, // 
    logging:true, // 开启日志
    entities:[User],
    poolSize:10,
    connectorPackage:'mysql2'
  })],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

⚽⚽⚽知道了,这就开始学Nest

  1. 配置User的entity
//user.entity.ts
import { Column,PrimaryGeneratedColumn,Entity } from "typeorm";
@Entity({
    name:'t_user'// 映射的表名
})
export class User {
    @PrimaryGeneratedColumn() // 主键
    id:number;

    @Column({
        length:50,
        comment:'姓名'
    })
    name:string;

    @Column({
        nullable:true,
        comment:"年龄"
    })
    age?:number;
}

我们给用户表定义了三个字段

⚽⚽⚽知道了,这就开始学Nest

字段名含义类型是否可为空
id主键,全局唯一intNo
name姓名varcharNo
age年龄in
  1. 运行项目
npm run start:dev 

等价于执行nest start --watch,在package.json文件中定义了,可以看到建表成功,并执行了sql语句 ⚽⚽⚽知道了,这就开始学Nest

⚽⚽⚽知道了,这就开始学Nest

连接数据库查询,可以看到表

⚽⚽⚽知道了,这就开始学Nest

增删改查

在上面连接typeorm成功后,就可以写业务逻辑,增删改查了

  1. 在User.service.ts中注入EntityManager

+++ 的部分为新增代码

import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
+++ import { InjectEntityManager } from '@nestjs/typeorm';
+++ import { EntityManager } from 'typeorm';

@Injectable()
export class UserService {
  +++ @InjectEntityManager()   
  +++ private manager:EntityManager;

  create(createUserDto: CreateUserDto) {
    return 'This action adds a new user';
  }

  findAll() {
    return `This action returns all user`;
  }

  findOne(id: number) {
    return `This action returns a #${id} user`;
  }

  update(id: number, updateUserDto: UpdateUserDto) {
    return `This action updates a #${id} user`;
  }

  remove(id: number) {
    return `This action removes a #${id} user`;
  }
}

注入EntityManager后就可以调用save/delete来新增修改/删除

  1. 实现具体业务逻辑
import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { InjectEntityManager } from '@nestjs/typeorm';
import { EntityManager } from 'typeorm';
+++ import { User } from './entities/user.entity';

@Injectable()
export class UserService {
  @InjectEntityManager()
  private manager:EntityManager;

  create(createUserDto: CreateUserDto) {
    +++ this.manager.save(User,createUserDto);
  }

  findAll() {
    +++ return this.manager.find(User);
  }

  findOne(id: number) {
   +++ return this.manager.findOne(User,{
   +++   where:{
   +++     id:id
   +++   }
   +++ });
  }

  update(id: number, updateUserDto: UpdateUserDto) {
    +++ this.manager.save(User,{
    +++  id:id,
    +++  ...updateUserDto
    +++})
  }

  remove(id: number) {
    return this.manager.delete(User,id);
  }
}

⚽⚽⚽知道了,这就开始学Nest 3. 用postman测试结果

路径直接在对应的user.controller.ts中查看

⚽⚽⚽知道了,这就开始学Nest

查询示例

等价于执行user.controller.ts这一段,查询所有用户

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

⚽⚽⚽知道了,这就开始学Nest 查看数据库,数据都对应上了,done~

⚽⚽⚽知道了,这就开始学Nest

新增示例

⚽⚽⚽知道了,这就开始学Nest 对应user.controller.ts的这一段,实际就是新增用户

  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return this.userService.create(createUserDto);
  } 

因为我们配置了loggging:true,控制台会打印执行的sql语句

⚽⚽⚽知道了,这就开始学Nest

刷新mysql-workbench,数据新增成功

⚽⚽⚽知道了,这就开始学Nest

修改示例

查看user.controller.ts这一段

  @Patch(':id')
  update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    return this.userService.update(+id, updateUserDto);
  }

等价于根据id更新用户,传入dto(对象数据)

⚽⚽⚽知道了,这就开始学Nest 如图,我在postman填入的是更新id为2的用户,姓名改成哈哈,可以看到控制台输出了sql日志

⚽⚽⚽知道了,这就开始学Nest 刷新用户表,看到数据更新成功

⚽⚽⚽知道了,这就开始学Nest

删除示例

查看user.controller.ts这一段

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.userService.remove(+id);
  }

等价于根据id删除用户,请求方法为delete

⚽⚽⚽知道了,这就开始学Nest 删除了id为3的用户

⚽⚽⚽知道了,这就开始学Nest 查看用户表,数据删除成功

⚽⚽⚽知道了,这就开始学Nest

redis

redis是一个非关系型数据库,它支持缓存,是key-value键值对形式,支持的数据类型有string(字符串)/list(列表)/hash(哈希表)/set(集合)/Zset(sorted set 有序集合)/bitmap(位图)

特点

  1. 速度快

Redis读写基于内存,速度快

  1. 多语言

支持java/c#/python/Node等多种语言

  1. 持久化

支持数据持久化存储,有两种存储方式:

  • RDB 指定时间间隔保存内存中的数据到磁盘中(异步操作),生成数据快照
  • AOF 基于数据变化(写入/删除操作)写入AOF文件
  1. 支持事务/主从复制

5.支持高可用和集群

引入哨兵监听redis服务器的状态

数据类型

string

  • get 获取值
  • mget 获取多个值
  • set 设置值
  • setnx 设置一个值如果不存在
  • incr 用于递增

⚽⚽⚽知道了,这就开始学Nest

⚽⚽⚽知道了,这就开始学Nest

redis可视化GUI推荐使用redis-insight

list

  • lpush(可以理解为left push) 从列表头部增加数据
  • rpush(right push) 从列表末尾增加数据
  • lpop 从列表头部删除数据,并返回这条数据
  • rpop 从列表末尾删除数据,并返回这条数据
  • llen 返回列表长度
  • lmove 元素从一个列表移到另外一个列表
  • ltrim
  • lrange 遍历列表
lrange arr 0 -1 // 遍历列表arr的全部数据,从下标为0的元素开始,-1结束(等价于列表的长度结束)

⚽⚽⚽知道了,这就开始学Nest

set

set和js的es6的set含义是一样的,不重复数据的集合(无序)

  • sadd set添加元素
  • srem 从set删除元素
  • sismember 判断是否是set中的元素
  • sinter
  • scard 返回set的长度

用途

只能用于元素去重,不能用于排序

⚽⚽⚽知道了,这就开始学Nest 判断是否是set中的元素

sismemeber set 1 // 1是否在set中
sismemeber set 0 // 0是否在set中

zset

API

  • zadd 增加数据到zset,会按照score的大小增序排列
  • zrange 取数据
// 格式 zadd key score value
zadd zset1 5 lyllovelemon
zadd zset1 4 hello
zadd zset1 1 qaq
zadd zset1 6 beauty

// zrange key start end 
zrange zset1 0 3 //取zset1前4位数据,下标从0到3,左右闭区间

⚽⚽⚽知道了,这就开始学Nest 查看zset类型数据zset1,可以看到数据是根据score排序的

⚽⚽⚽知道了,这就开始学Nest

zrange 的end超过本身的长度,也是不会报错的,只会取已有的数据(也就是zset类型的所有数据)

⚽⚽⚽知道了,这就开始学Nest

hash

  • hset 设置值
  • hget 获取值
  • hmget 获取多个值
  • hincrby 递增值
hset hash1 key1 1 // 设置
hset hash1 key2 2

hget hash1 key2  // 2

⚽⚽⚽知道了,这就开始学Nest

⚽⚽⚽知道了,这就开始学Nest

下图的可视化工具为redis-cli,请自行下载 ⚽⚽⚽知道了,这就开始学Nest

geo

地理坐标

  • geoadd 设置坐标经纬度
  • geodist 计算两个坐标的距离
  • georadius 计算某个半径内的其他点
geoadd loc 24.666 10.115556 "lyl" 33.5452 60.5313 "lemon"
geodist loc lyl lemon // 计算lyl和lemon的距离
georadius loc 20 50 10 KM // 计算经度20纬度50的其他点,半径为10KM以内

过期处理

  • expire 用于设置redis某个key在规定的时间内过期
  • ttl 查询某个key剩余过期时间
expire lyl 30 // 设置key为lyl的数据30秒后过期,默认单位为秒

⚽⚽⚽知道了,这就开始学Nest

常用功能

redis作为后端必学的中间件,可以支持限流/令牌桶/布隆过滤器/分布式锁等功能

布隆过滤器

用于判断一个元素是否在集合中,而且比哈希表的空间复杂度低很多

一般用于去除垃圾邮件、爬虫网站的判重、解决缓存穿透问题

分布式锁

它可以防止多个线程/进程操作同一个资源,避免数据一致性问题,在分布式系统的高并发场景中确保同一个时间只有一个节点可以访问共享资源

用于并发请求比如秒杀等场景,在电商业务中,商品的库存是固定的,假设很多人秒杀库存只剩1的商品,如果没有分布式锁,可能会导致超卖问题(1个商品卖给多个人)

Nest中使用redis

  1. 初始化nest项目并安装redis
nest new redis-demo -p npm // nest创建新项目,项目名redis-demo,-p npm 指使用的版本管理工具为npm
cd redis-demo // 切换到redis-demo
npm install redis // 安装redis

打开项目可以看到目录结构如下,main.ts是我们的入口文件

⚽⚽⚽知道了,这就开始学Nest 2. 在app.module.ts文件中引入redis

// app.module.ts
/* eslint-disable prettier/prettier */
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import {createClient} from 'redis'; // 引入redis

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService,{
    provide:'REDIS_CLIENT',
    // useFactory是工厂函数
    async useFactory() {
      // 初始化redis连接对象
      const client = createClient({
        socket:{
          host:'localhost',
          port:6379
        }
      });
      // 等待连接 client.connect返回的是promise
      await client.connect();
      // 返回redis连接实例
      return client;
    }
  }],
})
export class AppModule {}
  1. app.service.ts中引入redis,调接口时具体的业务逻辑写在service文件中
// app.service.ts
/* eslint-disable prettier/prettier */
import { Injectable,Inject } from '@nestjs/common';
import {RedisClientType} from "redis";

@Injectable()
export class AppService {
  // 动态创建provider,token为REDIS_CLIENT
  @Inject('REDIS_CLIENT')
  private redisClient:RedisClientType;

  async getHello() {
    // keys('*')指获取redis所有key 
    const value = await this.redisClient.keys('*');
    console.log(value);
    return 'Hello World!';
  }
}

  1. 在对应的controller中也引入redis,controller中用于写增删改查接口
// app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}
  
  // 定义一个get接口,路径为根路径,post接口就用@Post
  @Get()
  async getHello(): Promise<string> {
    // 这个service就是从上面的app.service.ts中引入的
    // 由于service中定义的getHello为async方法,因此这边也要做改动
    return await this.appService.getHello();
  }
}
  1. 启动项目
npm run start:dev

⚽⚽⚽知道了,这就开始学Nest 可以看到项目已经成功启动了,使用postman测试接口

⚽⚽⚽知道了,这就开始学Nest 成功的返回了controller中return的内容,在控制台查看输出,可以看到返回了redis的所有key

⚽⚽⚽知道了,这就开始学Nest

日志系统

Nest内置了日志类Logger,可以从@nestjs/common中获取

一般日志系统有以下几种控制方式:

  • 全局禁用日志
  • 展示特定级别的日志(支持errors/warnings/debug等)
  • 通过扩展自定义日志
  • 和node日志库配合使用(比如winston)

全局禁用日志

实现很简单,在main.ts中配置logger为false即可(默认值为true)

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
    const app = await NestFactory.create(AppModule,{
    logger:false
  });
  await app.listen(3000);
}
bootstrap();

展示特定级别日志

// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule,{
    logger:['error','warn']
  });
  await app.listen(3000);
}
bootstrap();

这样只有报错和告警的日志才会输出

自定义日志

  1. 新建文件MyLogger.ts
// myLogger.ts
import { LoggerService } from '@nestjs/common';

export class MyLogger implements LoggerService {
  /**
   * Write a 'log' level log.
   */
  log(message: any, ...optionalParams: any[]) {}

  /**
   * Write a 'fatal' level log.
   */
  fatal(message: any, ...optionalParams: any[]) {}

  /**
   * Write an 'error' level log.
   */
  error(message: any, ...optionalParams: any[]) {}

  /**
   * Write a 'warn' level log.
   */
  warn(message: any, ...optionalParams: any[]) {}

  /**
   * Write a 'debug' level log.
   */
  debug?(message: any, ...optionalParams: any[]) {}

  /**
   * Write a 'verbose' level log.
   */
  verbose?(message: any, ...optionalParams: any[]) {}
}
  1. 在main.ts中使用自定义类MyLogger.ts
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { MyLogger } from './myLogger';

async function bootstrap() {
  const app = await NestFactory.create(AppModule,{
    logger:new MyLogger()
  });
  await app.listen(3000);
}
bootstrap();

以上方法用于自定义所有的日志类,你也可以只自定义某一个级别的日志

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

export class MyLogger extends ConsoleLogger {
  error(message: any, stack?: string, context?: string) {
    super.error(...arguments);
  }
}

认证

一般和@nestjs/jwt一起使用实现jwt认证

示例项目

  1. 新建nest项目
$ nest new jwt-test -p npm
  1. 新建auth的module/controller/service
$ nest g module auth
$ nest g controller auth
$ nest g service auth
  1. 新建user的module/service
$ nest g module user
$ nest g service user
  1. 在user.service.ts中实现findOne
// user.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class UserService {
    private readonly users = [
        {
            id:1,
            username:'lyllovelemon',
            // 真实项目密码不能明文传输,一般和加密算法库一起使用
            password:'123456'
        },
        {
            id:2,
            username:'柠檬酱',
            password:'qaqqaq'
        },
        {
            id:3,
            username:'lowcode',
            password:'lowcode_vue'
        }
    ]
    async findOne(username:string){
        return this.users.find(user=>user.username === username)
    }
}

定义了一个findOne方法用于根据用户名查找用户信息

在真实项目中,数据一般保存在数据库中,此处为了使用方便,直接硬编码users变量

  1. 修改user.module.ts
// user.module.ts
import { Module } from '@nestjs/common';
+++ import { UserService } from './user.service';

@Module({
  providers: [UserService],
  +++ exports:[UserService]
})
export class UserModule {}

  1. 在auth.service.ts中实现singIn登录方法
// auth.service.ts
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { UsersService } from '../users/users.service';

@Injectable()
export class AuthService {
  constructor(private usersService: UsersService) {}

  async signIn(username: string, pass: string): Promise<any> {
    const user = await this.usersService.findOne(username);
    if (user?.password !== pass) {
      throw new UnauthorizedException();
    }
    const { password, ...result } = user;
    return result;
  }
}
  1. 在AuthModule中导入UserModule
// auth.module.ts
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { UserModule } from '../users/users.module';

@Module({
  imports: [UserModule],
  providers: [AuthService],
  controllers: [AuthController],
})
export class AuthModule {}
  1. 在AuthController中定义登录接口
// auth.controller.ts
import { Body, Controller,HttpCode,HttpStatus,Post } from '@nestjs/common';
import { AuthService } from './auth.service';

@Controller('auth')
export class AuthController {
    constructor(private authService:AuthService){}

    @HttpCode(HttpStatus.OK)
    @Post('login')
    signIn(@Body() signInDto:Record<string,any>){
        return this.authService.signIn(signInDto.username,signInDto.password);
    }
}

  1. 安装jwt依赖
$ npm install --save @nestjs/jwt
  1. 在AuthService中引入jwt
// auth.service.ts
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { UserService } from 'src/user/user.service';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthService {
    constructor(
        private userService:UserService,
        private jwtService:JwtService
    ){}

    async signIn(username:string,pass:string):Promise<any>{
        const user = await this.userService.findOne(username);
        // 密码不对抛出未认证错误
        if(user?.password !== pass){
            throw new UnauthorizedException();
        }
        const payload = {sub:user.id,username:user.username}
        return {
            access_token:await this.jwtService.signAsync(payload)
        }
    }
}

11.在auth目录下新建常量

// auth/constant.ts
export const jwtConstants={
    secret: 'DO NOT USE THIS VALUE. INSTEAD, CREATE A COMPLEX SECRET AND KEEP IT SAFE OUTSIDE OF THE SOURCE CODE.',
}
  1. 在authModule中引入jwtConstants常量
import { Module } from '@nestjs/common';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { UserModule } from 'src/user/user.module';
import { JwtModule } from '@nestjs/jwt';
import { jwtConstants } from './constant';

@Module({
  imports:[
    UserModule,
    JwtModule.register({
      global:true,
      secret:jwtConstants.secret,
      signOptions:{expiresIn:'60s'}
    })
  ],
  controllers: [AuthController],
  providers: [AuthService],
  exports:[AuthService]
})
export class AuthModule {}

13.启动项目后用postman测试

npm run start:dev

⚽⚽⚽知道了,这就开始学Nest ⚽⚽⚽知道了,这就开始学Nest

Nest相关的内容很多,一篇没法写完,后续还会陆续更新

参考文档

Nest官方文档

Redis文档

@nestjs/typeorm npm

转载自:https://juejin.cn/post/7321634253909590070
评论
请登录