【问题标题】:Returning Mono and Flux error always returns 500返回 Mono 和 Flux 错误总是返回 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 结果 = client.get() .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON) .retrieve() .onStatus(HttpStatus::is4xxClientError, response -> ... ) .onStatus(HttpStatus::is5xxServerError, response -> ...) .bodyToMono(Person.class);
  • 刚刚尝试了这个建议,它只是跳过 4xx 谓词直接到 5xx,打印响应并返回“org.springframework.web.reactive.function.client.DefaultClientResponse@c7a55c43”
  • @EdenDupont 请看一遍我的回答。我希望它能澄清你的疑惑

标签: java exception mono spring-webflux flux


【解决方案1】:

这不是从应用程序返回 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 > getException(){ return Mono.just(ResponseEntity.ok().body(new String("MyString"))); } 正确吗?
  • 如果直接返回对象,spring会默认设置响应码为200。如果您需要 202 接受或 204 无内容之类的其他内容,则需要返回响应实体
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-28
  • 1970-01-01
  • 2017-05-29
相关资源
最近更新 更多