NestJs的基础使用

news2024/11/17 23:59:57

初始化项目

创建项目

// 安装脚手架(只需要安装一次,因为这个是全局的)
npm i -g @nestjs/cli
// 创建项目
nest new project-name
// (该过程有个选择包管理工具的,我选的yarn)

启动项目

yarn run start:dev
// 可以在浏览器访问localhost:3000  输出helloWorld

控制器和路由

查看命令

nest - g;
// 列表描述(name:生成的文件名,alias:简写文件名,description:文件的描述)

生成controller层命令为

// 要使用CLI创建控制器,只需执行$ nest g controller [生成的文件夹名]命令。
// 也可以使用简写
nest g co [生成的文件夹名]
// 然后查看src下面的+创建的文件夹
// 项目创建完成查看app.module.ts里面会自动将创建的controller注入进来

在新创建的controller内添加以下内容,确保能被访问到

import { Controller, Get } from '@nestjs/common';
// 这里有个小坑(也不算坑,这个Get在我电脑不会自动导入,一度导致我以为报错了,长个记性)
// 配置了下面的之后启动项目访问:localhost:3000/cat 会返回一个JSON对象(数组和对象自动转JSON)
@Controller('cat')
export class CatController {
  @Get() //不写参数默认为空,直接方位/cat就能访问到这个接口
  say() {
    return {
      name: 'cat',
    };
  }
  @Get('/say2') //访问localhost:3000/cat/say2 会返回一个数组(被json转化过的)
  say2() {
    return [1, 2, 3, 4, 5];
  }
}

生成service层命令为

// 要使用CLI创建控制器,只需执行$ nest g service [生成的文件夹名]命令。
// 也可以使用简写
nest g s [生成的文件夹名] // 建议和controller放在同一文件夹
// 然后查看src下面的+创建的文件夹
// 项目创建完成查看app.module.ts里面会自动将创建的service注入进来

controller和service结合使用

// controller改造如下
import { Controller, Get } from '@nestjs/common';
import { CatService } from './cat.service';

@Controller('cat')
export class CatController {
  // 私有的,只读的service
  constructor(private readonly catService:CatService){}
  @Get()
  say() {
    return this.catService.say();
  }
  @Get('/say1')
  say1() {
    return this.catService.say1();
  }
}

// service改造如下
import { Injectable } from '@nestjs/common';

@Injectable()
export class CatService {
  say() {
    return {
      name: 'catservice',
    };
  }
  say1() {
    return {
      name: [1, 2, 3, 4, 5, 6, 7],
    };
  }
}
// http://localhost:3000/cat/say1 返回{name:[1,2,3,4,5,6,7]}

路由介绍

官方路由示例

import { Controller, Get, Query, Post, Body, Put, Param, Delete } from '@nestjs/common';
import { CreateCatDto, UpdateCatDto, ListAllEntities } from './dto';

@Controller('cats')
export class CatsController {
  @Post()
  create(@Body() createCatDto: CreateCatDto) {
    return 'This action adds a new cat';
  }

  @Get()
  findAll(@Query() query: ListAllEntities) {
    return `This action returns all cats (limit: ${query.limit} items)`;
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return `This action returns a #${id} cat`;
  }

  @Put(':id')
  update(@Param('id') id: string, @Body() updateCatDto: UpdateCatDto) {
    return `This action updates a #${id} cat`;
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return `This action removes a #${id} cat`;
  }
}

本人这里还是以上面个人创建的controller和service为例

以下全是catcontroller.ts内容(都是针对不同接口类型接收参数的介绍)
import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Post,
  Put,
  Query,
} from '@nestjs/common';
import { CatService } from './cat.service';

@Controller('cat')
export class CatController {
  // 私有的,只读的service
  constructor(private readonly catService: CatService) {}

  // 1.@Query
  // 浏览器访问: http://localhost:3000/cat/say?name=%E5%B0%8F%E7%BA%A2&age=18
  // 打印如下:query { name: '小红', age: '18' }
  // 无参数时打印为: query {}
  @Get('/say')
  say(@Query() query: any) {
    // 限定参数类型为any
    console.log('query', query);
    return this.catService.say();
  }

  // 2.动态路由 :id
  // 浏览器访问:http://localhost:3000/cat/99
  // 打印如下: id1 99
  @Get(':id')
  say1(@Param('id') id: number) {
    console.log('id1', id);
    return this.catService.say1();
  }

  // 3.动态路由 /say3/:id
  // 浏览器访问:http://localhost:3000/cat/say3/99
  // 打印如下: id3 99
  @Get('/say3/:id')
  say3(@Param('id') id: string) {
    console.log('id3', id);
    return this.catService.say1();
  }

