You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NestJS gRPC网关POST/PATCH请求无限发送问题求助

问题描述

开发应用时突然发现订单无法加载、提交,Postman中POST和PATCH请求一直处于「Sending Request」无限循环,无法完成,但GET和DELETE请求正常,此前功能运行正常。

服务层代码

/* eslint-disable prettier/prettier */
import { Comprador, Compradores, CreateOneCompraDTO, PaginacaoDto, UpdateCompraDTO } from '@app/lib';
import { Injectable, NotFoundException, OnModuleInit } from '@nestjs/common';

//import {Compra, Compradores,CreateCompraDto,PaginacaoDto,UpdateCompraDto} from '@app/common'
import { randomUUID } from 'crypto';
import { Observable, Subject } from 'rxjs';


@Injectable()
export class DadosService implements OnModuleInit {
 private readonly compradores: Comprador[] = [];

  onModuleInit() {
    
  }
  create(CreateCompraDto: CreateOneCompraDTO) {
    const comprador: Comprador = {
      ...CreateCompraDto,
      dadosCompra: {},
      id: randomUUID(),
    }
    this.compradores.push(comprador)
    return comprador
  }
  findAll(): Compradores {
    return {compradores: this.compradores}
  }

  findOne(id: string): any{
    return this.compradores.find((comprador) => comprador.id === id)
  }

  updateOne(id: string, dados: UpdateCompraDTO) {
    const compradorId = this.compradores.findIndex((comp) => comp.id === id)
    if(compradorId !== -1){
      this.compradores[compradorId] = {
        ...this.compradores[compradorId],
        ...dados
      }
      return this.compradores[compradorId]
    }
    throw new NotFoundException(`Comprador do id: ${id}`)
  }

  remove(id: string){
    const deleteId = this.compradores.findIndex((del) => del.id === id)
    if(deleteId !== -1){
      return this.compradores.splice(deleteId, 1)[0]
      
    }
    throw new NotFoundException(`Comprador do id: ${id}`)
  }

  queryCompradores(paginacaoDTO: Observable<PaginacaoDto>): Observable<Compradores> {
    const subject = new Subject<Compradores>()

    const onNext = (paginacaoDTO: PaginacaoDto) => {
      const start = paginacaoDTO.page * paginacaoDTO.skip
      subject.next({
        compradores: this.compradores.splice(start, start + paginacaoDTO.skip),
      })
    };
    const onComplete = () => subject.complete()
    paginacaoDTO.subscribe({
      next: onNext,
      complete: onComplete
    })

    return subject.asObservable();
  }

}

控制器代码

/* eslint-disable @typescript-eslint/no-unused-vars *//* eslint-disable prettier/prettier */
//import { Controller } from '@nestjs/common';
import { Controller } from '@nestjs/common'
import { DadosService } from './dados.service';
import { Observable } from 'rxjs';
import { GrpcMethod } from '@nestjs/microservices';
import { Comprador, Compradores, CompradosServiceController, CreateOneCompraDTO, FindOneCompraDTO, PaginacaoDto, UpdateCompraDTO } from '@app/lib';
@Controller()
export class DadosController implements CompradosServiceController  {
  constructor(private readonly dadosService: DadosService) {}
  @GrpcMethod('CompradosService', 'queryComprador')
  queryComprador(request: Observable<PaginacaoDto>): Observable<Compradores> {
    return this.dadosService.queryCompradores(request)
  }
  @GrpcMethod('CompradosService', 'createOneCompra')
  createOneCompra(CreateCompraDto: CreateOneCompraDTO): Comprador{
    return this.dadosService.create(CreateCompraDto)

  }
  @GrpcMethod('CompradosService', 'findAllCompra')
  findAllCompra() {
    return this.dadosService.findAll()
  }
  @GrpcMethod('CompradosService', 'findOneCompra')
  findOneCompra(request: FindOneCompraDTO) {
    return this.dadosService.findOne(request.id)
  }
  @GrpcMethod('CompradosService', 'updateOneCompra')
  updateOneCompra(request: UpdateCompraDTO) {
    return this.dadosService.updateOne(request.id, request)
  }
  @GrpcMethod('CompradosService', 'deleteOneCompra')
  deleteOneCompra(FindOneCompraDTO: FindOneCompraDTO) {
    return this.dadosService.remove(FindOneCompraDTO.id)
  }
}

API网关控制器代码

/* eslint-disable prettier/prettier */

import { CreateOneCompraDTO, UpdateCompraDTO } from '@app/lib';
import { Body, Controller, Delete, Get, Param, Patch, Post } from '@nestjs/common';
import { CompradoresService } from 'apps/compra/src/compradores/compradores.service';
import { HashearSenha } from '../recursos/pipeHashearSenha';

