Spring Reactive WebFlux-如何自定义BadRequest错误消息

问题描述

在我的请求处理程序中,如果无法将传入的accountId转换为有效的ObjectId,我想捕获错误并发回有意义的消息;但是,这样做会导致返回类型不兼容,并且我无法弄清楚如何实现这种非常简单的用例。

我的代码:

  @GetMapping("/{accountId}")
  public Mono<ResponseEntity<Account>> get(@PathVariable String accountId) {
      log.debug(GETTING_DATA_FOR_ACCOUNT,accountId);

      try {
        ObjectId id = new ObjectId(accountId);
        return repository.findById(id)
            .map(ResponseEntity::ok)
            .switchIfEmpty(Mono.just(ResponseEntity.notFound().build()));
      } catch (IllegalArgumentException ex) {
        log.error(MALFORMED_OBJECT_ID,accountId);
        // TODO(marco): find a way to return the custom error message. This seems to be currently
        //  impossible with the Reactive API,as using body(message) changes the return type to
        //  be incompatible (and Mono<ResponseEntity<?>> does not seem to cut it).
        return Mono.just(ResponseEntity.badRequest().build());
      }
  }

body(T body)方法将返回的Mono的类型更改为String(假设一个人只是发送Mono<ResponseEntity<String>>)。但是,将方法的返回类型更改为Mono<ResponseEntity<?>>也不起作用:

        ...
        return Mono.just(ResponseEntity.badRequest().body(
            MALFORMED_OBJECT_ID.replace("{}",accountId)));

,因为它在另一个return语句上给出了“不兼容类型”错误:

error: incompatible types: Mono<ResponseEntity<Account>> cannot be converted to Mono<ResponseEntity<?>>
            .switchIfEmpty(Mono.just(ResponseEntity.notFound().build()));

很明显,将方法的返回类型更改为Mono<?>是可以的,但是响应是ResponseEntity的序列化JSON,这不是我想要的。

我也尝试过使用onErrorXxxx()方法,但是它们在这里也不起作用,因为甚至在计算通量之前,转换错误就发生了,我只收到“ vanilla” 400错误,并带有一条空消息

我想解决此问题的唯一方法是在我的message对象中添加一个Account字段并返回该字段,但这确实是一个可怕的骇客。

解决方法

这里是如何处理反应式应用程序中的错误的示例。我们不使用try/catch,而是创建一个Mono#error,它将中断反应式链并将错误传播到客户端。

return Mono.just(id)
    .flatMap(id -> {
        if(validate(id))
            return Mono.error(new ResponseStatusException(HttpStatus.BAD_REQUEST));
        return Mono.just(id);
     }).flatMap(id -> repository.findById(id)
            .switchIfEmpty(Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND))));
     }

private boolean validate(int id) {
    // some random validation
    return true;
}

在这里您可以阅读有关反应堆中错误处理的更多信息

Error Handling Operator

,

@ thomas-andolf的回答帮助我找出了实际的解决方案。

对于以后遇到任何麻烦的人,这是我真正解决难题的方法(是的,您仍然需要try/catch来拦截ObjectId构造函数引发的错误):>

  @GetMapping("/{accountId}")
  public Mono<ResponseEntity<Account>> get(@PathVariable String accountId) {
    return Mono.just(accountId)
        .map(acctId -> {
          try {
            return new ObjectId(accountId);
          } catch (IllegalArgumentException ex) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST,MALFORMED_OBJECT_ID));
          }
        })
        .flatMap(repository::findById)
        .map(ResponseEntity::ok)
        .switchIfEmpty(Mono.just(ResponseEntity.notFound().build()));
  }

要在返回的正文中实际看到message,您需要在server.error.include-message=always中添加application.properties(请参见here)。

在这里无法使用onError()(我尝试过所有变体),因为它需要Mono<ResponseEntity<Account>>,并且无法从错误状态生成一个(添加时,邮件正文)。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...