  // 4.动态路由 无参形式
  // 浏览器访问:http://localhost:3000/cat/say4/99
  // 打印如下: param {id:'99'}
  @Get('/say4/:id')
  // Param参数为空
  say4(@Param() param: { id: number }) {
    console.log('param', param);
    return this.catService.say1();
  }

  // 5.Delete 删除接口
  // APIFOX访问:http://localhost:3000/cat/say5/1 接口类型换成:Delete
  // 打印如下: param { id: '1' }
  @Delete('/say5/:id')
  // Param参数为空
  say5(@Param() param: { id: number }) {
    console.log('param', param);
    return this.catService.say1();
  }

  // 6.Put 新增接口
  // APIFOX访问:http://localhost:3000/cat/say5/1 接口类型换成:Put
  // 打印如下: param { id: '1' }
  @Put('/say6/:id')
  // Param参数为空
  say6(@Param() param: { id: number }) {
    console.log('param', param);
    return this.catService.say1();
  }

  // 7.Post 新增接口
  // APIFOX访问:http://localhost:3000/cat 接口类型换成:Post
  // 参数在接口测试工具的body的json类型加入以下参数: { id: 1, name: '萧寂' }
  // 打印如下: cat { id: 1, name: '萧寂' }
  @Post()
  say7(@Body() cat: any) {
    console.log('cat', cat);
    return this.catService.say1();
  }
}

提供者(也就是Java中的实体类)

在上面创建的cat文件夹内创建interface文件夹,在文件夹内创建cat.interface.ts文件
在上面创建的cat文件夹内创建dto文件夹,在文件夹内创建create-cat.dto.ts文件

// cat.interface.ts
export interface Cat {
  name: string;
  age: number;
  breed: string;
}

// create-cat.dto.ts
import { Cat } from "../interface/cat.interface";
export class CreateCatDto implements Cat{
  name: string;
  age: number;
  breed: string;
}

// cat.controller.ts
import {
  Body,
  Controller,
  Post,
} from '@nestjs/common';
import { CatService } from './cat.service';
import { CreateCatDto } from './dto/create-cat.dto';

@Controller('cat')
export class CatController {
  // 私有的,只读的service
  constructor(private readonly catService: CatService) {}
  // APIFOX访问:http://localhost:3000/cat 接口类型换成:Post
  // 参数在接口测试工具的body的json类型加入以下参数: { id: 1, name: '萧寂' }
  // 打印如下: cat { id: 1, name: '萧寂' }
  @Post()
  say(@Body() cat: CreateCatDto) {
    console.log('cat', cat); // 在apiFox打印结果和上面一样,只是加了类型校验,但是由于ts只进行类型约束,并不要求参数限定必须一致,因此这里请求的参数与dao和接口类不一致也可以拿到参数,并不会报错,只是不存在的参数不会对其类型进行校验而已了
    return this.catService.say1();
  }
}

中间件

创建中间件

nest g mi logger
// 安装完成后在目录中会多出一个logger文件夹

//在app.mudule.ts放入以下内容进行注册绑定中间件
import { MiddlewareConsumer, Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CatController } from './cat/cat.controller';
import { CatService } from './cat/cat.service';
import { LoggerMiddleware } from './logger/logger.middleware';

@Module({
  imports: [],
  controllers: [AppController, CatController],
  providers: [AppService, CatService],
})
export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    // 下面这段代码代表先绑定中间件再应用到路由上,当访问cat或者带cat子接口时会被拦截到
    consumer.apply(LoggerMiddleware).forRoutes('cat');
    // 由于同一个接口名可能会有get,put,delete,post等方法,因此可以使用下面方式对指定类型的接口进行拦截
    consumer.apply(LoggerMiddleware).forRoutes({path:'cat',method:RequestMethod.GET}); // 只拦截get请求的cat接口
    // 也可以直接将controller作为参数(这样的话,在当前控制器下面所有的请求都会被拦截到的)
    consumer.apply(LoggerMiddleware).forRoutes(CatController);
    // 如果当前需要拦截的请求较多,可以将controller作为参数,但又不希望其中个别接口被拦截到,可以使用下面方式忽略需要拦截的接口
    consumer.apply(LoggerMiddleware).exclude({path:'cat/:id',method:RequestMethod.GET},{path:'cat',method:RequestMethod.POST}).forRoutes(CatController);
  }
}

// ------------------------------------------------------------------------------------------------
// 在logger/lagger.middleware.tss中添加如下内容
import { Injectable, NestMiddleware } from '@nestjs/common';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: any, res: any, next: () => void) {
    // 每次访问cat接口都会被拦截到,并打印出一下日志
    console.log('正在访问cat接口');
    next();
  }
}

