Open In App

Handling Errors in Spring WebFlux

The Spring Boot Community Developed the Spring Reactive Web Framework. The SpringReactive allows developers to build asynchronous, non-blocking, and event-driven web applications. When compared with the Spring MVC framework the Spring Reactive Web framework provides more functionality.

The Spring Web Flux provides different applications to developers that develop asynchronous, non-blocking web applications by using Mono and Flux, and other classes. In this article, we will learn how to handle Errors in Spring WebFlux with related examples with explanations.



Prerequisites:

Project Creation:

Project Folder Structure:

Project Dependencies:

In this project, we have used below dependencies and project category is gradle.



dependencies {
implementation 'org.springframework.boot:spring-boot-starter-webflux'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.projectreactor:reactor-test'
}

Types of Error handling in Spring WebFlux:

In Spring WebFlux framework we handle different errors in various levels.

Now, we will explain these two levels of Error handling in Spring WebFlux with examples.

Handling Errors at a Functional Level

In this level, we have two key operators to handle Errors in WebFlux. Those are Mono and Flux APIs. For this, we need use onErrorReturn() method to prove successful error handling in Functional Level. This onErrorReturn is returns while any error is exist in the API. Below we have provided example for both Mono and Flux with onErrorReturn() and onErrorResume()

Note: We can use onErrorReturn() to return a static default value while an error occurs. And we can use onErrorResume to return dynamic value while an error occurs.

In this example, we will explore Mono with onErrorReturn() and Flux with onErrorResume(). Here The Mono and Flux always return a publisher which is types of server Response.

Example:

Here, we have created one handler class for creating our logic in this Java class. By using @Service Annotation It possible below we have provided the entire code for handling errors in functional level with Mono and Flux with onErrorReturn() and onErrorResume().

ServiceHandler.java:




package com.webflux.app;
  
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
  
import reactor.core.publisher.Mono;
  
@Service
public class ServiceHandler {
    
  // api for mono with on error return method
    public Mono<ServerResponse> errorHandlingAtFunctionalLevelWithMono(ServerRequest request){
        return sayHello(request)
                .onErrorReturn("This is Error Handling At Functional Level with Mono with onErrorReturn")
                .flatMap(response -> ServerResponse.ok()
                        .contentType(MediaType.TEXT_PLAIN)
                        .bodyValue(response));
    }
  // api for flux with on error resume method  
    public Mono<ServerResponse> errorHandlingAtFunctionalLevelWithFlux(ServerRequest request){
        return sayHello(request)
                .onErrorResume(error -> {
                    System.err.println("Error occurred: " + error.getMessage());
                    return Mono.just("This is Error Handling At Functional Level With Flux and onErrorResume()");
                })
                .flatMap(response -> ServerResponse.ok()
                        .contentType(MediaType.TEXT_PLAIN)
                        .bodyValue(response));
    }
  
  // used for creating a runtime error
    public Mono<String> sayHello(ServerRequest request) {
         return Mono.error(new RuntimeException("Exception is Raised"));
    }
      
}

In the above example, we have two different APIs. Below we have explained each API separately and provided the out images also.

errorHandlingAtFunctionalLevelWithMono API




public Mono<ServerResponse> errorHandlingAtFunctionalLevelWithMono(ServerRequest request){
        return sayHello(request)
                .onErrorReturn("This is Error Handling At Functional Level with Mono with onErrorReturn")
                .flatMap(response -> ServerResponse.ok()
                        .contentType(MediaType.TEXT_PLAIN)
                        .bodyValue(response));
    }
  
public Mono<String> sayHello(ServerRequest request) {
         return Mono.error(new RuntimeException("Exception is Raised"));
    }

In the above Service Handler, we have created two different APIs for handling Error in functional level. Here errorHandlingAtFunctionalLevelWithMono API is developed by using Mono type publisher. In this API, we return sayHello() in this method and intentionally raised runtime exception to test whether the API is Handle the error or not. If any errors are occurring then onErrorReturn() executed. We have tested this API with the help of POSTMAN tool.

Output:

errorHandlingAtFunctionalLevelWithFlux:

This another API for handling errors in functional level with help of Flux publisher with combination of onErrorResume(). This onErrorResume is used for after flatmap only means any error occurs in flatmap automatically execution go to onErrorResume. Then print the error message. Below API have one Method that is sayHello() in this method, we intentionally created Runtime Exception to test this API.




public Mono<ServerResponse> errorHandlingAtFunctionalLevelWithFlux(ServerRequest request)
{
        return sayHello(request)
                .onErrorResume(error -> {
                    System.err.println("Error occurred: " + error.getMessage());
                    return Mono.just("This is Error Handling At Functional Level With Flux and onErrorResume()");
                })
                .flatMap(response -> ServerResponse.ok()
                        .contentType(MediaType.TEXT_PLAIN)
                        .bodyValue(response));
    }
public Mono<String> sayHello(ServerRequest request) 
{
         return Mono.error(new RuntimeException("Exception is Raised"));
    }

Output:

We have tested this API by using POSTMAN tool. After testing, we got excepted output.

Service Router:

In this Java class, we have created an API end point by using RouterFunction class which returns a route. This class is created by using @Configuration annotation, Then we define the required API End Points.




package com.webflux.app;
  
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
  
@Configuration
public class ServiceRouter {
      
    @Autowired
    private ServiceHandler serviceHandler;
      
    @Bean
    RouterFunction<ServerResponse> routerFunction(){
        return RouterFunctions.route(RequestPredicates.POST("api/functional/mono"),serviceHandler::errorHandlingAtFunctionalLevelWithMono)
                .andRoute(RequestPredicates.POST("api/functional/flux"), serviceHandler::errorHandlingAtFunctionalLevelWithFlux);
    }
      
}

Handling Errors at a Global Level

This is another case for handling errors in Spring WebFlux. In this category we handle errors at Global level for we need use DefaultErrorAttributes class which is availble in built-in this class is used for handling errors at global level. To do this we have two steps are there:

When Exception is occurs, our handler throws will be automatically transform to HTTP Status. Here we override the getErrorAttributes(). Below we have provided that code for reference.

Here, we have created two different classes one for handling global errors another one is used for creating error attributes below provide them. And The Handler is Component type and the Error attribute class is normal class.

Error Handler:




package com.webflux.app;
  
import java.util.Map;
  
import org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler;
import org.springframework.boot.web.codec.CodecCustomizer;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
  
import reactor.core.publisher.Mono;
  
@Component
@Order(-2)
public class GlobalErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {
  
    public GlobalErrorWebExceptionHandler(ErrorAttributes errorAttributes, ApplicationContext applicationContext, CodecCustomizer serverCodecConfigurer) 
    {
        super(errorAttributes, new CodecCustomizer[]{serverCodecConfigurer});
    }
  
    @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, ErrorAttributeOptions.defaults());
        return ServerResponse.status(HttpStatus.BAD_REQUEST)
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(errorPropertiesMap));
    }
}

ErrorAttributes:




package com.webflux.app;
  
import java.util.Map;
  
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.server.ServerRequest;
  
public class GlobalErrorAttributes extends org.springframework.boot.web.reactive.error.DefaultErrorAttributes{
      
    @Override
    public Map<String, Object> getErrorAttributes(ServerRequest request, 
      ErrorAttributeOptions options) {
        Map<String, Object> map = super.getErrorAttributes(
          request, options);
        map.put("status", HttpStatus.BAD_REQUEST);
        map.put("message", "This is Global error");
        return map;
    }
  
}

After successfully run this project, we will get below output.

Output:

status : 400
message : This is Global error

Article Tags :