如何在 Spring webflux WebExceptionHandlder 中将消息写入 http 正文
How to write messages to http body in Spring webflux WebExceptionHandlder
在 的帮助下,我通过自定义 WebExceptionHandler
在我的 Spring 5 WebFlux 应用程序中进行了部分异常处理工作,但是当我想以友好的方式转换现有异常时给客户端的消息,它不起作用。
我的自定义 WebExceptionHandler 如下所示,完整代码为 here。
WebExchangeBindException cvex = (WebExchangeBindException) ex;
Map<String, String> errors = new HashMap<>();
log.debug("errors:" + cvex.getFieldErrors());
cvex.getFieldErrors().forEach(ev -> errors.put(ev.getField(), ev.getDefaultMessage()));
log.debug("handled errors::" + errors);
try {
DataBuffer db = new DefaultDataBufferFactory().wrap(objectMapper.writeValueAsBytes(errors));
exchange.getResponse().setStatusCode(HttpStatus.UNPROCESSABLE_ENTITY);
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8);
exchange.getResponse().writeWith(Mono.just(db));
return exchange.getResponse().setComplete();
} catch (JsonProcessingException e) {
e.printStackTrace();
return Mono.empty();
}
状态码设置正确,但响应内容长度为0。
在您的代码示例中,您同时调用了:
// write the given data buffer to the response
// and return a Mono that signals when it's done
exchange.getResponse().writeWith(Mono.just(db));
// marks the response as complete and forbids writing to it
exchange.getResponse().setComplete();
由于您正在调用第一个,但没有任何内容订阅它,因此不会向响应写入任何内容。
您可以将代码更新为:
exchange.getResponse().setStatusCode(HttpStatus.UNPROCESSABLE_ENTITY);
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF);
return exchange.getResponse().writeWith(Mono.just(db));
我建议您使用编解码器的标准 Spring 方式来序列化对象。
我至少知道两种方法:
- 使用 ServerCodecConfigurer class 和 EntityResponse 对象。
你可以定义 ServerCodecConfigurer @Bean
@Bean
public ServerCodecConfigurer serverCodecConfigurer() {
return new DefaultServerCodecConfigurer();
}
并像这样在某种 util 方法中使用它
public class ResponseUtil {
@NotNull
public static <T> Mono<Void> putResponseIntoWebExchange(ServerWebExchange exchange, ServerCodecConfigurer serverCodecConfigurer, Mono<EntityResponse<T>> responseMono) {
return responseMono.flatMap(entityResponse ->
entityResponse.writeTo(exchange, new ServerResponse.Context() {
@NotNull
@Override
public List<HttpMessageWriter<?>> messageWriters() {
return serverCodecConfigurer.getWriters();
}
@NotNull
@Override
public List<ViewResolver> viewResolvers() {
return Collections.emptyList();
}
}));
}
}
您的代码将如下所示
WebExchangeBindException cvex = (WebExchangeBindException) ex;
Errors errors = new Errors();//Class wrapper for Map with errors
log.debug("errors:" + cvex.getFieldErrors());
cvex.getFieldErrors().forEach(ev -> errors.put(ev.getField(), ev.getDefaultMessage()));
log.debug("handled errors::" + errors);
final Mono<EntityResponse<Errors>> responseMono = EntityResponse.fromObject(errors)
.status(HttpStatus.UNPROCESSABLE_ENTITY)
.contentType(APPLICATION_JSON)
.build();
return ResponseUtil.putResponseIntoWebExchange(exchange, serverCodecConfigurer, responseMono);
- 使用标准方式进行错误处理。
默认情况下 Spring WebFlux 使用 DefaultErrorAttributes class 来处理所有异常。您可以通过覆盖此 class 并在 spring 上下文中定义此 class 的 bean 来简单地自定义它。
像这样:
@Component
public class CustomErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
final Map<String, Object> errorAttributes = super.getErrorAttributes(request, includeStackTrace);
Throwable error = getError(request);
//Depends of error add or replace errorAttributes with your custom message, http status, etc
if (error instanceof WebExchangeBindException) {
errorAttributes.put("message", error.getMessage());
Errors errors = new Errors();
error.getFieldErrors().forEach(ev -> errors.put(ev.getField(), ev.getDefaultMessage()));
errorAttributes.put("errors", error);
}
return errorAttributes;
}
}
它是 Spring WebFlux 中处理异常的更原生的方式。
这很好 post - https://dzone.com/articles/exception-handling-in-spring-boot-webflux-reactive
在 WebExceptionHandler
在我的 Spring 5 WebFlux 应用程序中进行了部分异常处理工作,但是当我想以友好的方式转换现有异常时给客户端的消息,它不起作用。
我的自定义 WebExceptionHandler 如下所示,完整代码为 here。
WebExchangeBindException cvex = (WebExchangeBindException) ex;
Map<String, String> errors = new HashMap<>();
log.debug("errors:" + cvex.getFieldErrors());
cvex.getFieldErrors().forEach(ev -> errors.put(ev.getField(), ev.getDefaultMessage()));
log.debug("handled errors::" + errors);
try {
DataBuffer db = new DefaultDataBufferFactory().wrap(objectMapper.writeValueAsBytes(errors));
exchange.getResponse().setStatusCode(HttpStatus.UNPROCESSABLE_ENTITY);
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8);
exchange.getResponse().writeWith(Mono.just(db));
return exchange.getResponse().setComplete();
} catch (JsonProcessingException e) {
e.printStackTrace();
return Mono.empty();
}
状态码设置正确,但响应内容长度为0。
在您的代码示例中,您同时调用了:
// write the given data buffer to the response
// and return a Mono that signals when it's done
exchange.getResponse().writeWith(Mono.just(db));
// marks the response as complete and forbids writing to it
exchange.getResponse().setComplete();
由于您正在调用第一个,但没有任何内容订阅它,因此不会向响应写入任何内容。
您可以将代码更新为:
exchange.getResponse().setStatusCode(HttpStatus.UNPROCESSABLE_ENTITY);
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF);
return exchange.getResponse().writeWith(Mono.just(db));
我建议您使用编解码器的标准 Spring 方式来序列化对象。 我至少知道两种方法:
- 使用 ServerCodecConfigurer class 和 EntityResponse 对象。
你可以定义 ServerCodecConfigurer @Bean
@Bean
public ServerCodecConfigurer serverCodecConfigurer() {
return new DefaultServerCodecConfigurer();
}
并像这样在某种 util 方法中使用它
public class ResponseUtil {
@NotNull
public static <T> Mono<Void> putResponseIntoWebExchange(ServerWebExchange exchange, ServerCodecConfigurer serverCodecConfigurer, Mono<EntityResponse<T>> responseMono) {
return responseMono.flatMap(entityResponse ->
entityResponse.writeTo(exchange, new ServerResponse.Context() {
@NotNull
@Override
public List<HttpMessageWriter<?>> messageWriters() {
return serverCodecConfigurer.getWriters();
}
@NotNull
@Override
public List<ViewResolver> viewResolvers() {
return Collections.emptyList();
}
}));
}
}
您的代码将如下所示
WebExchangeBindException cvex = (WebExchangeBindException) ex;
Errors errors = new Errors();//Class wrapper for Map with errors
log.debug("errors:" + cvex.getFieldErrors());
cvex.getFieldErrors().forEach(ev -> errors.put(ev.getField(), ev.getDefaultMessage()));
log.debug("handled errors::" + errors);
final Mono<EntityResponse<Errors>> responseMono = EntityResponse.fromObject(errors)
.status(HttpStatus.UNPROCESSABLE_ENTITY)
.contentType(APPLICATION_JSON)
.build();
return ResponseUtil.putResponseIntoWebExchange(exchange, serverCodecConfigurer, responseMono);
- 使用标准方式进行错误处理。 默认情况下 Spring WebFlux 使用 DefaultErrorAttributes class 来处理所有异常。您可以通过覆盖此 class 并在 spring 上下文中定义此 class 的 bean 来简单地自定义它。
像这样:
@Component
public class CustomErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
final Map<String, Object> errorAttributes = super.getErrorAttributes(request, includeStackTrace);
Throwable error = getError(request);
//Depends of error add or replace errorAttributes with your custom message, http status, etc
if (error instanceof WebExchangeBindException) {
errorAttributes.put("message", error.getMessage());
Errors errors = new Errors();
error.getFieldErrors().forEach(ev -> errors.put(ev.getField(), ev.getDefaultMessage()));
errorAttributes.put("errors", error);
}
return errorAttributes;
}
}
它是 Spring WebFlux 中处理异常的更原生的方式。 这很好 post - https://dzone.com/articles/exception-handling-in-spring-boot-webflux-reactive