以上就是中间件基础使用方法了

函数式中间件

// 在logger/lagger.middleware.tss中添加如下内容
import { Injectable, NestMiddleware } from '@nestjs/common';

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

// 下面这个是函数式中间件(只有函数式中间件可以注册成为全局中间件)
export function logger(req, res, next) {
  console.log('Request_logger');
  next();
}

注册成为全局中间件,只有函数式中间件可以注册成为全局中间件

// 在main.ts进行注册(代码如下)
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { logger } from './logger/logger.middleware';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 将中间件注册成为全局中间件
  app.use(logger);
  await app.listen(3000);
}
bootstrap();

异常处理

异常抛出

例如我们输入了一个不存在的地址,就会响应一个404,这个是框架给我们默认返回的一个异常,有时候我们需要手动去抛出一个异常
Nest提供了一个内置的HttpException类,为基础异常类,可以很好的帮助我们进行异常的处理

// 在controller内其中一个接口如下:
 import {Get,HttpException,HttpStatus} from '@nestjs/common';

  @Get('/xiaoji')
  xiaoji(): string {
    throw new HttpException('服务器异常', HttpStatus.FORBIDDEN);
  }
// 浏览器访问:localhost:3000/cat/xiaoji
// 返回值为: {"statusCode":403,"message":"服务器异常"}

自定义异常及异常的处理

创建并配置异常处理器

// 创建异常过滤器
nest g f http-exception
// 创建完成后目录会多出一个http-exception文件夹

// 在新创建的http-exception.filter.ts文件放入以下内容(已经处理好的)
import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
} from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException)
export class HttpExceptionFilter<T> implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();
    const status = exception.getStatus();

    response.status(status).json({
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
    });
  }
}

使用异常处理器

在cat.controller.ts内使用

import {
  Body,
  Controller,
  Get,
  HttpException,
  HttpStatus,
  UseFilters,
} from '@nestjs/common';
import { HttpExceptionFilter } from 'src/http-exception/http-exception.filter';

// 在接口使用
  @Get('/xiaoji')
  // 访问到这个接口直接抛出异常(异常的格式为我们上面定制的格式(状态码,时间和接口路径))
  @UseFilters(new HttpExceptionFilter())
  xiaoji(): string {
    // 如果这里不抛出异常,上面这个异常过滤器不会执行,只有手动抛出异常则异常过滤器才会执行
    throw new HttpException('服务器异常', HttpStatus.FORBIDDEN);
  }

全局使用异常过滤器

// 例如有的情况有异常,但是我们不能一个个来抛出,例如全局的404异常,因此需要使用全局异常过滤器,方式如下

// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { logger } from './logger/logger.middleware';
import { HttpExceptionFilter } from './http-exception/http-exception.filter';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 将中间件注册成为全局中间件
  app.use(logger);
  // 全局注册异常过滤器
  app.useGlobalFilters(new HttpExceptionFilter());
  //
  await app.listen(3000);
}
bootstrap();

// 当访问一个不存在的接口会返回404状态码和时间以及异常的接口路径(改写了默认的样式)

管道

管道有两个典型的应用场景:
转换:管道将输入数据转换为所需的数据输出(例如,将字符串转换为整数)
验证:对输入数据进行验证,如果验证成功继续传递; 验证失败则抛出异常

Nest 自带九个开箱即用的管道,即

  • ValidationPipe
  • ParseIntPipe
  • ParseFloatPipe
  • ParseBoolPipe
  • ParseArrayPipe
  • ParseUUIDPipe
  • ParseEnumPipe
  • DefaultValuePipe
  • ParseFilePipe

转换

这里以ParseIntPipe为例,进行数据的转化

// 在cat.controller.ts其中一个接口如下内容
import {Get,Param,ParseIntPipe} from '@nestjs/common';
//这个管道可以将接收的参数转为number类型,否则返回的是{id:"123"},转换之后变成{id:123}
//如果参数给个abc导致不能转化成number,则会抛出400错误的异常,代表转化失败
  @Get(':id')
  say1(@Param('id', new ParseIntPipe()) id: number) {
    return {
      id: id,
    };
  }

验证(守卫)

基础使用

守卫在所有中间件之后执行,但在拦截器或管道之前执行。

// 创建守卫
nest g gu role
// 在新建的role.guard.ts内放入以下内容
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';

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

// 再新建一个controller
nest g co user
// controller内放入以下内容
import {
  Controller,
  Get,
  Param,
  ParseIntPipe,
  UseGuards,
} from '@nestjs/common';
import { RoleGuard } from 'src/role/role.guard';

@Controller('user')
@UseGuards(RoleGuard) // 使用守卫,当访问下面任意接口时就会触发守卫,并打印('RoleGuard')
export class UserController {
  @Get(':id')
  // @UseGuards(RoleGuard) // 使用守卫,当访问当前接口时就会触发守卫,并打印('RoleGuard')
  getUserDetial(@Param('id', ParseIntPipe) id: number) {
    return {
      id,
    };
  }
}

定义权限,进行路由比对

// controller.ts内加入下面代码
import {
  Controller,
  Get,
  Param,
  ParseIntPipe,
  UseGuards,
  SetMetadata,
} from '@nestjs/common';
import { RoleGuard } from 'src/role/role.guard';

@Controller('user')
export class UserController {
  @Get(':id')
  @SetMetadata('role', ['access1', 'access2'])
  @UseGuards(RoleGuard) // 使用守卫
  getUserDetial(@Param('id', ParseIntPipe) id: number) {
    return {
      id,
    };
  }
}

// role.guard.ts加入下面代码
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable } from 'rxjs';

@Injectable()
export class RoleGuard implements CanActivate {
  // 加上反射(可以拿到接口对应相关的一些信息)
  constructor(private reflector: Reflector) {}

  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    console.log('RoleGuard');
    // 下面的role与controller接口的@SetMetadata('role', ['access1','access2'])第一个参数保持一致
    const roles = this.reflector.get<string[]>('role', context.getHandler());
    console.log('roles', roles); // 打印的roles就是['access1','access2'],刚刚的第二个参数
    if (!roles) {
      return true;
    }
    const request = context.switchToHttp().getRequest();
    const user = request.user;
    // 上面那个可以在接口上定义权限,然后拿到定义的权限,这里的user代表用户发请求携带的权限,这里可以进行路由比对,有权限放行,没权限返回false
    // 这里是拿到了用户的标识,我们的系统可以查询到用户的权限
    console.log('user',user);
    return true;
  }
}

第二种方式(装饰器模式)

// 创建装饰器
nest g d role
// 创建完成后会发现多了个role.decorator.ts文件

// controller.ts
import {
  Controller,
  Get,
  UseGuards,
} from '@nestjs/common';
import { Role } from 'src/role/role.decorator'; // 引入装饰器
import { RoleGuard } from 'src/role/role.guard'; // 引入守卫

@Controller('user')
@UseGuards(RoleGuard) // 使用守卫,下面的所有接口都会触发守卫
export class UserController {
  @Get('user/list')
  @Role('access1', 'access2','access3', 'access4') // 装饰器内的参数就是权限(个人感觉较麻烦不如上面那个)
  getUserList() {
    return [1, 2, 3, 4];
  }
}

// 访问接口得到-----roles [ 'access1', 'access2', 'access3', 'access4' ]

数据库

连接数据库

// 安装依赖
yarn add --save @nestjs/typeorm typeorm mysql2
// @nestjs/typeorm这个本人运行上面命令一直安装不上,只好单独安装一下就成功了(yarn add @nestjs/typeorm)


// 安装完成后在app.module.ts进行导入
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'admin',
      database: 'shop',
      // entities: [], // 实体类文件,下面创建完curd会讲怎么写的
      synchronize: true, //是否将实体同步到数据库
      retryDelay:500, // 重试连接数据库的间隔
      retryAttempts: 10, // 重试连接数据库的次数
      autoLoadEntities: true, // 自动加载实体(加上这个就不用上面的entities了)
      logging: true, // 是否开启日志
      logger: 'advanced-console', // 日志
    }),
  ],
})
export class AppModule {}

创建CURD

nest g res test
// 选择第一个REST API(遵循rest api的规范)
// 选择y 确定创建curd(增删改查)

// 找到test/entities/test.entity.ts(只要创建了CURD这种文件,一般实体类都是这个位置,那么就可以动态加载实体类了,找到刚刚创建数据库的地方,将entities: [],填充以下内容,用于动态获取实体类),如果autoLoadEntities设置为true了,这个属性可以去掉,autoLoadEntities用于自动加载实体
entities: [__dirname + '/**/*.entity{.ts,.js}'],

在创建完的CURD文件内的实体类编写如下代码

test/entities/test.entity.ts

// 定义实体,定义列,定义自增主键
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity() // 定义实体类
export class Test {
  @PrimaryGeneratedColumn()
  id: number; // 自增id列

  @Column()
  name: string;  // 列名

  @Column()
  password: string;

  @Column()
  age: number;
}

在test/test.model.ts里面编写如下代码

