NashTech Blog

Spring WebFlux — Error Handling

Introduction

In this Blog, we’ll take a look at the Spring WebFlux Error Handling using @ControllerAdvice. While calling the services/micro-services anything could go wrong and result in 500 “Internal Server Errors as shown below error:

{
    "timestamp": "2022-09-09T03:42:10.982+00:00",

    "path": "/employee/all",

    "status": 500,

    "error": "Internal Server Error",

    "requestId": "3101b004-1"
}

Usually, error messages like this will not be handled properly and would be propagated to all the downstream services which might impact the user experience. In some cases, applications might want to use application-specific error codes to convey appropriate messages to the calling service.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" 

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 

https://maven.apache.org/xsd/maven-4.0.0.xsd">

   <modelVersion>4.0.0</modelVersion>

   <parent>

      <groupId>org.springframework.boot</groupId>

      <artifactId>spring-boot-starter-parent</artifactId>

      <version>2.6.3</version>

      <relativePath/> <!-- lookup parent from repository -->

   </parent>

   <groupId>com.example</groupId>

   <artifactId>error-handling</artifactId>

   <version>0.0.1-SNAPSHOT</version>

   <name>error-handling</name>

   <description>Demo project for Spring Boot</description>

   <properties>

      <java.version>11</java.version>

   </properties>

   <dependencies>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-webflux</artifactId>

      </dependency>

      <dependency>

         <groupId>org.projectlombok</groupId>

         <artifactId>lombok</artifactId>

         <optional>true</optional>

      </dependency>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-test</artifactId>

         <scope>test</scope>

      </dependency>

      <dependency>

         <groupId>io.projectreactor</groupId>

         <artifactId>reactor-test</artifactId>

         <scope>test</scope>

      </dependency>

   </dependencies>

   <build>

      <plugins>

         <plugin>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-maven-plugin</artifactId>

            <configuration>

               <excludes>

                  <exclude>

                     <groupId>org.projectlombok</groupId>

                     <artifactId>lombok</artifactId>

                  </exclude>

               </excludes>

            </configuration>

         </plugin>

      </plugins>

   </build>

</project>

DTO

First I create a simple DTO for employees. We are interested only in these 3 attributes of employees for now.

Employee.java

Now, I create another class that can respond in case of error.  errorCode can be some app-specific error code and some appropriate error message.

ErrorResponse.java

I also create another exception class as shown here for the service layer to throw an exception when the employee is not found for the given id.

EmployeeNotFoundException.java

Now, Finally, I create a service layer with these 2 methods.

EmployeeService.java

import com.example.errorhandling.dto.Employee;

import com.example.errorhandling.exception.EmployeeNotFoundException;

import org.springframework.stereotype.Service;

import reactor.core.publisher.Flux;

import reactor.core.publisher.Mono;

import java.util.Map;

@Service

public class EmployeeService {

    private static final Map<Integer, Employee> DATA = Map.of(

            1, new Employee(1, "DEEPAK", "KUMAR"),

            2, new Employee(2, "ASHIF", "ALI"),

            3, new Employee(3, "KRISHNA", "JAISHWAL"),

            4, new Employee(4, "HARSH", "GUPTA"),

            5, new Employee(5, "ANKIT", "MOGHA")
    );

    public Flux<Employee> getAllEmployees() {

        return Flux.fromIterable(DATA.values())

                .doFirst(() -> throwRandomError(true));
    }

    private void throwRandomError(boolean flag) {

        if (flag) {
            throw new RuntimeException("Some Errors!!");
        }
    }

    public Mono<Employee> findEmployeeById(int id) {

        return Mono.just(id)

                .filter(e -> DATA.containsKey(e))

                .map(e -> DATA.get(e))

                .switchIfEmpty(Mono.error(() -> new 

EmployeeNotFoundException(id)));

    }
}

To produce an error, I set the value to true, so that we can see the error, and make it false to be able to see the endpoints working.

doFirst(() -> throwRandomError(true));

If I pass the below request, I received the appropriate response instead of directly propagating a 500 Internal Server Error.

http://localhost:8080/employee/28

This error delivered a more meaningful error message. So the calling service using this error code might take appropriate action.

{
   "errorCode":101,

   "message":"Employee id 28 is not found"

}

Similarly, I used the below endpoint (after some time), then the below response get.

http://localhost:8080/employee/all
{
   "errorCode":100,

   "message":"Unable to fetch employees"
}

Conclusion

In this Blog, we learn all about “The error-handling-spring-boot-starter” library is fully ready for Spring WebFlux.

References

https://blog.softwaremill.com/spring-webflux-and-domain-exceptions-10ae2096b159

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top