How to customize DefaultHandlerExceptionResolver logic?
Asked Answered
C

2

10

I want to customize DefaultHandlerExceptionResolver in my Spring Boot application but custom implementation was never reached when exception was occurred.

build.gradle

buildscript {
    ext {
        springBootVersion = '2.1.1.RELEASE'
    }
    repositories {
        mavenCentral()
    }
}

plugins {
    id 'java'
    id 'org.springframework.boot' version '2.1.1.RELEASE'
    id 'io.spring.dependency-management' version '1.0.6.RELEASE'
    id 'io.franzbecker.gradle-lombok' version '1.14'
}

lombok {
    version = '1.18.4'
    sha256 = ""
}

group = 'ua.com.javaman'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    implementation('org.springframework.boot:spring-boot-starter-web')
}

Application.java

@SpringBootApplication
@RestController
@Configuration
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @ResponseBody
    @PostMapping("/mouse")
    public Mouse postMouse(@Valid Mouse mouse) {
        // mouse creation logic
        return mouse;
    }

    @Bean
    public HandlerExceptionResolver customHandlerExceptionResolver() {
        return new CustomExceptionHandlerResolver();
    }
}

Mouse.java

@Value
public class Mouse {
    private final Long id;
    @NotEmpty
    @Min(2)
    private final String name;
}

CustomExceptionHandlerResolver.java

public class CustomExceptionHandlerResolver extends DefaultHandlerExceptionResolver {
    @Override
    protected ModelAndView handleBindException(
        BindException ex, HttpServletRequest request, HttpServletResponse response, @Nullable Object handler
    ) throws IOException {
        System.out.println("In CustomExceptionHandlerResolver");
        response.sendError(HttpServletResponse.SC_BAD_REQUEST);
        return new ModelAndView();
    }
}

Package structure

.
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── LICENSE
├── README.md
├── settings.gradle
└── src
    └── main
        └── java
            └── ua
                └── com
                    └── javaman
                        └── exception_handling
                            ├── Application.java
                            ├── CustomExceptionHandlerResolver.java
                            └── Mouse.java

When I invoke application with wrong values for Mouse entity, for example,

POST localhost:8080/mouse?name=1

than handleBindException() method in DefaultHandlerExceptionResolver is invoked but not in my CustomExceptionHandlerResolver.

How can I handle BindException with my CustomExceptionHandlerResolver?

Cartercarteret answered 29/12, 2018 at 23:3 Comment(4)
are you missing @Component on CustomExceptionHandlerResolverThermoelectric
@Deadpool adding @Component have not resolved my problemCartercarteret
show the package structure alsoThermoelectric
@Deadpool I have added package structureCartercarteret
T
9

SpringBoot 's auto configuration will always create a bunch of exception handlers by default which one of them is DefaultHandlerExceptionResolver at order 0 (lower value has the higher priority) . Your handler by default will has lower priority than these default , so it will not be invoked as the exception is already handled by the default.

You can implement your configuration class with WebMvcConfigurer and override extendHandlerExceptionResolvers() to modify the default settings. The idea is to find out the DefaultHandlerExceptionResolver instance and replace it with yours.

But a better idea is to define your CustomExceptionHandlerResolver with @ControllerAdvice , which make sure that you will not mess up with the default settings while still add the customised behaviour that your want (i.e use your logic to handle BindException) :

@Component
@ControllerAdvice
public class CustomExceptionHandlerResolver {

    @ExceptionHandler(value= BindException.class)
    @Override
    protected ModelAndView handleBindException(
            BindException ex, HttpServletRequest request, HttpServletResponse response, @Nullable Object handler)
            throws IOException {
        System.out.println("In CustomExceptionHandlerResolver");
        response.sendError(HttpServletResponse.SC_BAD_REQUEST);
        return new ModelAndView();
    }

}
Transcaucasia answered 30/12, 2018 at 10:44 Comment(3)
@Order(Ordered.HIGHEST_PRECEDENCE) or some other custom order could be used to make sure exception handler takes precedence.Teleplay
Implement Ordered interface with the order less than 0 can take precedence over those default . But to be precised , there are multiple default exception handlers at the order 0 while your custom exception handler basically will handle all the exceptions .If you make your handler has higher priority over those default , you will override some of the default exception handling behaviour such as those @ExceptionHandler.....Transcaucasia
DefaultHandlerExceptionResolver has order Ordered.LOWEST_PRECEDENCE in spring 5.3.3Selenium
C
1

I had the same issue. When you extend DefaultHandlerExceptionResolver do not forget to override the getOrder method. By default it returns the LOWEST_PRECEDENCE. To ensure that your implementation works first returns HIGHEST_PRECEDENCE.

DefaultHandlerExceptionResolver eats core exceptions and sometimes it's very hard to understand what happens when a client get 500 status code. To avoid this I used simple workaround.

@Component
@Slf4j
public class ExceptionHandler extends DefaultHandlerExceptionResolver {
    @Override
    protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        var result = super.doResolveException(request, response, handler, ex);
        if (result != null) {
            log.warn("Request was finished with an error.", ex);
        }
        return result;
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}

Of course it's better to define a handler for each exception like [@Ken Chan][1] suggested [1]: https://mcmap.net/q/1088359/-how-to-customize-defaulthandlerexceptionresolver-logic

Coupon answered 16/5, 2023 at 12:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.