import { Module } from '@nestjs/common';
import { TestService } from './test.service';
import { TestController } from './test.controller';
// 引入实体
import { Test } from './entities/test.entity';
// 引入orm框架
import {TypeOrmModule} from '@nestjs/typeorm'
@Module({
  // 引入注册
  imports:[TypeOrmModule.forFeature([Test])],
  controllers: [TestController],
  providers: [TestService],
})
export class TestModule {}

保存重启项目会发现MySQL里面的shop这个数据库多了一个test这张表,表内的字段和实体类一一对应

实体类介绍

// 定义实体,定义列,定义自增主键
import { Entity, Column, PrimaryGeneratedColumn,CreateDateColumn,Generated } from 'typeorm';

@Entity() // 定义实体类
export class Test {
  @PrimaryGeneratedColumn("uuid") // 自增的uuid
  id: number; // 自增id列

  @Column({type:"varchar",length:255}) // 列类型
  name: string;

  @Column()
  password: string;

  @Column()
  age: number;

  // @CreateDateColumn()  // 自增日期
  @CreateDateColumn({ type: 'timestamp' }) // 时间戳类型
  createTime: Date;

  @Generated('uuid') // 自动生成列
  uuid: string;

   @Column({   // 定义枚举类型
    type: 'enum',
    enum: [1, 2, 3, 4],
    default: 1,
  })
  xiaoji: number;
}
mysql 所有类型

int, tinyint, smallint, mediumint, bigint, float, double, dec, decimal, numeric, date, datetime, timestamp, time, year, char, varchar, nvarchar, text, tinytext, mediumtext, blob, longtext, tinyblob, mediumblob, longblob, enum, json, binary, geometry, point, linestring, polygon, multipoint, multilinestring, multipolygon, geometrycollection

列选项

    @Column({
        type:"varchar",
        name:"ipaaa", //数据库表中的列名
        nullable:true, //在数据库中使列NULL或NOT NULL。 默认情况下,列是nullable:false
        comment:"注释",
        select:true,  //定义在进行查询时是否默认隐藏此列。 设置为false时,列数据不会显示标准查询,适合用于密码。 默认情况下,列是select:true
        default:"xxxx", //加数据库级列的DEFAULT值
        primary:false, //将列标记为主要列。 使用方式和@ PrimaryColumn相同。
        update:true, //指示"save"操作是否更新列值。如果为false,则只能在第一次插入对象时编写该值。 默认值为"true"
        collation:"", //定义列排序规则。
    })
    ip:string

simple-array 列类型
有一种称为simple-array的特殊列类型,它可以将原始数组值存储在单个字符串列中。 所有值都以逗号分隔

@Entity()
export class User {
    @PrimaryGeneratedColumn()
    id: number;
 
    @Column("simple-array")
    names: string[];
}

**simple-json列类型
还有一个名为simple-json的特殊列类型,它可以存储任何可以通过 JSON.stringify 存储在数据库中的值。 当你的数据库中没有 json 类型而你又想存储和加载对象,该类型就很有用了。 例如:
**

@Entity()
export class User {
    @PrimaryGeneratedColumn()
    id: number;
 
    @Column("simple-json")
    profile: { name: string; nickname: string };
}

数据库的增删改查的使用

axios 接口定义

import axios from 'axios'
axios.defaults.baseURL = 'http://localhost:3000'
 // 增
export const addUser = (data) => axios.post('/user',data).then(res => res.data)
 // 查
export const getList = (data) => axios.get('/user',{params:data}).then(res => res.data)
 // 删
export const delUser = (data) => axios.delete(`/user/${data.id}`).then(res => res.data)
 // 改
export const updateUser = (data) => axios.patch(`/user/${data.id}`,data).then(res => res.data)

nest后端代码为

实体类为

import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class CreateUserDto {
	@Column()
    name:string
    @Column()
    desc:string
}

controller代码如下

import { Controller, Get, Post, Body, Patch, Param, Delete, Query } from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
 
@Controller('user')
export class UserController {
  constructor(private readonly userService: UserService) {}
 
 // 增
  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return this.userService.create(createUserDto);
  }
 // 查
  @Get()
  findAll(@Query() query:{keyWord:string,page:number,pageSize:number}) {
    return this.userService.findAll(query);
  }
 // 改
  @Patch(':id')
  update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    return this.userService.update(+id, updateUserDto);
  }
 // 删
  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.userService.remove(+id);
  }
}

service代码如下

1.引入 InjectRepository typeOrm 依赖注入 接受一个实体
2.引入类型 Repository 接受实体泛型
3.Like 用于模糊查询
4.save 保存 find 查询 update 更新 delete 删除

