nest使用小记

313 阅读4分钟

起步

安装nest
npm i -g @nestjs/cli

新建nest项目
nest new project-name

开发监听启动
npm run start:dev

目录
src 
    ├── app.controller.spec.ts 
    ├── app.controller.ts 控制器
    ├── app.module.ts 模块
    ├── app.service.ts 方法服务
    └── main.ts  入口
    

使用

app.controller.ts 控制器

// 引入模块 Get Post对应接口请求类型,Body post参数体 Param get参数体
import { Controller, Get, Post, HttpCode, Body, Param } from '@nestjs/common';
// 方法服务文件
import { AppService } from './app.service';
// 引入自定义返回格式的接口
import { ResData } from './interface/app.interface';

@Controller('api') // 接口前缀,可以自定义
export class AppController {
  // 引入AppService后,可以通过this.appService使用AppService内定义的方法
  constructor(private readonly appService: AppService) {}

  @Post('index')  // 接口名称
  @HttpCode(200) // 设置状态码,post默认为201
  getIndex(@Body() body): ResData {
    // return 返回内容,返回string会在页面上打印出来,返回Object,同正常请求接口格式返回
    if (body.id) {
      let uuid = this.appService.uuid();
    }
    return { code: 200, data: { uuid }, msg: 'ok' };
  }
}

app.service.ts

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

@Injectable()
export class AppService {
  uuid(): string {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
      /[xy]/g,
      function (c) {
        var r = (Math.random() * 16) | 0,
          v = c == 'x' ? r : (r & 0x3) | 0x8;
        return v.toString(16);
      },
    );
  }
}

app.interface.ts ts接口

export interface ResData {
  code: number;
  data: object;
  msg: string;
}

数据库

配置env环境,typeorm数据库配置读取.env里的配置

.env

DB_HOST = 127.0.0.1
DB_USERNAME = root
DB_PASSWORD = password
DB_DATABASE = database

ormconfig.js

const { env } = process;
const db = {
 type: 'mysql',
 host: env.DB_HOST,
 port: 3306,
 username: env.DB_USERNAME,
 password: env.DB_PASSWORD,
 database: env.DB_DATABASE,
 entities: [
   `${env.NODE_ENV === 'development' ? 'dist/' : ''}**/*.entity{.ts,.js}`,
 ],
 synchronize: true,
};

module.exports = db;

安装使用dotenv

 npm i dotenv --save
 
 app.module.ts
 const dotenv = require('dotenv');
 dotenv.config('../.env');

npm script 设置环境变量NODE_ENV

 npm i cross-env -D
 
 package.json
 "scripts": {
   "build": "cross-env NODE_ENV=production nest build",
   "start:dev": "cross-env NODE_ENV=development nest start --watch",
 }
 

安装typeorm sql

 npm i --save @nestjs/typeorm typeorm mysql2

 在app.module.ts引入TypeOrmModule
 app.module.ts
 import { TypeOrmModule } from '@nestjs/typeorm';
 import { Connection } from 'typeorm';

 @Module({
   imports: [TypeOrmModule.forRoot()],
 })
 export class AppModule {
   constructor(private readonly connection: Connection) {}
 }

存储库

user.entity.ts

mport { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; 
@Entity() 
export class User { 
  rimaryGeneratedColumn() 
  id: number; 

  @Column() 
  nickname: string;

  @Column() 
  avatar: string
}

user.service.ts

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';

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

  /**
   * 查找所有用户
   * @returns 
   */
  findAll(): Promise<Store[]> {
    return this.usersRepository.find();
  }

  /**
   *
   * @param user 用户信息
   * @returns
   */
  addUser(user): Promise<User> {
    return this.usersRepository.save(user);
  }
}

user.controller.ts

import { Controller, Get, Post, Body } from '@nestjs/common';
import { userService } from '../services/store.service';
import { ResData } from '../interface/app.interface';

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

  @Post('user')
  async addUser(@Body() body): Promise<ResData> {
    let user: any;
    user = await this.userService.addUser(body);

    return {
      code: 200,
      data: {
        user,
      },
      msg: 'ok',
    };
  }
}

user.module.ts

import { Module } from '@nestjs/common';
import { TypeOrmModule, InjectRepository } from '@nestjs/typeorm';
import { User } from './user.entity';
import { UserService } from './user.service';
import { UserController } from './user.controller';

@Module({
  imports: [TypeOrmModule.forFeature([Store])],
  providers: [UserService],
  controllers: [UserController],
})
export class UsersModule {}

在app.module.ts引入user.module.ts

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './user.module'

import { TypeOrmModule } from '@nestjs/typeorm';
import { Connection } from 'typeorm';

@Module({
  imports: [TypeOrmModule.forRoot(),UserModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {
  constructor(private readonly connection: Connection) {}
}

拦截器

请求处理成功返回

新建成功拦截器文件
nest g in interceptor/transform

import {
  CallHandler,
  ExecutionContext,
  Injectable,
  NestInterceptor,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable()
export class TransformInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(
      map((data) => ({
        code: 200,
        data,
        msg: 'ok',
      })),
    );
  }
}

请求处理错误返回

新建错误拦截器文件
nest g f filters/httpExecption

import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
} from '@nestjs/common';
import { Response } from 'express';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const status = exception.getStatus();
    const message = exception.message;

    response.status(200).json({
      code: status,
      message,
    });
  }
}

