vue+nest 开发笔记

106 阅读4分钟

快速nestjs框架搭建

npm i -g @nestjs/cli nest new project-name

NestJS实现Restful API

// Param:restful api参数
// Query:url 参数
// body:post 参数

import { Controller, Get,Post,Put,Delete,Param,Query,Body } from '@nestjs/common';
import { AppService } from './app.service';

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

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

  // Get:获取数据
  // post 插入数据
  // put 更新
  // delete 删除
 // http://localhost:300/data/10
  @Get('/data/:id')
  getData(@Param() param):string{
    return 'data'
  }

  @Get('/data')
  getAllData(){
    return 'get all data'
  }

  // http://localhost:300/data?id=10
  @Post('/data')
  addData(@Body() body,@Query() query){
    console.log(body,query)
    return 'add data:' + JSON.stringify(body) + ',id=' + query.id 
  }

  @Put('/data')
  updateData(){
    return 'update data'
  }

  @Delete('/data/:id')
  deleteData(){
    return 'delete data'
  }
}

模块

创建模块

nest g module user

创建控制器

nest g controller user

连接数据库mysql TypeORM

MySQL8安装+数据库导入

npm install --save @nestjs/typeorm typeorm mysql2

app.modele.ts 引入

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type:'mysql',
      host:'127.0.0.1',
      port:3306,
      username:'root',
      password:'123456',
      database:'vben-book-dev',
    }),
    UserModule, 
    AuthModule, 
    BookModule],
  controllers: [AppController, BookController],
  providers: [AppService],
})

数据库实体Entity创建

//数据库实体Entity创建 手动创建
import {Entity,Column, Unique, PrimaryGeneratedColumn} from 'typeorm';

@Entity('admin_user')
export class User{
    @PrimaryGeneratedColumn()
    id:number;

    @Column()
    @Unique(['username'])
    username:string;

    @Column()
    password:string;

    @Column()
    avatar:string;

    @Column()
    role:string;

    @Column()
    nickname:string;

    @Column()
    active:number;

}

image.png

MySQL表关联和查询逻辑实现

第一步 创建服务层 service
// 这里是手动创建  不过可以命令创建  nest g service module/user
import {InjectRepository} from '@nestjs/typeorm';
import {User} from './user.entity';
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';

@Injectable()
export class UserService{
    constructor(@InjectRepository(User) private readonly usersRepository:Repository<User>){}

    findOne(id:number):Promise<User>{
        return this.usersRepository.findOneBy({id})
    }
}
第二步 在模块导入
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { TypeOrmModule} from '@nestjs/typeorm';
import {User} from './user.entity';
import { UserService } from './user.service';

@Module({
  imports:[TypeOrmModule.forFeature([User])],
  controllers: [UserController],
  providers:[UserService]
})
export class UserModule {}
第三步 在控制层 请求数据
import { Controller, Param,Get,ParseIntPipe } from '@nestjs/common';
import {UserService} from './user.service'

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

    @Get(':id')
    getUser(@Param('id',ParseIntPipe) id:number){
        return this.userService.findOne(id);
    }
}

image.png

有个看不懂的返回

await 一下

image.png

数据库新增和删除逻辑实现

image.png

image.png

image.png

NestJS请求守卫开发

image.png

image.png

image.png

image.png

实现登录鉴权接口的调用链路

image.png

image.png

image.png

登录密码校验逻辑实现

加密依赖

npm i md5

JWT基本概念讲解

Token 本质是字符串,用于请求时附带在请求头中,检验请求是否合法及判断用户身份

npm install -S @nestjs/jwt

import {JwtModule} from '@nestjs/jwt';

// 创建模块 nest g module module/auth
@Module({
  // 实现登录鉴权接口的调用链路 第一步 引入模块
  imports:[
    UserModule,
    JwtModule.register({
      global:true,
      secret:'abcdefg', // 密钥
      signOptions:{expiresIn:30 * 40 * 60 *60 + 's'}  // 过期时间
    })
  
  ],

生成token

image.png

image.png

image.png

统一返回

export function successCount(data,count,msg){
    return {
        code:0,
        result:data,
        message:msg,
        count,
    };
}

export function success(data,msg){
    return{
        code:0,
        result:data,
        message:msg
    }
}

export function error(msg) {
    return {
      code: -1,
      message: msg,
    };
  }
  
  export function wrapperResponse(p, msg) {
    return p
      .then((data) => success(data, msg))
      .catch((err) => error(err.message));
  }
 // 加上注解 允许被请求
    @Public()
    @Post('login')
    // 接口进行统一拦截器处理
    @UseFilters(new HttpExceptionFilter())
    async login(@Body() body){
        return  this.authService.login(body.username,body.password)
            .then((data)=> success(data,'登录成功'))
            .catch((err)=> error("登录失败"))
    }

image.png

image.png

JsonWebTokenError: invalid signature

获取用户信息API开发

import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable } from 'rxjs';
import { IS_PUBLIC_KEY } from './public.decorator';
import { JwtService } from '@nestjs/jwt';
import { JWT_SECRET_KEY } from './auth.jwt.secrect';

@Injectable()
// 第二步 实现一个接口
export class AuthGuard implements CanActivate{
  constructor(private jwtService:JwtService,private reflector:Reflector){}
  async canActivate(context: ExecutionContext): Promise<boolean> {
    // 请求可以被处理之前,可以这里处理 验证是否登录
   // console.log('canActive',context)
   // 第四步 通过API通过IS_PUBLIC_KEY 判断是否允许被请求
    const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY,[
      context.getHandler(),
      context.getClass(),
    ]);
    // 如果为true 允许被调用
    if(isPublic){
      return true;
    }
    // 获取request对象
    const request = context.switchToHttp().getRequest();
    console.log("6456",request.headers.authorization)
    const token = request.headers.authorization
   
    if(!token){
      throw new UnauthorizedException()
    }
    try {
      const user = this.jwtService.verify(token)
      console.log(user)

      request['user'] = user;
      
    }catch(e){
      console.log(e)
      throw new UnauthorizedException();
    }
    //解析token
    return true;
  }

}


