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

Mar*_*rco 5 java spring-webflux spring-reactive

在我的请求处理程序中,如果传入的内容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());
      }
  }
Run Code Online (Sandbox Code Playgroud)

body(T body)方法更改返回的类型Mono,使其成为(假设只发送 a String) a Mono<ResponseEntity<String>>; 但是,将方法的返回类型更改为Mono<ResponseEntity<?>>也不起作用:

        ...
        return Mono.just(ResponseEntity.badRequest().body(
            MALFORMED_OBJECT_ID.replace("{}", accountId)));
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

我能想到解决这个问题的唯一方法是message向我的对象添加一个字段Account并返回该字段,但这确实是一个可怕的黑客行为。

Mar*_*rco 2

@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()));
  }
Run Code Online (Sandbox Code Playgroud)

要实际message在返回的正文中查看 ,您需要添加server.error.include-message=alwaysapplication.properties请参阅此处)。

使用onError()在这里不起作用(我确实尝试过,在它的所有变体中),因为它需要 aMono<ResponseEntity<Account>>并且无法从错误状态生成一个(添加消息正文时)。