Nest.js初体验(二):Configuration模块、拦截器和过滤器

561 阅读3分钟

我正在参加掘金创作者训练营第5期,点击了解活动详情.

本文将以上一篇Nest初体验为基础,介绍NestJS的Configuration模块、拦截器和过滤器。

使用Configuration模块配置数据库

在上一篇文章中,我们是这样配置数据库的:

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: '123456',
      database: 'test',
      entities: [User],
      synchronize: true,
    }),
    UserModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})

但是在实际开发中,为了区分不同环境,数据库配置应该保存在环境变量中。接下来,我们就通过NestJS的Configuration模块来实现这一功能。

安装config依赖:

npm i --save @nestjs/config

编写app.module文件,默认加载项目根目录下的.env的键值对。

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';

@Module({
  imports: [ConfigModule.forRoot()],
})
export class AppModule {}

forRoot方法将会注册ConfigService,通过ConfigService的get方法可以获取环境变量。

在项目根目录下创建.env文件:

DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWORD=123456
DB_DATABASE=test

创建config/env.ts文件,用于根据不同环境使用不同的环境变量:生产环境下读取.env.prod文件的环境变量,否则读取.env文件的。

import * as fs from 'fs';
import * as path from 'path';

const isProd = process.env.NODE_ENV === 'production';

function parseEnv() {
  const localEnv = path.resolve('.env');
  const prodEnv = path.resolve('.env.prod');

  if (!fs.existsSync(localEnv) && !fs.existsSync(prodEnv)) {
    throw new Error('缺少环境配置文件');
  }

  const filePath = isProd && fs.existsSync(prodEnv) ? prodEnv : localEnv;
  return { path: filePath };
}

export default parseEnv();

在app.module中,设置自定义环境变量文件,通过ConfigService.get获取环境变量:

import envConfig from '../config/env';
import { TypeOrmModule } from '@nestjs/typeorm';

  imports: [
    ConfigModule.forRoot({
      isGlobal: true, // 设置为全局
      envFilePath: [envConfig.path],//自定义环境变量文件
    }),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: async (configService: ConfigService) => ({
        type: 'mysql', // 数据库类型
        entities: [User], // 数据表实体
        host: configService.get('DB_HOST'), // 主机,默认为localhost
        port: configService.get<number>('DB_PORT'), // 端口号
        username: configService.get('DB_USER'), // 用户名
        password: configService.get('DB_PASSWORD'), // 密码
        database: configService.get('DB_DATABASE'), //数据库名
        timezone: '+08:00', //服务器上配置的时区
        synchronize: false, //根据实体自动创建数据库表, 生产环境建议关闭
    }),
}),

使用拦截器统一接口数据格式

在上篇文章中,获取用户列表接口是这么返回数据的:

截屏2022-08-07 下午9.57.56.png

这样的格式其实并不符合实际需求。我们一般希望拿到这样的数据格式:

{
data:[]         //返回的数据
code: 0,        //表示请求是否成功,如 0 成功 -1失败
msg: '请求成功', //失败or成功信息
}

显然,我们想要所有接口都按这样的格式返回。所以,应该在全局统一处理。NestJS借鉴了AOP(Aspect Oriented Programming)的编程思想,提供了拦截器,可以实现这个功能。

创建core/interceptor/transform.interceptor.ts文件:

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

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

每个拦截器都要实现intercept方法。这个方法接收两个参数,分别是ExecutionContext和CallHandle. 通过intercept方法,拦截器可以控制请求和响应流。

要使得拦截器生效,还得在main.ts中注册它:

import { TransformInterceptor } from './core/interceptor/transform.interceptor';

 app.useGlobalInterceptors(new TransformInterceptor());

再次请求接口,可以看到数据按统一格式返回了:

截屏2022-08-07 下午10.23.43.png

使用过滤器统一处理异常

通过过滤器捕获http请求异常:

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

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

    let message: string | string[];

    if (typeof exceptionResponse === 'object') {
      message = JSON.parse(JSON.stringify(exceptionResponse)).message ?? '';
    } else {
      message = exception.message
        ? exception.message
        : `${status >= 500 ? 'Service Error' : 'Client Error'}`;
    }

    const errorResponse = {
      data: {},
      message: message,
      code: status,
    };

    response.status(status);
    response.header('Content-Type', 'application/json;charset=utf-8');
    response.send(errorResponse);
  }
}

同样的,需要在main.ts中注册:

 app.useGlobalFilters(new HttpExceptionFilter());

现在请求遇到404时返回的数据格式也是我们自定义的了:

截屏2022-08-07 下午10.42.37.png

总结

本文介绍了Nest的三个重点知识点,即Configuration模块、拦截器和过滤器。其中,拦截器和过滤器都借鉴了AOP的编程思想,统一处理数据格式和处理http异常。

以上就是本文全部内容,水平有限,欢迎指教~

参考资料:

nestjs.com/

juejin.cn/post/703207…