问题描述
在我的请求处理程序中,如果无法将传入的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;
}
在这里您可以阅读有关反应堆中错误处理的更多信息
,@ 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>>
,并且无法从错误状态生成一个(添加时,邮件正文)。