总是返回 Mono 和 Flux 错误 returns 500

Returning Mono and Flux error always returns 500

我仍在尝试了解 WebFlux 异常是如何工作的,据我所知,当 return 在 Flux 或 Mono 中访问一个对象时,消费者应该收到从服务器。

但是,例如,当我 return 在 Mono 中出现 HTTPException 401 时,我在消费者中收到的响应主体与我发送的响应主体不同,我读取了 500 内部服务器错误而不是 401。

这是一个简单的控制器class我为这个问题制作的

package com.example.demo;

import javax.xml.ws.http.HTTPException;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import reactor.core.publisher.Mono;

@RestController
public class Controller {

@RequestMapping(
        path="/getExceptionMono",
        method = RequestMethod.GET,
        produces = MediaType.APPLICATION_JSON_VALUE)
    public Mono<String> getException(){
        return Mono.error(new HTTPException(401));
    }
}

这是消费者:

package com.example.demo;

import org.springframework.boot.CommandLineRunner;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;

@Component
public class ReactiveDemoConsumer implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        String url = "http://localhost:8080/getExceptionMono";
        WebClient.create(url)
            .get()
            .accept(MediaType.APPLICATION_JSON)
            .retrieve()
            .bodyToMono(String.class)
            .subscribe(value -> System.err.println("Received String: " + value),
                        err -> System.err.println("Received error " + err));

    }
}

这是消费者的控制台日志

Received error org.springframework.web.reactive.function.client.WebClientResponseException$InternalServerError: 500 Internal Server Error from GET http://localhost:8080/getExceptionMono

如何传递我的异常,以便消费者看到我在 Mono 中传递的原始异常? 希望我的问题很清楚,提前致谢

这不是 return 来自应用程序的 4xx 响应的正确方法。 应用程序中抛出的任何类型的异常都将被 WebClientResponseException 包装并在客户端作为 500 内部服务器错误接收。

改变它的一种方法是在你的控制器中有一个异常处理程序,如下所示:

  @ExceptionHandler({UnsupportedMediaTypeException.class})
  public ResponseEntity<String> exceptionHandler(Exception ex) {
    return ResponseEntity.badRequest().body(ex.getMessage());
  }

另一种方法是在您的代码中使用全局异常处理程序: (这里的 Ex1 类似于 HTTPException)

@Component
public class GlobalExceptionHandler implements ErrorWebExceptionHandler {

  @Override
  public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
    ServerHttpResponse httpResponse = exchange.getResponse();
    setResponseStatus(httpResponse, ex);
    return httpResponse.writeWith(Mono.fromSupplier(() -> {
      DataBufferFactory bufferFactory = httpResponse.bufferFactory();
      try {
        //Not displaying any error msg to client for internal server error
        String errMsgToSend = (httpResponse.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR) ? "" : ex.getMessage();
        return bufferFactory.wrap(new ObjectMapper().writeValueAsBytes(errMsgToSend));
      } catch (JsonProcessingException e) {
        return bufferFactory.wrap(new byte[0]);
      }
    }));
  }

  private void setResponseStatus(ServerHttpResponse httpResponse, Throwable ex) {
    if (ex instanceof Ex1 || ex instanceof Ex2 || ex instanceof Ex3) {
      httpResponse.setStatusCode(HttpStatus.BAD_REQUEST);
    } else {
      httpResponse.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }
}

或者你可以像这样重写你的控制器:

@RequestMapping(
        path="/getExceptionMono",
        method = RequestMethod.GET,
        produces = MediaType.APPLICATION_JSON_VALUE)
    public Mono<ResponseEntity> getException(){
        return Mono.just(ResponseEntity.badRequest().build());
    }
}

然后在webclient代码中,你可以这样做:

webClient
 .get()
 .uri("/some/url")
 .exchange()
 .flatMap(clientResponse -> {
     if (clientResponse.statusCode().is5xxServerError()) {
        //do something and return some mono
     }
     else if(clientResponse.statusCode().is4xxClientError()) {
        //do something and return some mono
     }
     else {
       //do something and return some mono  
     }
});