我想在我的 NestJS 应用程序中正确处理异常。我观看了视频、阅读了博客并在 stackoverflow 上找到了一些答案,但实现正确的异常处理对我来说太混乱了。
用户微服务(控制器)中的登录方法:
@GrpcMethod('UserService', 'Login')
async login(@Payload() payload: Login): Promise<any> {
try {
const { email, password } = payload;
const { user, token, refreshToken } = await this.usersService.login(email, password);
return {
user: {
email: user.email,
userId: user.id,
name: user.name,
phone: user.phone,
},
token,
refreshToken,
};
} catch (error) {
throw new RpcException(error);
}
}
用户微服务(服务)中的登录方法以及通过电子邮件查找用户:
async findByEmail(email: string): Promise<any> {
const user = this.userModel.findOne({ email }).exec();
if (!user) return 'User not found!!';
return user;
}
async login(email: string, password: string): Promise<any> {
try {
const user = (await this.findByEmail(email)) as User;
const comparePassword = await passwordService.comparePasswords(password, user.password);
if (user && comparePassword) {
const { token, refreshToken } = await this.sessionService.createSession(user._id, {
userId: user._id,
type: user.type,
});
return { user, token, refreshToken };
}
} catch (error) {
throw new Error(error);
}
}
API网关文件中的控制器方法:
@Post('login')
async login(@Body() loginData: Login): Promise<any> {
try {
const user = await firstValueFrom(this.userService.Login(loginData));
return sendSuccess(user, 'Log-in successful.');
} catch (error) {
return sendError(error.details, 404);
}
}
我想处理所有异常并抛出适当的异常。就像如果电子邮件未注册,则抛出“未找到电子邮件”,如果密码错误,则抛出“无效凭据”等等。 我怎样才能做到这一点?
请求-响应实用程序代码:
export function sendSuccess<T>(data: T, message: string = 'Success', statusCode: number = HttpStatus.OK): ApiResponse<T> {
return {
status: 'success',
statusCode,
message,
data,
};
}
export function sendError<T>(message: string, statusCode: number): ApiResponse<T> {
return {
status: 'error',
statusCode,
message,
data: null,
};
}
export interface ApiResponse<T> {
status: 'success' | 'error';
statusCode: number;
message: string;
data: T | null;
}
@Catch()
export class AllExceptionsFilter {
catch(exception: any, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;
const message = exception instanceof HttpException ? exception.message : 'Internal Server Error';
response.status(status).json(sendError(message, status));
}
}
我已经在我的 main.ts 文件中注册了 AllExceptionsFilter:
app.useGlobalFilters(new AllExceptionsFilter());
但它永远不会到达那里。
从我在docs中可以找到的内容来看,
AllExceptionsFilter
应该实现RpcExceptionFilter
并使用RpcException
作为@Catch
装饰器。 import { Catch, RpcExceptionFilter, ArgumentsHost } from '@nestjs/common';
import { Observable, throwError } from 'rxjs';
import { RpcException } from '@nestjs/microservices';
@Catch(RpcException)
export class ExceptionFilter implements RpcExceptionFilter<RpcException> {
catch(
exception: any, host: ArgumentsHost
): Observable<any> {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;
const message = exception instanceof HttpException ? exception.message : 'Internal Server Error';
return throwError(() => response.status(status).json(sendError(message, status)););
}
}
请注意
方法必须返回一个Observablecatch()
意外行为的原因:
您已从微服务控制器抛出 RpcException,该异常将被您的 API 网关控制器捕获。 但是在你的 API 网关控制器中你已经返回了一个返回的函数
return {
status: 'error',
statusCode,
message,
data: null,
};
所以你的 AllExceptionsFilter 永远不会捕获它,因为你的 API GateWay 永远不会抛出错误。
可能的解决方案:
如果您甚至将其从 API 网关中抛出,它仍然无法按照您想要的方式响应。因为当您抛出错误时,它将以对象的形式出现,但在 AllExceptionsFilter 中,它会检查它是否是 HttpException 的实例,抛出消息和状态,否则抛出内部服务器错误。 所以它会抛出响应显示类似这样的内容
{
"status": "error",
"statusCode": 500,
"message": "Internal Server Error",
"data": null
}
现在要抓住你可以这样修改:
//Default Response
let status = HttpStatus.INTERNAL_SERVER_ERROR;
let message = 'Internal Server Error';
//Modify the response based on the exception
if (exception instanceof HttpException) {
//Handle HttpException
}
//Modify the response based on the exception
if (exception instanceof Error || typeof exception === 'object') {
// Now this will catch the error you have thrown form API Gate Way
//This is Just example, here you have to know how your exception is coming
message = exception.message;
status = exception.status;
}
更好的方法:
从你的微服务控制器抛出 RpcExcpetion() 像这样
throw new RpcException({
statusCode:400,
message:'Any Message You want'
})
为您的微服务创建异常过滤器
@Catch(RpcException)
export class MicroServiceExceptionFilter
implements RpcExceptionFilter<RpcException>
{
catch(exception: RpcException): Observable<any> {
const micro_service_Response = exception.getError();
const error = (micro_service_Response as { error }).error;
return throwError(() => msCustomError(error));
}
}
function msCustomError(error: IRpcException): IRpcException {
return {
statusCode: error.statusCode,
message: error.message,
};
}
这不会返回具有 IRpcExcpetion 结构的 Observables
现在在你的 AllExceptionsFilter 中捕获这样的
if ((exception instanceof Error || typeof exception === 'object') &&
exception != null) {
//For catching Exceptions from Micro-Services
const _error = exception as IRpcException;
status = _error?.statusCode;
message = _error?.message;
}
这应该可行