import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { Repository, Like } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
@Injectable()
export class UserService {
  constructor(@InjectRepository(User) private readonly user: Repository<User>) { }
	// 增
  create(createUserDto: CreateUserDto) {
    const data = new User() // 创建实体类对象
    data.name = createUserDto.name // 实体类的name赋值为传来的name
    data.desc = createUserDto.desc // 实体类的desc赋值为传来的desc
    return this.user.save(data) // 保存(每次保存都相当于添加一次)
  }
 // 查
  async findAll(query: { keyWord: string, page: number, pageSize: number }) {
    const data = await this.user.find({ // 查询
      where: {
        name: Like(`%${query.keyWord}%`) //模糊查询
      },
      order: {
        id: "DESC" // 倒序
      },
      skip: (query.page - 1)* query.pageSize, // skip跳过,从0开始
      take:query.pageSize, // 需要展示的每页的数量
    })
    const total = await this.user.count({  // 总数量
      where: {
        name: Like(`%${query.keyWord}%`)
      },
    })
    return {
      data,
      total
    }
  }
 // 改
  update(id: number, updateUserDto: UpdateUserDto) {
    return this.user.update(id, updateUserDto)
  }
 // 删
  remove(id: number) {
    return this.user.delete(id)
  }
}

Module代码如下

import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import {TypeOrmModule} from '@nestjs/typeorm'
import { User } from './entities/user.entity';
@Module({
  imports:[TypeOrmModule.forFeature([User])],
  controllers: [UserController],
  providers: [UserService]
})
export class UserModule {}

[多表联查的使用]

新增了一个接口
export const addTags = (data) => axios.post(‘/user/add/tags’,data).then(res => res.data)

后端Nestjs
1.新建一个 tags.entity.ts
定义Tags的数据表

import { Column, Entity, PrimaryGeneratedColumn, BeforeInsert, CreateDateColumn, Generated, OneToOne, JoinColumn, ManyToOne } from 'typeorm'
import { User } from './user.entity'
@Entity()
export class Tags {
    @PrimaryGeneratedColumn()
    id: number
 
    @Column()
    tags:string
 
    @ManyToOne(()=>User,(user)=>user.tags)
    @JoinColumn()
    user:User
} 

Modal 需要关联tag表

import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import {TypeOrmModule} from '@nestjs/typeorm'
import { User } from './entities/user.entity';
import { Tags } from './entities/tags.entity';
// import { example } from './entities/tags.entity';
@Module({
  imports:[TypeOrmModule.forFeature([User,Tags])],
  controllers: [UserController],
  providers: [UserService]
})
export class UserModule {}

然后user表跟tags表进行关联

import { Column, Entity, PrimaryGeneratedColumn, BeforeInsert, CreateDateColumn, Generated, OneToOne, JoinColumn, OneToMany } from 'typeorm'
import { Tags } from './tags.entity'
@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number
 
  @Column({ type: "varchar", length: 255 })
  name: string
 
 
  @Column({ type: "text" })
  desc: string
 
  @Generated('uuid')
  uuid: string
 
  @CreateDateColumn({ type: "timestamp" })
  createTime: Date
 
  @OneToMany(() => Tags, (tags) => tags.user)
  tags:Tags[]
  // example: example
}

这儿我们解释一下 OneToMany 和 ManyToOne的用法
对于用户来说一个用户可以拥有多个tag 他们的关系是一对多 OneToMany
对于tag来说他们是多个tag指定单个用户 所以是 ManyToOne
在这里插入图片描述
OneToMany 接受两个参数
第一个参数是个函数返回关联的类 所以在user表关联tag
第二个参数 创建双向关系
ManyToOne 用法一样

@OneToMany(() => Tags, (tags) => tags.user)

保存该关系

沿用上一章的代码增加Controller 增加 addTags

import { Controller, Get, Post, Body, Patch, Param, Delete, Query } from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
 
@Controller('user')
export class UserController {
  constructor(private readonly userService: UserService) {}
 
  @Post('/add/tags')
  addTags (@Body() params:{tags:string[],userId:number}) {
    return this.userService.addTags(params)
  }
 
  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return this.userService.create(createUserDto);
  }
 
  @Get()
  findAll(@Query() query:{keyWord:string,page:number,pageSize:number}) {
    return this.userService.findAll(query);
  }
 
 
  @Patch(':id')
  update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    return this.userService.update(+id, updateUserDto);
  }
 
  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.userService.remove(+id);
  }
}

service 增加 addTags 方法

import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { Repository, Like } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { Tags } from './entities/tags.entity';
// import { example } from './entities/tags.entity';
@Injectable()
export class UserService {
  constructor(
  @InjectRepository(User) private readonly user: Repository<User>,
  @InjectRepository(Tags) private readonly tag: Repository<Tags>
  ) { }
 
//通过前端传入的userId 查到当前id 的用户信息,然后拿到前端传入的tags [tag1,tag2,tag3]
// 进行遍历 给tag实例进行赋值 然后调用保存方法添加tag 添加完之后 通过 tagList 保存该tag类
// 最后把tagList 赋给 user类的tags属性 然后重新调用save 进行更新
 
