WebExceptionHandler : 如何使用 Spring Webflux 编写主体
WebExceptionHandler : How to write a body with Spring Webflux
我想通过添加 WebExceptionHandler 来处理 api 的异常。
我可以更改状态代码,但当我想更改响应正文时卡住了:例如添加异常消息或自定义对象。
有没有人有例子?
我如何添加 WebExceptionHandler:
HttpHandler httpHandler = WebHttpHandlerBuilder.webHandler(toHttpHandler(routerFunction))
.prependExceptionHandler((serverWebExchange, exception) -> {
exchange.getResponse().setStatusCode(myStatusGivenTheException);
exchange.getResponse().writeAndFlushWith(??)
return Mono.empty();
}).build();
WebExceptionHandler
级别比较低,所以要直接和request/response交易所打交道。
注意:
-
Mono<Void>
return 类型应该表示响应处理的结束;这就是为什么它应该连接到 Publisher
写入响应
- 在此级别,您将直接处理数据缓冲区(没有可用的序列化支持)
您的 WebExceptionHandler
可能如下所示:
(serverWebExchange, exception) -> {
exchange.getResponse().setStatusCode(myStatusGivenTheException);
byte[] bytes = "Some text".getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
return exchange.getResponse().writeWith(Flux.just(buffer));
}
鉴于答案,我使用这种方式序列化对象:
Mono<DataBuffer> db = commonsException.getErrorsResponse().map(errorsResponse -> {
ObjectMapper objectMapper = new ObjectMapper();
try {
return objectMapper.writeValueAsBytes(errorsResponse);
} catch (JsonProcessingException e) {
return e.getMessage().getBytes();
}
}).map(s -> exchange.getResponse().bufferFactory().wrap(s));
exchange.getResponse().getHeaders().add("Content-Type", "application/json");
exchange.getResponse().setStatusCode(commonsException.getHttpStatus());
return exchange.getResponse().writeWith(db);
ServerResponse
有一个方法 writeTo
可以用来将你的 body 写入 ServerExchange
(Spring 框架就是这样做的)。唯一的问题是你必须提供 Context
作为第二个参数,所以我刚刚从框架实现中复制了 HandlerStrategiesResponseContext
。
确保您至少使用 Spring Boot 2.0.0 M2,在此版本之前 WebExceptionHandler
在使用 RouterFunctions
时未注册。
import org.springframework.http.HttpStatus
import org.springframework.http.HttpStatus.*
import org.springframework.http.codec.HttpMessageWriter
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.server.HandlerStrategies
import org.springframework.web.reactive.function.server.ServerResponse
import org.springframework.web.reactive.result.view.ViewResolver
import org.springframework.web.server.ServerWebExchange
import org.springframework.web.server.WebExceptionHandler
@Component
class GlobalErrorHandler() : WebExceptionHandler {
override fun handle(exchange: ServerWebExchange, ex: Throwable): Mono<Void> =
handle(ex)
.flatMap {
it.writeTo(exchange, HandlerStrategiesResponseContext(HandlerStrategies.withDefaults()))
}
.flatMap {
Mono.empty<Void>()
}
fun handle(throwable: Throwable): Mono<ServerResponse> {
return when (throwable) {
is EntityNotFoundException -> {
createResponse(NOT_FOUND, "NOT_FOUND", "Entity not found, details: ${throwable.message}")
}
else -> {
createResponse(INTERNAL_SERVER_ERROR, "GENERIC_ERROR", "Unhandled exception")
}
}
}
fun createResponse(httpStatus: HttpStatus, code: String, message: String): Mono<ServerResponse> =
ServerResponse.status(httpStatus).syncBody(ApiError(code, message))
}
private class HandlerStrategiesResponseContext(val strategies: HandlerStrategies) : ServerResponse.Context {
override fun messageWriters(): List<HttpMessageWriter<*>> {
return this.strategies.messageWriters()
}
override fun viewResolvers(): List<ViewResolver> {
return this.strategies.viewResolvers()
}
}
对于寻找编写 JSON 响应主体的方法的任何人,这里有一个 Kotlin 代码示例:
fun writeBodyJson(body: Any, exchange: ServerWebExchange) =
exchange.response.writeWith(
Jackson2JsonEncoder().encode(
Mono.just(body),
exchange.response.bufferFactory(),
ResolvableType.forInstance(body),
MediaType.APPLICATION_JSON_UTF8,
Hints.from(Hints.LOG_PREFIX_HINT, exchange.logPrefix)
)
)
不是 100% 确定这是要走的路,想听听一些意见。
我想通过添加 WebExceptionHandler 来处理 api 的异常。 我可以更改状态代码,但当我想更改响应正文时卡住了:例如添加异常消息或自定义对象。
有没有人有例子?
我如何添加 WebExceptionHandler:
HttpHandler httpHandler = WebHttpHandlerBuilder.webHandler(toHttpHandler(routerFunction))
.prependExceptionHandler((serverWebExchange, exception) -> {
exchange.getResponse().setStatusCode(myStatusGivenTheException);
exchange.getResponse().writeAndFlushWith(??)
return Mono.empty();
}).build();
WebExceptionHandler
级别比较低,所以要直接和request/response交易所打交道。
注意:
-
Mono<Void>
return 类型应该表示响应处理的结束;这就是为什么它应该连接到Publisher
写入响应 - 在此级别,您将直接处理数据缓冲区(没有可用的序列化支持)
您的 WebExceptionHandler
可能如下所示:
(serverWebExchange, exception) -> {
exchange.getResponse().setStatusCode(myStatusGivenTheException);
byte[] bytes = "Some text".getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
return exchange.getResponse().writeWith(Flux.just(buffer));
}
鉴于答案,我使用这种方式序列化对象:
Mono<DataBuffer> db = commonsException.getErrorsResponse().map(errorsResponse -> {
ObjectMapper objectMapper = new ObjectMapper();
try {
return objectMapper.writeValueAsBytes(errorsResponse);
} catch (JsonProcessingException e) {
return e.getMessage().getBytes();
}
}).map(s -> exchange.getResponse().bufferFactory().wrap(s));
exchange.getResponse().getHeaders().add("Content-Type", "application/json");
exchange.getResponse().setStatusCode(commonsException.getHttpStatus());
return exchange.getResponse().writeWith(db);
ServerResponse
有一个方法 writeTo
可以用来将你的 body 写入 ServerExchange
(Spring 框架就是这样做的)。唯一的问题是你必须提供 Context
作为第二个参数,所以我刚刚从框架实现中复制了 HandlerStrategiesResponseContext
。
确保您至少使用 Spring Boot 2.0.0 M2,在此版本之前 WebExceptionHandler
在使用 RouterFunctions
时未注册。
import org.springframework.http.HttpStatus
import org.springframework.http.HttpStatus.*
import org.springframework.http.codec.HttpMessageWriter
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.server.HandlerStrategies
import org.springframework.web.reactive.function.server.ServerResponse
import org.springframework.web.reactive.result.view.ViewResolver
import org.springframework.web.server.ServerWebExchange
import org.springframework.web.server.WebExceptionHandler
@Component
class GlobalErrorHandler() : WebExceptionHandler {
override fun handle(exchange: ServerWebExchange, ex: Throwable): Mono<Void> =
handle(ex)
.flatMap {
it.writeTo(exchange, HandlerStrategiesResponseContext(HandlerStrategies.withDefaults()))
}
.flatMap {
Mono.empty<Void>()
}
fun handle(throwable: Throwable): Mono<ServerResponse> {
return when (throwable) {
is EntityNotFoundException -> {
createResponse(NOT_FOUND, "NOT_FOUND", "Entity not found, details: ${throwable.message}")
}
else -> {
createResponse(INTERNAL_SERVER_ERROR, "GENERIC_ERROR", "Unhandled exception")
}
}
}
fun createResponse(httpStatus: HttpStatus, code: String, message: String): Mono<ServerResponse> =
ServerResponse.status(httpStatus).syncBody(ApiError(code, message))
}
private class HandlerStrategiesResponseContext(val strategies: HandlerStrategies) : ServerResponse.Context {
override fun messageWriters(): List<HttpMessageWriter<*>> {
return this.strategies.messageWriters()
}
override fun viewResolvers(): List<ViewResolver> {
return this.strategies.viewResolvers()
}
}
对于寻找编写 JSON 响应主体的方法的任何人,这里有一个 Kotlin 代码示例:
fun writeBodyJson(body: Any, exchange: ServerWebExchange) =
exchange.response.writeWith(
Jackson2JsonEncoder().encode(
Mono.just(body),
exchange.response.bufferFactory(),
ResolvableType.forInstance(body),
MediaType.APPLICATION_JSON_UTF8,
Hints.from(Hints.LOG_PREFIX_HINT, exchange.logPrefix)
)
)
不是 100% 确定这是要走的路,想听听一些意见。