Nestjs配置文件上传, 配置中间件以及管道的使用

Nestjs中的文件上传

  • 文档: https://docs.nestjs.com/techniques/file-upload

安装插件

  • $ yarn add @types/multer

示例

1 ) 简单单个上传

  • 前端代码

        <form action="upload/add" method="post" enctype="multipart/form-data">
            <input type="file" name="files" />
            <br> <br>
            <input type="submit" value="提交">
        form>
    
  • 后端代码

    import { Controller, Get, Render, Post, Body, UseInterceptors, UploadedFile } from '@nestjs/common';
    import { FileInterceptor } from '@nestjs/platform-express';
    import { createWriteStream } from 'fs';
    import { join } from 'path';
    
    @Controller('upload')
    export class UploadController {
        @Get()
        @Render('default/upload')
        index() {
        }
    
        @Post('add')
        @UseInterceptors(FileInterceptor('files'))
        doAdd(@Body() body, @UploadedFile() file) {
            console.log(body);
            console.log(file);  //上传图片的信息  必须在form的属性里面配置enctype="multipart/form-data"
            const writeStream = createWriteStream(join(__dirname, '../../public/upload', `${Date.now()}-${file.originalname}`))
            writeStream.write(file.buffer);
            return '上传图片成功';
        }
    }
    

2 ) 多文件上传

  • 如果field一样(用的name都是files),我们可选择使用 UploadedFiles 的方法来处理,并且通过 for … of 来遍历
  • 如果不一样,则提供了,如下的配置
    @Post('upload')
    @UseInterceptors(FileFieldsInterceptor([
        { name: 'avatar', maxCount: 1 },
        { name: 'background', maxCount: 1 },
    ]))
    uploadFile(@UploadedFiles() files: { avatar?: Express.Multer.File[], background?: Express.Multer.File[] }) {
        console.log(files);
    }
    
  • 或者使用Any类型的拦截器AnyFilesInterceptor
    @Post('upload')
    @UseInterceptors(AnyFilesInterceptor())
    uploadFile(@UploadedFiles() files: Array<Express.Multer.File>) {
        console.log(files);
    }
    
  • 需要注意的是,我们每次上传时,这些路径,图片处理什么的,需要封装一下

Nestjs 中间件

  • 同express一样,nestjs中间件也可以理解为在请求和响应之间的一个管道处理程序
  • 可以是一个函数,也可以是一个@Injectable() 装饰器装饰的类

中间件作用

  • 执行任何代码
  • 对请求和响应对象进行更改
  • 结束请求-响应周期
  • 调用堆栈中的下一个中间件函数
  • 如果当前的中间件函数没有结束请求-响应周期, 它必须调用 next() 将控制传递给下一个中间件函数
  • 否则, 请求将被挂起

中间件的创建和使用

1 ) 创建

  • $ nest g middleware http 这里http是中间件名
  • 会生成src/http.middleware.ts文件,如下:
    import { Injectable, NestMiddleware } from '@nestjs/common';
    
    @Injectable()
    export class HttpMiddleware implements NestMiddleware {
        use(req: any, res: any, next: () => void) {
            next();
        }
    }
    
  • 我们按照文档上,改造一下
    import { Injectable, NestMiddleware } from '@nestjs/common';
    import { Request, Response, NextFunction } from 'express';
    
    @Injectable()
    export class HttpMiddleware implements NestMiddleware {
        use(req: Request, res: Response, next: NextFunction) {
            console.log('Request...');
            next();
        }
    }
    

2 ) 在app.module.ts中使用

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { HttpMiddleware } from './http.middleware';
import { CatsModule } from './cats/cats.module'; // 模块

@Module({
  imports: [CatsModule],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(HttpMiddleware)
      // .forRoutes('cats'); // 表示哪些路由可以使用这个中间件
      // .forRoutes(someController); // 匹配所有的路由 // 或者传入指定控制器,但是不建议这样写 
      // .forRoutes({path: 'cats', method: RequestMethod.GET}); // 只匹配GET
      // .forRoutes({path: 'cats', method: RequestMethod.ALL}); // 匹配所有方法
      // .forRoutes({path: 'cats', method: RequestMethod.ALL}, {path: 'user', method: RequestMethod.ALL}); // 配置多个
      .forRoutes('*'); // 匹配所有的路由 
  }
}
  • 当然,我们最好把中间件放到一个通用的模块中,如下:./common/middleware/
    import { LoggerMiddleware } from './common/middleware/logger.middleware';
    
  • 其实可以在创建的时候指定目录 $ nest g middleware common/middleware/http.middleware

