如何在Spring webflux WebExceptionHandlder中将消息写入http正文

Han*_*tsy 6 spring spring-webflux

这篇文章的帮助下,我通过 custom 在 Spring 5 WebFlux 应用程序中获得了部分异常处理工作WebExceptionHandler,但是当我想将友好消息中的现有异常转换为客户端时,它不起作用。

我的自定义 WebExceptionHandler 如下所示,完整代码在这里

 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();
            }
Run Code Online (Sandbox Code Playgroud)

状态码设置正确,但响应内容长度为 0。

Bri*_*zel 9

在您的代码示例中,您同时调用:

// 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();
Run Code Online (Sandbox Code Playgroud)

由于您正在调用第一个并且没有订阅它,因此没有任何内容写入响应。

您可以更新代码以具有:

exchange.getResponse().setStatusCode(HttpStatus.UNPROCESSABLE_ENTITY);
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF);
return exchange.getResponse().writeWith(Mono.just(db));
Run Code Online (Sandbox Code Playgroud)


Bor*_*eyn 5

我建议你使用标准的 Spring 方式和 Codecs 来序列化对象。我知道至少有两种方法:

  1. 使用 ServerCodecConfigurer 类和 EntityResponse 对象。

您可以定义 ServerCodecConfigurer @Bean

@Bean
public ServerCodecConfigurer serverCodecConfigurer() {
    return new DefaultServerCodecConfigurer();
}
Run Code Online (Sandbox Code Playgroud)

并在这样的某种 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();
                    }
                }));
    }

}
Run Code Online (Sandbox Code Playgroud)

您的代码将如下所示

        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);
Run Code Online (Sandbox Code Playgroud)
  1. 使用标准方式进行错误处理。默认情况下,Spring WebFlux 使用 DefaultErrorAttributes 类来处理所有异常。您可以通过覆盖此类并在 spring 上下文中定义此类的 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;
    }

}
Run Code Online (Sandbox Code Playgroud)

这是在 Spring WebFlux 中处理异常的更原生方式。有关于这个的好帖子 - https://dzone.com/articles/exception-handling-in-spring-boot-webflux-reactive