javaspringspring-mvcspring-boot

Spring Boot - Handling NoHandlerFoundException


Read the Spring Reference Guide on how to handle NoHandlerFoundException and found that Spring sets, by default, throwExceptionIfNoHandlerFound to false.

Knowing that, I thought it was a good idea to set this parameter to true.

I'm using Spring Boot.

Did so:

MyConfig.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

@SpringBootApplication
public class MyConfig {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(MyConfig.class, args);
        context.getBean(DispatcherServlet.class).setThrowExceptionIfNoHandlerFound(true);

        // ...
    }
}

Alright, now throwExceptionIfNoHandlerFound is equals true. However, that didn't worked as expected. The DispatcherServlet continued not throwing the NoHandlerFoundException. This way, I was unable to handle it.

CustomExceptionHandler.java (this didn't worked)

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@ControllerAdvice
public class CustomExceptionHandler extends ResponseEntityExceptionHandler {

    @Override
    protected ResponseEntity <Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        // do my own handle ...

        // then, return ...
    }

}

After a bit of search, found that adding @EnableWebMvc should work. Then, I did add this annotation to my CustomExceptionHandler

CustomExceptionHandler.java (this did worked)

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@EnableWebMvc
@ControllerAdvice
public class CustomExceptionHandler extends ResponseEntityExceptionHandler {

    @Override
    protected ResponseEntity <Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        // do my own handle ...

        // then, return ...
    }

}

This way, the handle works. However, I'm using Spring Boot. The Spring Boot docs suggests that if I insert @EnableWebMvc, I would lose some Spring Boot MVC Auto-configuration features, since I would take complete controll of Spring MVC. (See here).

"If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc."

Would I lose Spring MVC Auto-configuration? I mean, the @EnableWebMvc, in my case, it's not in a @Configuration class.

My question is based on the fact that I'm not sure how this code above works, and I want to know if there's another way of doing this without losing the Spring MVC Auto-configuration. Could someone explain?


Solution

  • Spring Boot auto-configuration will automatically add a ResourceHttpRequestHandler to deal with serving static resource. By default this handler is mapped against /** and is the last item in the handler chain.

    What this means is the DispatcherServlet won't throw a NoHandlerFoundException because it found the resource handler. The resource handler processes the request and calls response.sendError(HttpServletResponse.SC_NOT_FOUND) to return a 404.

    If you don't want this behavior, you can add the following to your application.properties:

    spring.mvc.throw-exception-if-no-handler-found=true
    spring.resources.add-mappings=false
    

    The will configure the dispatcher servlet to throw the exception and also tell Spring Boot not to register the resource handler.

    Before you go too far down that route, you might want to look at this section of the reference docs to see if it wouldn't be better to handle those errors in a different way. It's not totally clear from your question what you're actually trying to do in your error handler, but if it's just dealing with 404s then there's probably a better way.