我正在参加掘金创作者训练营第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, //根据实体自动创建数据库表, 生产环境建议关闭
}),
}),
使用拦截器统一接口数据格式
在上篇文章中,获取用户列表接口是这么返回数据的:
这样的格式其实并不符合实际需求。我们一般希望拿到这样的数据格式:
{
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());
再次请求接口,可以看到数据按统一格式返回了:
使用过滤器统一处理异常
通过过滤器捕获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时返回的数据格式也是我们自定义的了:
总结
本文介绍了Nest的三个重点知识点,即Configuration模块、拦截器和过滤器。其中,拦截器和过滤器都借鉴了AOP的编程思想,统一处理数据格式和处理http异常。
以上就是本文全部内容,水平有限,欢迎指教~
参考资料: