Spring webflux 使用RouterFunction处理WebFlux中的错误

Spring webflux 使用RouterFunction处理WebFlux中的错误,spring-webflux,Spring Webflux,我很难让我的反应式代码以一种常见的方式处理错误。理想的方法是在一个可重用的组件中,我可以将其作为依赖项添加到其他项目中 过去,我们使用@RestControllerAdvise使用个性化的@ExceptionHandler函数来处理它们。作为参考,我的代码: @Configuration public class VesselRouter { @Bean public RouterFunction<ServerResponse> route(VesselHandle

我很难让我的反应式代码以一种常见的方式处理错误。理想的方法是在一个可重用的组件中,我可以将其作为依赖项添加到其他项目中

过去,我们使用
@RestControllerAdvise
使用个性化的
@ExceptionHandler
函数来处理它们。作为参考,我的代码:

@Configuration
public class VesselRouter {

    @Bean
    public RouterFunction<ServerResponse> route(VesselHandler handler) {
        return RouterFunctions.route(GET("/vessels/{imoNumber}").and(accept(APPLICATION_JSON)), handler::getVesselByImo)
                .andRoute(GET("/vessels").and(accept(APPLICATION_JSON)), handler::getVessels);
    }
}
我还尝试使用
AbstractErrorWebExceptionHandler
,如中的示例所示。似乎也不起作用:

@Component
@Order(-2)
public class GlobalErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {

    public GlobalErrorWebExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties, ApplicationContext applicationContext) {
        super(errorAttributes, resourceProperties, applicationContext);
    }

    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(
            ErrorAttributes errorAttributes) {

        return RouterFunctions.route(
                RequestPredicates.all(), this::renderErrorResponse);
    }

    private Mono<ServerResponse> renderErrorResponse(
            ServerRequest request) {

        Map<String, Object> errorPropertiesMap = getErrorAttributes(request, false);

        return ServerResponse.status(HttpStatus.BAD_REQUEST)
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .body(BodyInserters.fromObject(errorPropertiesMap));
    }
}
@组件
@订单(-2)
公共类GlobalErrorWebExceptionHandler扩展了AbstractErrorWebExceptionHandler{
公共GlobalErrorWebExceptionHandler(ErrorAttributes ErrorAttributes,ResourceProperties ResourceProperties,ApplicationContext ApplicationContext){
超级(errorAttributes、resourceProperties、applicationContext);
}
@凌驾
受保护的路由函数getRoutingFunction(
错误属性(错误属性){
返回路由函数(
RequestPredicates.all(),this::renderErrorResponse);
}
专用单声道渲染器响应(
服务器请求(请求){
映射errorPropertiesMap=getErrorAttributes(请求,false);
返回ServerResponse.status(HttpStatus.BAD\u请求)
.contentType(MediaType.APPLICATION\u JSON\u UTF8)
.body(BodyInserters.fromObject(errorPropertiesMap));
}
}

那么,在不使用
@RestController
的情况下,如何使用WebFlux处理全局错误呢

@ControllerAdvice
仅适用于带注释的编程模型。要使用功能端点提供类似于
ControllerAdvice
的功能,您可以利用。参考文献:

路由器函数映射的路由可以通过调用RouterFunction.filter(HandlerFilterFunction)进行过滤,其中HandlerFilterFunction本质上是一个接受ServerRequest和HandlerFunction并返回ServerResponse的函数。handler函数参数表示链中的下一个元素:这通常是路由到的HandlerFunction,但如果应用了多个筛选器,也可以是另一个FilterFunction。通过注释,可以使用@ControllerAdvice和/或ServletFilter实现类似的功能

@Bean
RouterFunction路由(){
返回路由函数
.route(GET(“/foo”),request->Mono.error(new DataNotFoundException())
.andRoute(GET(“/bar”),request->Mono.error(new DataNotFoundException())
.filter(dataNotFoundToBadRequest());
}
私有HandlerFilterFunction dataNotFoundToBadRequest(){
return(request,next)->next.handle(request)
.onErrorResume(DataNotFoundException.class,e->ServerResponse.badRequest().build());
}
或者,您可以使用WebFilter完成相同的任务:

@Bean
RouterFunction路由(){
返回路由函数
.route(GET(“/foo”),request->Mono.error(new DataNotFoundException())
.andRoute(GET(“/bar”),request->Mono.error(newdatanotfoundexception());
}
@豆子
WebFilter dataNotFoundToBadRequest(){
return(exchange,next)->next.filter(exchange)
.OneErrorResume(DataNotFoundException.class,e->{
ServerHttpResponse response=exchange.getResponse();
response.setStatusCode(HttpStatus.BAD_请求);
返回response.setComplete();
});
}

对我来说,我创建了一个AppException,并将其扔到应用程序(Rest控制器)的任何地方,我认为这应该是一个“错误”响应

  • AppException:我的特定异常,它可以包含您想要处理、显示、返回错误的任何内容

    public class AppException extends RuntimeException {
        int code;
        HttpStatus status = HttpStatus.OK;
        ...
    }
    
然后我定义了(a)全局控制器建议,它负责过滤掉这些异常

这是我的示例,我可以取出我在Rest控制器中抛出的AppException,然后将其作为ResponseEntity返回,其中body作为“ErrorResponse”POJO返回

在webflux中,返回Mono.error()作为Rob Winch的答案可能会抛出错误

@ControllerAdvice
@Slf4j
public class ExceptionHandlers {

    @ExceptionHandler(value = DataNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ResponseEntity<String> handleDataNotFoundException(DataNotFoundException dataNotFoundException,
                                                                ServletWebRequest servletWebRequest) {
        //habdling expcetions code here
    }
}
@Test
    public void findByImoNoData() {
        when(vesselsService.getByImoNumber("1234567")).thenReturn(Mono.empty());
        webTestClient.get().uri("/vessels/1234567")
                .accept(MediaType.APPLICATION_JSON)
                .exchange()
                .expectStatus().isNotFound();
    }
@Component
@Order(-2)
public class GlobalErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {

    public GlobalErrorWebExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties, ApplicationContext applicationContext) {
        super(errorAttributes, resourceProperties, applicationContext);
    }

    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(
            ErrorAttributes errorAttributes) {

        return RouterFunctions.route(
                RequestPredicates.all(), this::renderErrorResponse);
    }

    private Mono<ServerResponse> renderErrorResponse(
            ServerRequest request) {

        Map<String, Object> errorPropertiesMap = getErrorAttributes(request, false);

        return ServerResponse.status(HttpStatus.BAD_REQUEST)
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .body(BodyInserters.fromObject(errorPropertiesMap));
    }
}
public class AppException extends RuntimeException {
    int code;
    HttpStatus status = HttpStatus.OK;
    ...
}
public class ErrorResponse {

    boolean error = true;
    int code;
    String message;
}

@ControllerAdvice
public class GlobalExceptionHandlingControllerAdvice {

    @ExceptionHandler(AppException.class)
    public ResponseEntity handleAppException(AppException ex) {
        return ResponseEntity.ok(new ErrorResponse(ex.getCode(), ex.getMessage()));
    }
}