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.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Chantz
  • 5,883
  • 10
  • 56
  • 79

5 Answers5

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);
Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • Thanks @Sotirios for the response. But I dont seem to be able to get the `dispatcherServlet` in `AbstractAnnotationConfigDispatcherServletInitializer`. – Chantz Jul 01 '14 at 23:04
  • @Chantz Override the `registerDispatcherServlet`. Look at the implementation of the super type to get an idea of what to do. – Sotirios Delimanolis Jul 01 '14 at 23:08
  • 1
    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. – Link Aug 01 '15 at 08:39
  • @Link Looking at the source, it doesn't look like they've changed it. – Sotirios Delimanolis Aug 01 '15 at 17:09
  • But you can override createDispatcherServlet by adding one more line to it as I mentioned down – Ysak Jan 13 '16 at 07:43
7

Enable DispatcherServlet throw a NoHandlerFoundException through web.xml configuartion.

<init-param>
    <param-name>throwExceptionIfNoHandlerFound</param-name>
    <param-value>true</param-value>
</init-param>
Aborn Jiang
  • 981
  • 10
  • 9
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;
    }
Ysak
  • 2,601
  • 6
  • 29
  • 53
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"

Simon Ludwig
  • 1,754
  • 1
  • 20
  • 27
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.

user3559338
  • 410
  • 3
  • 6