返回 Mono 和 Flux 错误总是返回 500
Posted
技术标签:
【中文标题】返回 Mono 和 Flux 错误总是返回 500【英文标题】:Returning Mono and Flux error always returns 500 【发布时间】:2020-10-28 22:12:36 【问题描述】:据我所知,我仍在尝试了解 WebFlux 异常的工作原理,当返回 Flux 或 Mono 中的对象时,消费者应该收到从服务器发送的相同异常。
但是,例如,当我在 Mono 中返回 HTTPException 401 时,我在消费者中收到的响应正文与我发送的不同,我读到的是 500 Internal Server 错误而不是 401。
这是我为这个问题制作的一个简单的控制器类
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 中传递的原始异常? 希望我的问题很清楚,提前谢谢
【问题讨论】:
可以使用onStatus查看响应码吗? Mono这不是从应用程序返回 4xx 响应的正确方法。 应用程序中抛出的任何类型的异常都将被 WebClientResponseException 包装,并在客户端作为 500 Internal Server Error 接收。
改变这种情况的一种方法是在控制器中设置一个异常处理程序,如下所示:
@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
);
【讨论】:
现在事情似乎有点清楚了,所以发送对象的正确方法也被一个 ResponseEntity 包裹,比如说我想发送一个字符串,它在控制器中看起来像这样:public Mono以上是关于返回 Mono 和 Flux 错误总是返回 500的主要内容,如果未能解决你的问题,请参考以下文章
如何从返回 Mono 的 Stream 创建 Flux? [复制]
让 HTTP 端点返回 Flux/Mono 实例而不是 DTO 的好处 [关闭]
如何使用 Micrometer Timer 记录异步方法的持续时间(返回 Mono 或 Flux)