  async addTags (params:{tags:string[],userId:number}) {
    const userInfo = await this.user.findOne({where:{id:params.userId}})
    const tagList:Tags[] = []
    for (let i = 0;i<params.tags.length;i++) {
       let T =  new Tags()
       T.tags = params.tags[i];
       await this.tag.save(T)
       tagList.push(T)
    }
    userInfo.tags = tagList;
    console.log(userInfo,1)
    return this.user.save(userInfo)
  }
 
  async create(createUserDto: CreateUserDto) {
    const data = new User()
    // const ex = new example()
    data.name = createUserDto.name
    data.desc = createUserDto.desc
    // await this.example.save(ex)
    return this.user.save(data)
  }
 
  async findAll(query: { keyWord: string, page: number, pageSize: number }) {
    const data = await this.user.find({
      //查询的时候如果需要联合查询需要增加 relations
      relations:['tags'],
      where: {
        name: Like(`%${query.keyWord}%`)
      },
      order:{
        id:"DESC",
      },
      skip: (query.page - 1) * query.pageSize,
      take: query.pageSize
    })
    const total = await this.user.count({
      where: {
        name: Like(`%${query.keyWord}%`)
      },
    })
    return {
      data,
      total
    }
  }
 
  update(id: number, updateUserDto: UpdateUserDto) {
    return this.user.update(id, updateUserDto)
  }
 
  remove(id: number) {
    return this.user.delete(id)
  }
}

事务

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1295121.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

为什么Java程序员需要掌握多线程?揭秘并发编程的奥秘

为什么Java程序员需要掌握多线程&#xff1f;揭秘并发编程的奥秘 个人简介前言多线程对于Java的意义&#x1f4cc;1.提高程序性能&#xff1a;&#x1f4cc;2 提高用户体验&#xff1a;&#x1f4cc;3支持并发处理&#xff1a;&#x1f4cc;4 资源共享和同步&#xff1a;&#…

从 0 到 100TB,MatrixOne 助您轻松应对

作者&#xff1a;邓楠MO产品总监 导读 随着传感器和网络技术的大规模应用&#xff0c;海量 IoT 设备产生了巨量数据&#xff0c;传统数据库方案难以满足这些数据的存储和处理需求。MatrixOne 是一款强大的云原生超融合数据库&#xff0c;具备优秀的流式数据写入和加工能力&am…

Python中的range()函数详解:掌握迭代的利器

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com Python中的range()函数是一个强大的工具&#xff0c;用于生成一系列的数字&#xff0c;常用于循环操作。虽然看似简单&#xff0c;但其灵活性和功能却不容小觑。在本文中&#xff0c;将深入研究range()函数&…

神经网络训练、验证、测试集

在配置训练、验证和测试数据集的过程中做出正确决策会在很大程度上帮助大家创建高效的神经网络。训练神经网络时&#xff0c;需要做出很多决策&#xff0c;例如&#xff1a; 神经网络分多少层 每层含有多少个隐藏单元 学习速率是多少 各层采用哪些激活函数 创建新应用的过程…

Github与Gitlab

学习目标 能够使用GitHub创建远程仓库并使用能够安装部署GitLab服务器能够使用GitLab创建仓库并使用掌握CI/CD的概念掌握蓝绿部署, 滚动更新,灰度发布的概念 GitHub是目前最火的开源项目代码托管平台。它是基于web的Git仓库&#xff0c;提供公有仓库和私有仓库&#xff0c;但私…

Leetcode—198.打家劫舍【中等】

2023每日刷题&#xff08;五十二&#xff09; Leetcode—198.打家劫舍 算法思想 具体思路 首先&#xff0c;我们从上面的题目描述中抽象出题意。 ● 从一个非负整数数组中找到一个子序列&#xff0c;并且该子序列的和最大 ● 子序列中每个数的位置不能够相邻。举例来讲&…

yolo目标检测+目标跟踪+车辆计数+车辆分割+车道线变更检测+速度估计

这个项目使用YOLO进行车辆检测&#xff0c;使用SORT&#xff08;简单在线实时跟踪器&#xff09;进行车辆跟踪。该项目实现了以下任务&#xff1a; 车辆计数车道分割车道变更检测速度估计将所有这些详细信息转储到CSV文件中 车辆计数是指在道路上安装相应设备&#xff0c;通过…

有哪些值得分享的销售拓客技巧?