function extractTokenFromHeader(request){
  const [type, token] = request.headers.authorization?.split(' ') ?? [];
  return type === 'Bearer' ? token : '';
}

image.png

服务层执行sql

findAll() {
    const sql = 'select * from menu order by id asc';
    return this.menuRepository.query(sql);
  }

// 新增

create(body) {
    return this.menuRepository.save(body.data || body);
  }
 @Post()
  create(@Body() body) {
    return wrapperResponse(
      this.menuService.create(body),
      '菜单创建成功',
    );
  }

image.png

image.png

修改菜单

update(body) {
    const id = body?.data?.id || body.id;
    const data = body.data || body;
    return this.menuRepository.update(id, data);
  }
  
  @Put()
  update(@Body() body) {
    return wrapperResponse(
      this.menuService.update(body),
      '菜单更新成功',
    );
  }

image.png

根据id查询book Param

   getBook(id){
        const sql = `SELECT * FROM book WHERE id="${id}"`
        return this.repository.query(sql);
    }
    
      // http://localhost:3000/book/1  Param
    @Get(':id')
    getBook(@Param('id',ParseIntPipe) id){
        return wrapperResponse(
            this.bookService.getBook(id),
            '查询电子书成功'
        )
    }

新增

Field 'updateType' doesn't have a default value

updateType 字段不能为空

  async addBook(params){
        const {title,author,fileName,category,categoryText,cover,language,publisher,rootFile} = params;

        const insertSql = `INSERT INTO book(
            title,
            author,
            fileName,
            category,
            categoryText,
            cover,
            language,
            publisher,
            rootFile
        )VALUES(
         '${title}',
         '${author}',
         '${fileName}',
         '${category}',
         '${categoryText}',
         '${cover}',
         '${language}',
         '${publisher}',
         '${rootFile}'
        )`;
        return this.repository.query(insertSql);
    }
    
    @Post()
    insertBook(@Body() body){
        return wrapperResponse(
            this.bookService.addBook(body),
            '新增电子书成功'
        )
    }

更新

image.png

image.png

async updateBook(params){
        const { id, title, author, category, categoryText, language, publisher } = params;
        const setSql = [];
        if(title){
            setSql.push(`title="${title}"`);
        }

        if(author){
            setSql.push(`author="${author}"`);
        }
        const updateSql = `UPDATE book SET ${setSql.join(',')} WHERE id=${id}` ;
        return this.repository.query(updateSql);
    }
    
     @Put()
    updateBook(@Body() body){
        return wrapperResponse(
            this.bookService.updateBook(body),
            '更新电子书成功'
        )
    }

删除

async deleteBook(id){
        const deleteSql = `DELETE FROM book WHERE id = ${id}`
        return this.repository.query(deleteSql);
    }
    
@Delete()
    deleteBook(@Body() body){
        console.log(body)
        return wrapperResponse(
            this.bookService.deleteBook(body.id),
            '删除电子书成功'
        )
    }

分页功能 @Query

image.png

 //图书列表分页组件开发
    async getBookList(params:any = {}){
        let page = +params.page || 1;
        let pageSize = +params.pageSize || 20;
        const {title = '',author = ''} = params;

        if(page <= 0){
            page =1;
        }
        if(pageSize <= 0){
            pageSize = 20;
        }

        let where = 'where 1=1';
        if(title){
            where += ` AND title LIKE '%${title}%'`;
        }
        if(author){
            where += ` AND author LIKE '%${author}%'`;
        }
        // const categoryAuth = await this.getCategoryAuth(userid);
        // if (categoryAuth.length > 0) {
        // where += ` AND categoryText IN (${categoryAuth.join(',')})`;
        // }
        const sql = `select * from book ${where} limit ${pageSize} offset ${(page - 1) * pageSize}`;
        const res = await this.repository.query(sql);
        return res
    }
    
     async countBookList(params: any = {}) {
        const { title = '', author = '' } = params;
        let where = 'where 1=1';
        if (title) {
          where += ` AND title LIKE '%${title}%'`;
        }
        if (author) {
          where += ` AND author LIKE '%${author}%'`;
        }
        // const categoryAuth = await this.getCategoryAuth(userid);
        // if (categoryAuth.length > 0) {
        //   where += ` AND categoryText IN (${categoryAuth.join(',')})`;
        // }
        const sql = `select count(*) as count from book ${where}`;
        return this.repository.query(sql);
      }
      
      // 控制器
       // 分页查询book列表
    @Get()
    getBookList(@Query() params){
        return wrapperCountResponse(
            this.bookService.getBookList(params),
            this.bookService.countBookList(params),
            '获取图片列表成功',
        )
    }