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

依然范特西╮ 提交于 2019-12-30 04:22:05

问题


(SOLUTION posted at the end of the Question)

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.

SOLUTION I did what @Sotirios mentioned here. Here is my portion WebAppInitializer code that will set the dispatcher properly.

public class WebAppInitializer extends
        AbstractAnnotationConfigDispatcherServletInitializer {

    ...

    @Override
    protected void registerDispatcherServlet(ServletContext servletContext) {

        String servletName = super.getServletName();
        Assert.hasLength(servletName, "getServletName() may not return empty or null");

        WebApplicationContext servletAppContext = super.createServletApplicationContext();
        Assert.notNull(servletAppContext,
                "createServletApplicationContext() did not return an application " +
                "context for servlet [" + servletName + "]");

        DispatcherServlet dispatcherServlet = new DispatcherServlet(servletAppContext);

        //>>> START: My custom code, rest is exqact copy of the super class
        dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
        //>>> END

        ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
        Assert.notNull(registration,
                "Failed to register servlet with name '" + servletName + "'." +
                "Check if there is another servlet registered under the same name.");

        registration.setLoadOnStartup(1);
        registration.addMapping(getServletMappings());
        registration.setAsyncSupported(isAsyncSupported());

        Filter[] filters = getServletFilters();
        if (!ObjectUtils.isEmpty(filters)) {
            for (Filter filter : filters) {
                super.registerServletFilter(servletContext, filter);
            }
        }

        super.customizeRegistration(registration);
    }

    ...
}

Although I am not particularly happy with this solution because my only custom code is 1 line & rest is directly copied from super class. I wish there was a nicer way to do this.


回答1:


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);



回答2:


Enable DispatcherServlet throw a NoHandlerFoundException through web.xml configuartion.

<init-param>
    <param-name>throwExceptionIfNoHandlerFound</param-name>
    <param-value>true</param-value>
</init-param>



回答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;
    }



回答4:


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"




回答5:


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.



来源:https://stackoverflow.com/questions/24498662/howto-handle-404-exceptions-globally-using-spring-mvc-configured-using-java-base

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!