我偶然发现了这个,所以我想我不妨发布我的代码。
我所做的是创建一个全局处理程序,负责处理来自 Web 客户端的请求和响应错误。这是在 Kotlin 中,但当然可以很容易地转换为 Java。这扩展了默认行为,因此您可以确保在客户处理之上获得所有自动配置。
正如您所看到的,这并没有真正做任何自定义,它只是将 Web 客户端错误转换为相关响应。对于响应错误,代码和响应正文只是简单地传递给客户端。对于当前的请求错误,它只处理连接问题,因为这就是我所关心的(目前),但正如您所见,它可以轻松扩展。
@Configuration
class WebExceptionConfig(private val serverProperties: ServerProperties) {
@Bean
@Order(-2)
fun errorWebExceptionHandler(
errorAttributes: ErrorAttributes,
resourceProperties: ResourceProperties,
webProperties: WebProperties,
viewResolvers: ObjectProvider<ViewResolver>,
serverCodecConfigurer: ServerCodecConfigurer,
applicationContext: ApplicationContext
): ErrorWebExceptionHandler? {
val exceptionHandler = CustomErrorWebExceptionHandler(
errorAttributes,
(if (resourceProperties.hasBeenCustomized()) resourceProperties else webProperties.resources) as WebProperties.Resources,
serverProperties.error,
applicationContext
)
exceptionHandler.setViewResolvers(viewResolvers.orderedStream().collect(Collectors.toList()))
exceptionHandler.setMessageWriters(serverCodecConfigurer.writers)
exceptionHandler.setMessageReaders(serverCodecConfigurer.readers)
return exceptionHandler
}
}
class CustomErrorWebExceptionHandler(
errorAttributes: ErrorAttributes,
resources: WebProperties.Resources,
errorProperties: ErrorProperties,
applicationContext: ApplicationContext
) : DefaultErrorWebExceptionHandler(errorAttributes, resources, errorProperties, applicationContext) {
override fun handle(exchange: ServerWebExchange, throwable: Throwable): Mono<Void> =
when (throwable) {
is WebClientRequestException -> handleWebClientRequestException(exchange, throwable)
is WebClientResponseException -> handleWebClientResponseException(exchange, throwable)
else -> super.handle(exchange, throwable)
}
private fun handleWebClientResponseException(exchange: ServerWebExchange, throwable: WebClientResponseException): Mono<Void> {
exchange.response.headers.add("Content-Type", "application/json")
exchange.response.statusCode = throwable.statusCode
val responseBodyBuffer = exchange
.response
.bufferFactory()
.wrap(throwable.responseBodyAsByteArray)
return exchange.response.writeWith(Mono.just(responseBodyBuffer))
}
private fun handleWebClientRequestException(exchange: ServerWebExchange, throwable: WebClientRequestException): Mono<Void> {
if (throwable.rootCause is ConnectException) {
exchange.response.headers.add("Content-Type", "application/json")
exchange.response.statusCode = HttpStatus.BAD_GATEWAY
val responseBodyBuffer = exchange
.response
.bufferFactory()
.wrap(ObjectMapper().writeValueAsBytes(customErrorWebException(exchange, HttpStatus.BAD_GATEWAY, throwable.message)))
return exchange.response.writeWith(Mono.just(responseBodyBuffer))
} else {
return super.handle(exchange, throwable)
}
}
private fun customErrorWebException(exchange: ServerWebExchange, status: HttpStatus, message: Any?) =
CustomErrorWebException(
Instant.now().toString(),
exchange.request.path.value(),
status.value(),
status.reasonPhrase,
message,
exchange.request.id
)
}
data class CustomErrorWebException(
val timestamp: String,
val path: String,
val status: Int,
val error: String,
val message: Any?,
val requestId: String,
)