错误使用实例
if (false) {
   throw new HttpException('错误信息', ‘自定义返回code’);
}

在main.ts中引入拦截器

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { TransformInterceptor } from './interceptor/transform.interceptor';
import { HttpExceptionFilter } from './filters/http-execption.filter';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.enableCors();
  app.useGlobalInterceptors(new TransformInterceptor());
  app.useGlobalFilters(new HttpExceptionFilter());
  await app.listen(3000);
}
bootstrap();

认证

passport验证

npm install --save @nestjs/passport passport passport-local @nestjs/jwt passport-jwt
npm install --save-dev @types/passport-local @types/passport-jwt md5

admin.module.ts导出AdminsService,用于auth.service中调用

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Admin } from '../entitys/admin.entity';
import { AdminsService } from '../services/admin.service';
import { AdminController } from '../controller/admin.controller';

@Module({
 imports: [TypeOrmModule.forFeature([Admin])],
 providers: [AdminsService],
 controllers: [AdminController],
 exports: [AdminsService]
})
export class AdminsModule {}

admin.service.ts

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Admin } from '../entitys/admin.entity';
const md5 = require('md5');

@Injectable()
export class AdminsService {
  constructor(
    @InjectRepository(Admin)
    private adminRepository: Repository<Admin>,
  ) {}
  
  login(params): Promise<Admin> {
    let admin = {
      username: params.username,
      password: md5(params.password),
    };
    return this.adminRepository.findOne(admin);
  }
 }

新建auth.service.ts

import { Injectable } from '@nestjs/common';
import { AdminsService } from '../services/admin.service';

@Injectable()
export class AuthService {
 constructor(
   private readonly adminService: AdminsService,
 ) {}

 async validateUser(username: string, password: string): Promise<any> {
   const user = await this.adminService.login({ username, password });
   if (user) {
     return { ...user };
   }
   return null;
 }
}

新建auth.module.ts

引入AdminsModule assportModule LocalStrategy

import { Module } from '@nestjs/common';
import { AdminsModule } from '../modules/admin.mudule';
import { PassportModule } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { LocalStrategy } from './local.strategy';

@Module({
 imports: [
   AdminsModule,
   PassportModule,
 ],
 providers: [AuthService, LocalStrategy],
})
export class AuthModule {}

新建local.strategy.ts

import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, HttpException } from '@nestjs/common';
import { AuthService } from './auth.service';

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
 constructor(private authService: AuthService) {
   super();
 }

 async validate(username: string, password: string): Promise<any> {
   const user = await this.authService.validateUser(username, password);
   if (!user) {
     throw new HttpException('登录验证失败', 401);
   }
   return user;
 }
}

在app.module.ts引入auth.module

@Module({
  imports: [AuthModule],
})

jwt验证

新建jwt.strategy.ts

import { Injectable } from '@nestjs/common';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
 constructor() {
   super({
     jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
     ignoreExpiration: false,
     secretOrKey: process.env.JWT_SECRET,
   });
 }

 async validate(payload: any) {
   return { userId: payload.id };
 }
}

auth.module.ts引入JwtModule JwtStrategy

import { Module } from '@nestjs/common';
import { AdminsModule } from '../modules/admin.mudule';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';

import { AuthService } from './auth.service';

import { LocalStrategy } from './local.strategy';
import { JwtStrategy } from './jwt.strategy';

@Module({
 imports: [
   AdminsModule,
   PassportModule.register({ defaultStrategy: 'jwt' }),
   JwtModule.register({
     secret: process.env.JWT_SECRET,
     signOptions: {
       //   expiresIn: '60s'
     },
   }),
 ],
 providers: [AuthService, LocalStrategy, JwtStrategy],
})
export class AuthModule {}

admin.service.ts引入JwtService

import { Injectable } from '@nestjs/common';
import { AdminsService } from '../services/admin.service';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthService {
 constructor(
   private readonly adminService: AdminsService,
   private readonly jwtService: JwtService,
 ) {}

 async validateUser(username: string, password: string): Promise<any> {
   const user = await this.adminService.login({ username, password });
   if (user) {
     return { ...user, access_token: this.jwtService.sign({ id: user.id }) };
   }
   return null;
 }
}

在admin.controller.ts中引入AuthGuard

 import {
  Controller,
  HttpException,
  Post,
  Body,
  UseGuards,
  Request,
} from '@nestjs/common';
import { AdminsService } from '../services/admin.service';
import { AuthGuard } from '@nestjs/passport';

@Controller('admin')
export class AdminController {
  constructor(private readonly adminService: AdminsService) {}

  @UseGuards(AuthGuard('local'))
  @Post('login')
  async login(@Request() req) {
    return req.user;
  }
  
  @UseGuards(AuthGuard('jwt'))
  @Post('list')
  async getList(@Request() req) {
    let list = await this.adminService.findAll(req.query);
    return { list, page: { total: list.length } };
  }
}