HOWTO handle 404 exceptions globally using Spring MVC configured using Java based Annotations
Asked Answered
M

5

11

I am building a Spring 4 MVC app. And it is completely configured using Java Annotations. There is no web.xml. The app is configured by using instance of AbstractAnnotationConfigDispatcherServletInitializer and WebMvcConfigurerAdapter like so,

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"com.example.*"})
@EnableTransactionManagement
@PropertySource("/WEB-INF/properties/application.properties")
public class WebAppConfig extends WebMvcConfigurerAdapter {
...
}

and

public class WebAppInitializer extends
    AbstractAnnotationConfigDispatcherServletInitializer {
...
}

I am now trying to add a global/catch-all exception handler for 404 pages i.e. HttpStatus.NOT_FOUND but no success. Below are some of the ways I tried.

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;

@ControllerAdvice
public class GlobalExceptionHandlerController {

    @ExceptionHandler
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ModelAndView handleException (NoSuchRequestHandlingMethodException ex) {
            ModelAndView mav = new ModelAndView();
            return mav;
    }

    @ExceptionHandler
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ModelAndView handleExceptiond (NoHandlerFoundException ex) {
            ModelAndView mav = new ModelAndView();
            return mav;
    }

    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(NoHandlerFoundException.class)
    public void handleConflict() {

    }

    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(NoSuchRequestHandlingMethodException.class)
    public void handlesdConflict() {
    }

}

None of these methods get executed. I am at a loss as to how to handle this. I do not want to use web.xml becasue then I would have to create one just for this.

Misteach answered 30/6, 2014 at 20:46 Comment(2)
I don't know why they didn't make the DispatcherServlet visible through some protected method, but the custom solution seems good.Eyeless
Why dont you just override just createDispatcherServletBeekeeper
E
15

By default, the DispatcherServlet does not throw a NoHandlerFoundException. You need to enable that.

The AbstractAnnotationConfigDispatcherServletInitializer should let you override how the DispatcherServlet is created. Do that and call

DispatcherServlet dispatcherServlet = ...; // might get it from super implementation
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
Eyeless answered 30/6, 2014 at 21:12 Comment(5)
Thanks @Sotirios for the response. But I dont seem to be able to get the dispatcherServlet in AbstractAnnotationConfigDispatcherServletInitializer.Misteach
@Misteach Override the registerDispatcherServlet. Look at the implementation of the super type to get an idea of what to do.Eyeless
One year later... Is there a simpler way of doing this? I usually don't override registerDispatcherServlet in my Initializer so I find this method too verbose. All those lines rewritten just to add one line.Flaring
@Flaring Looking at the source, it doesn't look like they've changed it.Eyeless
But you can override createDispatcherServlet by adding one more line to it as I mentioned downBeekeeper
C
7

Enable DispatcherServlet throw a NoHandlerFoundException through web.xml configuartion.

<init-param>
    <param-name>throwExceptionIfNoHandlerFound</param-name>
    <param-value>true</param-value>
</init-param>
Carlist answered 14/10, 2015 at 2:20 Comment(0)
B
3

Instead overriding registerDispatcherServlet one can override the createDispatcherServlet method as follows.

@Override
    protected DispatcherServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
        DispatcherServlet ds = new DispatcherServlet(servletAppContext);
        ds.setThrowExceptionIfNoHandlerFound(true);
        return ds;
    }
Beekeeper answered 13/1, 2016 at 7:42 Comment(0)
M
3

I resolved the problem with the following entry in my application.yml

 server.error.whitelabel.enabled: false
 spring.mvc.throw-exception-if-no-handler-found: true

and the following ControllerExceptionHandler:

@ControllerAdvice
public class ControllerExceptionHandler {

@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String processMethodNotSupportedException(Exception exception) {
    exception.printStackTrace();
    return "error";
}

}

and last but not least i added a template "/html/error.html"

Manipur answered 30/6, 2017 at 10:18 Comment(1)
After SpringBoot version 2.0, using spring.mvc.throwExceptionIfNoHandlerFound: trueEnlightenment
C
0

I can't comment on the above post by @Ysak (reputation<50), however I can confirm that this method does work with the setup described by the OP.

I will add that from another guide I also had the DefaultServletHandling configured in my WebConfig to fix a separate issue, as below:

@Configuration
@EnableWebMvc
@ComponentScan("com.acme.tat.controllers")
public class WebConfig extends WebMvcConfigurerAdapter implements ApplicationContextAware {
...
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
}

}

With this set to enable, there is no Exception thrown. Once I removed this line and set up manual resourceHandlers everything worked as expected.

It took me around 2.5 hours to set up 404 redirecting because of this simple one line method.

Cuttlebone answered 17/4, 2017 at 23:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.