nestjs微服务中如何处理异常?

问题描述 投票:0回答:2

我想在我的 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());
但它永远不会到达那里。

error-handling nestjs microservices
2个回答
2
投票

从我在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)););
  }
}

请注意

catch()
方法必须返回一个Observable


0
投票

意外行为的原因:

您已从微服务控制器抛出 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;
}

这应该可行

© www.soinside.com 2019 - 2024. All rights reserved.