Spring Reactive WebFlux - 如何自定义 BadRequest 错误信息
Spring Reactive WebFlux - how to customize the BadRequest error message
在我的请求处理程序中,如果传入的 accountId
无法转换为有效的 ObjectId
我想捕获错误并发回有意义的消息;但是,这样做会导致 return 类型不兼容,我不知道如何实现这个非常简单的用例。
我的代码:
@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)
方法改变了 returned Mono
的类型,因此它是(假设只发送一个 String
)一个 Mono<ResponseEntity<String>>
;但是,将方法的 return 类型更改为 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()));
显然,将方法的 return 类型更改为 Mono<?>
会起作用,但响应是 ResponseEntity
的序列化 JSON,这不是什么我要。
我也尝试过使用 onErrorXxxx()
方法,但它们在这里也不起作用,因为转换错误甚至在计算 Flux 之前就发生了,我只是得到一个“vanilla”400 错误空消息。
我能想到的解决此问题的唯一方法是向我的 Account
对象和 return 添加一个 message
字段,但这确实是一个可怕的 hack。
@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
,您需要在 application.properties
中添加 server.error.include-message=always
(参见 )。
使用 onError()
在这里不起作用(我确实尝试过,在它的所有变体中)因为它需要一个 Mono<ResponseEntity<Account>>
并且无法从错误状态生成一个(当添加邮件正文)。
在我的请求处理程序中,如果传入的 accountId
无法转换为有效的 ObjectId
我想捕获错误并发回有意义的消息;但是,这样做会导致 return 类型不兼容,我不知道如何实现这个非常简单的用例。
我的代码:
@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)
方法改变了 returned Mono
的类型,因此它是(假设只发送一个 String
)一个 Mono<ResponseEntity<String>>
;但是,将方法的 return 类型更改为 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()));
显然,将方法的 return 类型更改为 Mono<?>
会起作用,但响应是 ResponseEntity
的序列化 JSON,这不是什么我要。
我也尝试过使用 onErrorXxxx()
方法,但它们在这里也不起作用,因为转换错误甚至在计算 Flux 之前就发生了,我只是得到一个“vanilla”400 错误空消息。
我能想到的解决此问题的唯一方法是向我的 Account
对象和 return 添加一个 message
字段,但这确实是一个可怕的 hack。
@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
,您需要在 application.properties
中添加 server.error.include-message=always
(参见
使用 onError()
在这里不起作用(我确实尝试过,在它的所有变体中)因为它需要一个 Mono<ResponseEntity<Account>>
并且无法从错误状态生成一个(当添加邮件正文)。