如何在NestJS中处理RpcException

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

我正在尝试构建一个具有多个微服务和一个 REST API 作为与微服务通信的网关的 NestJS 后端。对于网关和微服务之间的通信,我使用 gRPC。 简单的通信已经可以工作,但现在我想在微服务中实现错误处理。 NestJS 文档指出,这可以通过 RpcException 类实现。https://docs.nestjs.com/microservices/exception-filters但是,如果我尝试捕获网关 API 中的异常,我只会得到“ERROR [ExceptionsHandler]” 2 UNKNOWN: ...”,后跟异常错误消息。

网关API: 用户.控制器.ts

import { Controller, Get, Param } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { UserViewModel } from '../../proto/build/service-name/user';
import { UserService } from './user.service';

@Controller('user')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Get(':id')
  async getUserById(@Param('id') id: number): Promise<UserViewModel> {
    try {
      return await this.userService.getUserById(id);
    } catch (error) {
      return error;
    }
  }
}

网关API: 用户.service.ts

import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import {
  IUserService,
  UserViewModel,
} from '../../proto/build/service-name/user';

@Injectable()
export class UserService implements OnModuleInit {
  private grpcService: IUserService;

  constructor(@Inject('USER_PACKAGE') private client: ClientGrpc) {}

  onModuleInit() {
    this.grpcService = this.client.getService<IUserService>('IUserService');
  }

  async getUserById(id: number): Promise<UserViewModel> {
    return this.grpcService.getUserById({ id });
  }
}

微服务: 用户.控制器.ts

import { Metadata } from '@grpc/grpc-js';
import { Controller } from '@nestjs/common';
import { GrpcMethod, RpcException } from '@nestjs/microservices';
import { User } from './../../node_modules/.prisma/client/index.d';
import { PrismaService } from '../prisma/prisma.service';
import { UserViewModel, GetUserById } from '../proto/build/service-name/user';

@Controller()
export class UserController {
  constructor(private readonly prisma: PrismaService) {}

  @GrpcMethod('IUserService', 'getUserById')
  async getUserById(
    data: GetUserById,
    metadata: Metadata,
  ): Promise<UserViewModel> {
    const user: User = await this.prisma.user.findFirst({
      where: { id: { equals: data.id } },
    });

    if (!user) {
      throw new RpcException('User not found');
    }

    return { name: user.name, email: user.email };
  }
}

nestjs grpc nestjs-gateways
3个回答
5
投票

我在使用 API 网关构建微服务时也遇到了这个问题。我想出的解决方案是我在这里找到的答案的组合,但允许您在 NestJS 异常中使用构建。

所以基本上我用微服务中的

RpcException
包装内置的 NestJS HTTP 异常。然后,您可以捕获 api 网关中的执行并使用过滤器处理它。
RcpException
消息可以是
string
object
,这允许您将内置的HTTP异常(
NotFoundException
UnauthorizedException
等)作为消息传递,这样您就不必处理状态代码。

微服务

// Some service method to fetch a product by id
public async findById(id: number): Promise<Product> {
  // ...
  if (!product) {
    throw new RpcException(
      new NotFoundException("Product was not found!");
    );
  }
  //...
}

网关控制器

@Get(':productId')
public getProductById(@Param('productId') productId: number): Observable<any> {
  return this._productsServiceClient
    .send({ cmd: 'find-by-id' }, { productId })
    .pipe(catchError(error => throwError(() => new RpcException(error.response))))
}

异常过滤器

import { ArgumentsHost, Catch, ExceptionFilter } from '@nestjs/common';
import { RpcException } from '@nestjs/microservices';
import { Response } from 'express';

@Catch(RpcException)
export class RpcExceptionFilter implements ExceptionFilter {
  catch(exception: RpcException, host: ArgumentsHost) {
    const error: any = exception.getError();
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    
    response
      .status(error.statusCode)
      .json(error);
  }
}

注册过滤器

// main.ts
app.useGlobalFilters(new RpcExceptionFilter());

1
投票
  1. 扩展RpcException并在微服务中使用。
  2. 在网关(发送方)中创建 AllGlobalExceptionsFilter
  3. 用于网关(发送器)控制器
export interface IRpcException {
  message: string;
  status: number;
}

export class FitRpcException extends RpcException implements IRpcException {
  constructor(message: string, statusCode: HttpStatus) {
    super(message);
    this.initStatusCode(statusCode);
  }
  public status: number;

  private initStatusCode(statusCode) {
    this.status = statusCode;
  }
}

@Catch()
export class AllGlobalExceptionsFilter implements ExceptionFilter {
  constructor(private readonly httpAdapterHost: HttpAdapterHost) {}

  catch(exception: IRpcException, host: ArgumentsHost): void {
    const { httpAdapter } = this.httpAdapterHost;
    const ctx = host.switchToHttp();

    const httpStatus = exception.status
      ? exception.status
      : HttpStatus.INTERNAL_SERVER_ERROR;

    const responseBody = {
      statusCode: httpStatus,
      timestamp: new Date().toISOString(),
      path: httpAdapter.getRequestUrl(ctx.getRequest()),
      message: exception.message,
    };

    httpAdapter.reply(ctx.getResponse(), responseBody, httpStatus);
  }
}

@UseFilters(AllGlobalExceptionsFilter)


0
投票

我已经尝试过nestjs 9。只是

throw new RpcException({ code: grpc.status.UNAVAILABLE, message: 'error' })

ps: grpc 需要这样导入

从'@grpc/grpc-js'导入*作为grpc。

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