拓客对于销售的重要性 拓客&#xff08;Toker&#xff09;是一个商业上的名词&#xff0c;核心就是提高售前服务、市场推广的水平&#xff0c;从而挖掘出潜在客户的隐形需求&#xff08;或称软需求&#xff09;。 拓客的核心&#xff0c;其实就是提高售前服务、市场推广的水平…

【操作系统笔记】-文件系统

引言 之前已经学习过数据在内存中是如何表示&#xff0c;如何存储&#xff0c;但是这些存储在PC断电后数据便消失。因此我们需要一个可以持久化存储并且容量远远大于内存的结构&#xff0c;这一篇我们将学习&#xff0c;文件是如何被组织和操作的&#xff0c;这是一个操作系统…

轮播插件Slick.js使用方法详解

相比于Swiper而选择使用Slick.js的原因主要是因为其兼容不错并且在手机端的滑动效果更顺畅 参数&#xff1a; 1.基本使用&#xff1a;一般使用只需前十个属性 $(.box ul).slick({autoplay: true, //是否自动播放pauseOnHover: false, //鼠标悬停暂停自动播放speed: 1500, //…

根据java类名找出当前是哪个Excel中的sheet

pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 …

定制开发外贸引流工具的流程分享!

随着全球互联网的快速发展&#xff0c;外贸行业面临着越来越多的挑战和机遇&#xff0c;为了在激烈的市场竞争中获得更多的客户和订单&#xff0c;许多外贸企业开始寻求创新和突破。 其中&#xff0c;定制开发外贸引流工具成为了越来越多企业的选择&#xff0c;本文将为您分享…

Navicat Premium 16 for Mac/Windows:高效的数据库开发工具

Navicat Premium 16是一款功能强大的数据库开发工具&#xff0c;为开发人员提供了全面的工具和功能&#xff0c;帮助他们更高效地进行数据库开发和管理。不论是初学者还是专业开发人员&#xff0c;Navicat Premium 16都能满足他们的需求&#xff0c;并提供直观、易用的界面。 …

报名学历的同学,月底前记得申请抵扣个税!

2024年度专项附加扣除开始确认啦&#xff01; 已经报名学历&#xff08;自考、成考、开放大学&#xff09;的同学&#xff0c;记得去申请抵扣个税哦&#xff01; 每个月的应纳税额可以减免400元呢&#xff0c;学历提升在读这几年算下来&#xff0c;可以省不少钱。 注意&#x…

使用ASIRequest库进行Objective-C网络爬虫示例

在Objective-C中&#xff0c;ASIHTTPRequest是一个非常受欢迎的库&#xff0c;用于处理HTTP请求。它可用于下载网页内容&#xff0c;处理API请求&#xff0c;甚至进行复杂的网络交互。下面是一个简单的示例&#xff0c;展示了如何使用ASIHTTPRequest库来爬取网页代码。 首先&a…

C# 语法笔记

1.ref、out&#xff1a;参数传递的两种方式 ref&#xff1a;引用传递 using System; namespace CalculatorApplication {class NumberManipulator{public void swap(ref int x, ref int y){int temp;temp x; /* 保存 x 的值 */x y; /* 把 y 赋值给 x */y temp; /* 把 t…

【无线网络技术】——无线局域网(学习笔记)

&#x1f4d6; 前言&#xff1a;本章首先介绍无线局域网的基本概念&#xff0c;然后详细介绍IEEE 802.11的基本工作原理&#xff0c;侧重于媒体访问控制和一跳范围内的通信技术。 目录 &#x1f552; 1. 概述&#x1f558; 1.1 覆盖范围&#x1f558; 1.2 特点&#x1f558; 1.…

《opencv实用探索·十五》inRange二值化图像

opencv接口如下&#xff1a; void inRange(InputArray src, InputArray lowerb, InputArray upperb, OutputArray dst);函数实现二值化功能&#xff0c;主要是将在两个阈值内的像素值设置为白色&#xff08;255&#xff09;&#xff0c;而不在阈值区间内的像素值设置为黑色&am…

命令行参数(C语言)

目录 什么是命令行参数 main函数的可执行参数 不传参打印 传参打印 IDE传参 cmd传参 命令行参数的应用&#xff08;文件拷贝&#xff09; 什么是命令行参数 概念&#xff1a;命令行参数指的是在运行可执行文件时提供给程序的额外输入信息。它们通常以字符串形式出现&am…

【MATLAB】MODWT分解+FFT+HHT组合算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~也可转原文链接获取~ 1 基本定义 MODWT分解FFTHHT组合算法是一种综合性的信号处理方法&#xff0c;它结合了经验小波变换&#xff08;Empirical Wavelet Transform&#xff0c;EWT&#xff09;、快速傅里叶变换&#xff…