3 ) 配置多个中间件的示例

3.1 一种配置

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { HttpMiddleware } from './http.middleware';
import { UserMiddleware } from './user.middleware';

@Module({
  imports: [],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(HttpMiddleware).forRoutes('*')
      .apply(UserMiddleware).forRoutes('user')
  }
}

3.2 另一种配置

// 这里只写关键代码
consumer
.apply(NewsMiddleware,UserMiddleware)
.forRoutes({ path: 'u*r', method: RequestMethod.ALL},{ path: 'news', method: RequestMethod.ALL}) // 这里不讲究顺序

4 ) 函数式中间件

// 只写关键代码
export function logger(req, res, next) { 
    console.log(`函数式中间件...`);
    next();
};

export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(HttpMiddleware, logger).forRoutes('*')
  }
}

5 ) 全局中间件

  • 在main.ts中使用,注意不能使用类中间件,只能使用函数中间件

    //全局中间件只能引入函数式中间件
    import { logger } from './middleware/logger.middleware';
    
    app.use(logger);
    

管道

  • 关于管道:https://docs.nestjs.com/pipes
  • 管道的概念是主要用于将输入数据转换为所需的输出数据
  • 或者处理Get,Post提交的数据

创建管道

  • $ nest g pipe cart

  • 或指定路径生成 $ nest g middleware common/pipe/cart.pipe

  • 创建一个cart的管道

    import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common';
    
    @Injectable()
    export class CartPipe implements PipeTransform {
      transform(value: any, metadata: ArgumentMetadata) {
        return value;
      }
    }
    
  • 说明:管道是由@Injectable()装饰器装饰的一个类

  • 需要实现 PipeTransform 这个接口,并且实现其内部的一个 transform 方法

  • 这里有两个参数:value,metadata

  • 其中, value是传递到管道里的数据, metadata是一种类型

  • 在这里面可以修改传入的值以及验证传入值的合法性

使用管道

  • 在控制器中使用管道

    import { Controller, Get, Query, UsePipes} from '@nestjs/common';
    //引入管道
    import { CartPipe } from '../../pipe/cart.pipe';
    
    @Controller('cart')
    export class NewsController {
        @Get()
        @UsePipes(new CartPipe())
        index(@Query() info){
            console.log(info);
            return 'Cart'
        }
    }
    
  • 由上面的代码可知,先要引入管道,之后使用UsePipes将引入的管道实例作为参数作为当前路由的一项装饰器

  • 这里的info就会被传入到管道里面,也就是管道里的value参数

  • 之后,在管道里就可以针对当前的值进行修改,校验等操作

关于管道中的校验

  • 在接收到数据前,需要对数据进行一些校验等操作,这时候就比较适合在管道中写相关程序

  • 一般而言,我们会借助第三方的库来帮助我们完成校验,官方给我们提供了使用joi库相关的示例

  • https://github.com/hapijs/joi,https://joi.dev/api/?v=17.6.0

  • 下面做一下演示

    // 控制器文件中使用
    import { Controller, Get, Query, UsePipes } from '@nestjs/common';
    import { UserPipe } from '../../pipe/user.pipe';
    import * as Joi from 'joi';
    
    // 定义 json schema
    const userSchema = Joi.object().keys({
        name: Joi.string().required(),
        age: Joi.number().integer().min(6).max(66).required(),
    })
    
    @Controller('user')
    export class UserController {
        @Get()
        @UsePipes(new UserPipe(userSchema))
        index(@Query() info) {
            console.log(info);
            return '用户中心';
        }
    }
    
    // 在管道文件中
    import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common';
    
    @Injectable()
    export class UserPipe implements PipeTransform {
      private schema;
      constructor(schema){
        this.schema=schema // 实例化的时候,参数传入
      }
      transform(value: any, metadata: ArgumentMetadata) {
        const { error } = this.schema.validate(value); // 进行校验
        // 如果错误,则提示,或统一返回相同的实体数据结构, 比如给前端提示
        if(error) {
          return {"success": false};
        }
        return value;    
      }
    }
    
  • 上面是基础用法,当然一般而言,也可以添加相关message信息,用于给前端用户提示

  • 更多用法,请参考上面文档

你可能感兴趣的:(NodeJs,中间件)