Nest 项目小实践之注册登陆

复杂的项目还需要一些内容,mysql 做关系型数据库、redis 做缓存和临时数据存储、minio 做 OSS 服务、docker 和 docker compose 做部署、typeorm 做 ORM 框架

目前可以实现这样的功能

新建

sql 复制代码
nest new book-management-system-backend

跑起来

arduino 复制代码
npm run start:dev

先实现下登录、注册

创建一个 user 模块

sql 复制代码
nest g resource user --no-spec

会自动引入

在 User下的Constructor 添加注册接口

less 复制代码
@Controller('user')
export class UserController {
    constructor(private readonly userService: UserService) { }
    
    @Post('register')
    register(@Body() registerUserDto: RegisterUserDto) {
    console.log(registerUserDto);
    return '完成注册';
  }

新建dto/register-user.dto.ts

typescript 复制代码
export class RegisterUserDto {
    username: string;
    password: string;
}

需要对参数校验

在 main.ts 里全局启用 ValidationPipe

arduino 复制代码
app.useGlobalPipes(new ValidationPipe())

安装需要的包

arduino 复制代码
npm install --save class-transformer class-validator

dto/register-user.dto.ts

less 复制代码
import { IsNotEmpty, MinLength } from "class-validator";

export class RegisterUserDto {
    @IsNotEmpty({ message: '用户名不能为空!' })
    username: string;

    @IsNotEmpty({ message: '密码不能为空!' })
    @MinLength(3, { message: '密码至少需要 3 位'})
    password: string;
}

如果在 ValidationPipe 指定 transform: true 之后,就会转为 dto 的实例

创建一个 db 模块 先用json 代替数据库存储

arduino 复制代码
nest g module db 
nest g service db

希望 DbModule 用的时候可以传入 json 文件的存储路径

db.module.ts

typescript 复制代码
import { DynamicModule, Module } from '@nestjs/common';
import { DbService } from './db.service';

export interface DbModuleOptions {
  path: string
}

@Module({})
export class DbModule {
  static register(options: DbModuleOptions ): DynamicModule {
    return {
      module: DbModule,
      providers: [
        {
          provide: 'OPTIONS',
          useValue: options,
        },
        DbService,
      ],
      exports: [DbService]
    };
  }
}

在 register 方法里接收 options 参数,返回 providers、exports 等模块配置

把传入的 options 用 useValue 来声明为 provider,token 为 OPTIONS

在 DbService 里实现下 read、write 方法

typescript 复制代码
import { Inject, Injectable } from '@nestjs/common';
import { DbModuleOptions } from './db.module';
import { access, readFile, writeFile } from 'fs/promises';

@Injectable()
export class DbService {

    @Inject('OPTIONS')
    private options: DbModuleOptions;

    async read() {
        const filePath  = this.options.path;

        try {
            await access(filePath)
        } catch(e) {
            return [];
        }

        const str = await readFile(filePath, {
            encoding: 'utf-8'
        });
        
        if(!str) {
            return []
        }

        return JSON.parse(str);
        
    }

    async write(obj: Record<string, any>) {
        await writeFile(this.options.path, JSON.stringify(obj || []), {
            encoding: 'utf-8'
        });
    }
}

在 UserController 里调用下 UserService 的 register 方法

less 复制代码
@Controller('user')
export class UserController {
    constructor(private readonly userService: UserService) { }
    
    @Post('register')
    async register(@Body() registerUserDto: RegisterUserDto) {
        return this.userService.register(registerUserDto);
    }

UserService 需要加一个 register 方法

typescript 复制代码
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import { RegisterUserDto } from './dto/register-user.dto';
import { DbService } from 'src/db/db.service';
import { User } from './entities/user.entity';

@Injectable()
export class UserService {
  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`;
  }
    
    @Inject(DbService)
    dbService: DbService;

    async register(registerUserDto: RegisterUserDto) {
        const users: User[] = await this.dbService.read();
        
        const foundUser = users.find(item => item.username === registerUserDto.username);

        if(foundUser) {
            throw new BadRequestException('该用户已经注册');
        }

        const user = new User();
        user.username = registerUserDto.username;
        user.password = registerUserDto.password;
        users.push(user);

        await this.dbService.write(users);
        return user;
    }
}

注入 DbService 来读写数据。

首先读取出 users 的数据,如果找到当前 username,那就返回 400 的响应提示用户已注册。

否则创建一个新的用户,写入 user.json 文件中(此处代替数据库)

user.entity.ts

typescript 复制代码
export class User {
    username: string;
    password: string;
}

再加一个用户

重复注册 报错 也没问题

类似的流程 写登陆

user.controller.ts

less 复制代码
    @Post('login')
    async login(@Body() loginUserDto: LoginUserDto) {
    return this.userService.login(loginUserDto);
    }

user.service.ts

javascript 复制代码
    async login(loginUserDto: LoginUserDto) {
    const users: User[] = await this.dbService.read();

    const foundUser = users.find(item => item.username === loginUserDto.username);

    if(!foundUser) {
        throw new BadRequestException('用户不存在');
    }

    if(foundUser.password !== loginUserDto.password) {
        throw new BadRequestException('密码不正确');
    }

    return foundUser;
    }

login-user.dto.ts

less 复制代码
import { IsNotEmpty, MinLength } from "class-validator";

export class LoginUserDto {
    @IsNotEmpty({ message: '用户名不能为空!' })
    username: string;

    @IsNotEmpty({ message: '密码不能为空!' })
    @MinLength(3, { message: '密码至少需要 3 位'})
    password: string;
}
相关推荐
Profile排查笔记10 分钟前
JavaScript 实现浏览器指纹生成:基础字段、Canvas 采样与 SHA-256 摘要
前端·人工智能·后端·自动化
不可能片场11 分钟前
Electron 子进程退化成 node 的 env 陷阱
前端·electron
搬砖记录员16 分钟前
录屏总黑屏?90% 的人没关这个开关——浏览器硬件加速冲突排查指南
前端·音视频开发
promiseThen19 分钟前
我用 7 条铁律管住 Cursor:让 AI 写代码不再「自由发挥」
前端·ai编程
用户712828968446720 分钟前
NestJS 发布v12版本,看一下有哪些改动
nestjs
梨想橙汁36 分钟前
JS 核心语法:运算符流程控制、函数、数组与对象实战详解
前端·javascript
七牛开发者36 分钟前
拆解 dsh:Turn 与 Step 如何组织 Agent 主循环
前端·javascript·人工智能
梨想橙汁37 分钟前
JavaScript 零基础入门:引入方式、变量与数据类型,吃透原始与引用类型
前端·javascript
不可能片场41 分钟前
puppeteer 调试实录:CDP 双层响应让取值永远 None
前端·electron
咖啡无伴侣42 分钟前
2. 从零搭建企业级 Monorepo 工程化模板:ESLint 10 (基础骨架)+ Prettier 配置与避坑指南
前端·架构