
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