@Controller('/compradores')
export class CompradoresController {
  constructor(private readonly compradoresService: CompradoresService) {}
  @Post()
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  criarCompradores(@Body() {senha, ...dados}: CreateOneCompraDTO, @Body('senha', HashearSenha) senhaHash: string){
    return this.compradoresService.criarComprador({senha: senhaHash, ...dados})

  }

  @Get()
  listarOsCOmpradores (){
      return this.compradoresService.listarCompradores()
  }
  
  @Get('/:id')
  listarOComprador(@Param('id') id: string){
    return this.compradoresService.listarComprador(id)
  }

  @Patch('/:id')
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  atualizarOComprador(@Param('id') id: string, @Body() {senha, ...dados}: UpdateCompraDTO, @Body('senha', HashearSenha) senhaHash: string){
    return this.compradoresService.atualizarComprador(id, {senha: senhaHash, ...dados})
  }
  
  @Delete('/:id')
  deletarOComprador(@Param('id') id: string){
    return this.compradoresService.deletarComprador(id)
  }

  @Post('compradores')
  compradoresStream(){
    return this.compradoresService.Compradores()
  }
}

API网关服务层代码

/* eslint-disable prettier/prettier */

//import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import { ReplaySubject } from 'rxjs';
import { AUTH_SERVICE } from './constantes';
import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
import { COMPRADOS_SERVICE_NAME, CompradosServiceClient, CreateOneCompraDTO, PaginacaoDto, UpdateCompraDTO } from '@app/lib';


@Injectable()
export class CompradoresService implements OnModuleInit {
    private compradorService: CompradosServiceClient
    constructor(
        @Inject(AUTH_SERVICE) private cliente: ClientGrpc

    ){}
    onModuleInit() {
        this.compradorService = this.cliente.getService<CompradosServiceClient>(COMPRADOS_SERVICE_NAME)
    }

    criarComprador(dados: CreateOneCompraDTO){
        return this.compradorService.createOneCompra(dados)
    }
    listarCompradores(){
        return this.compradorService.findAllCompra({})
    }
  
    listarComprador(id: string){
        return this.compradorService.findOneCompra({id})
    }

    atualizarComprador(id: string, dados: UpdateCompraDTO){
        return this.compradorService.updateOneCompra({id, ...dados})
    }

    deletarComprador(id: string){
        return this.compradorService.deleteOneCompra({id})
    }

    Compradores(){
        const Compradores$ = new ReplaySubject<PaginacaoDto>()

        Compradores$.next({ page: 0, skip: 200})
        Compradores$.next({ page: 1, skip: 200})
        Compradores$.next({ page: 2, skip: 200})
        Compradores$.next({ page: 3, skip: 200})

        Compradores$.complete();

        let parcialNumber = 1;

        this.compradorService.queryComprador(Compradores$).subscribe((empresas) => {
            console.log('Chunk', parcialNumber, empresas)
            parcialNumber += 1
        })
    }
}

问题定位与解决方案

核心问题

  1. queryCompradores方法破坏数据源:服务层中使用this.compradores.splice(start, start + paginacaoDTO.skip),splice是修改原数组的方法,会直接删除数组元素。当该方法被触发后,原compradores数组被清空,导致后续POST/PATCH请求无法正常处理数据。
  2. 网关Compradores方法无返回值:该方法内部订阅了GRPC流但未返回任何响应,导致对应的POST请求挂起,引发无限循环。

修复步骤

  1. 替换splice为slice,保护原数组
    修改服务层queryCompradores方法中的数据截取逻辑:

    const onNext = (paginacaoDTO: PaginacaoDto) => {
      const start = paginacaoDTO.page * paginacaoDTO.skip;
      // slice返回新数组,不修改原数据源
      subject.next({
        compradores: this.compradores.slice(start, start + paginacaoDTO.skip),
      });
    };
    
  2. 修复网关Compradores方法的返回逻辑
    修改方法使其返回可观察对象,让请求能正常结束:

    Compradores(): Observable<Compradores> {
      const Compradores$ = new ReplaySubject<PaginacaoDto>();
    
      Compradores$.next({ page: 0, skip: 200 });
      Compradores$.next({ page: 1, skip: 200 });
      Compradores$.next({ page: 2, skip: 200 });
      Compradores$.next({ page: 3, skip: 200 });
    
      Compradores$.complete();
    
      return this.compradorService.queryComprador(Compradores$);
    }
    
  3. 添加错误处理
    在GRPC调用中添加错误捕获,避免未处理的异常导致请求阻塞:

    import { catchError } from 'rxjs';
    import { BadRequestException } from '@nestjs/common';
    
    criarComprador(dados: CreateOneCompraDTO){
      return this.compradorService.createOneCompra(dados).pipe(
        catchError(err => {
          console.error('创建失败:', err);
          throw new BadRequestException(err.message);
        })
      );
    }
    

内容的提问来源于stack exchange,提问作者user22358808

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 01:10:05