简体   繁体   English

处理Web应用程序中的错误404 - 而不是REST应用程序

[英]Handle error 404 in web application - not REST application

I'm trying to handle 404 error using an @ControllerAdvice in a Spring MVC application totally configured using Javaconfig. 我正在尝试使用完全使用Javaconfig配置的Spring MVC应用程序中的@ControllerAdvice来处理404错误。

Spring MVC version is 4.1.5 Spring MVC版本是4.1.5

I have read this: 我读过这个:

But unfortunately it does not work for me. 但不幸的是,它对我不起作用。

Here you have my conf: 在这里你有我的conf:

SpringConfigurationInitializer SpringConfigurationInitializer

public class SpringConfigurationInitializer extends
        AbstractAnnotationConfigDispatcherServletInitializer {



    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { AppConfiguration.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return null;
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

    @Override
    public void customizeRegistration(ServletRegistration.Dynamic registration) {
        registration.setInitParameter("throwExceptionIfNoHandlerFound", "true");
    }
}

Note that i'm using 请注意,我正在使用

registration.setInitParameter("throwExceptionIfNoHandlerFound", "true");

And

GlobalExceptionHandler (version 1) GlobalExceptionHandler(版本1)

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(NoHandlerFoundException.class)
    public ModelAndView handleError404(HttpServletRequest request, Exception e) {
        System.out.println("handled!!!");
        ModelAndView mav = new ModelAndView("/errors/404");
        mav.addObject("exception", e);
        return mav;
    }
}

GlobalExceptionHandler (version 2) GlobalExceptionHandler(版本2)

@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    @Override
    public ResponseEntity handleNoHandlerFoundException(NoHandlerFoundException ex,
            HttpHeaders headers, HttpStatus status, WebRequest request) {
        System.out.println("handled¡¡¡");
        return null;
    }
}

Keep in mind that i'm not using any kind of xml config file and i'm trying to build a web application (not REST) 请记住,我没有使用任何类型的xml配置文件,我正在尝试构建一个Web应用程序(而不是REST)

AppConfiguration AppConfiguration

@Configuration
@ComponentScan({ "org.moyanojv.opendata.*" })
@Import({ MvcConfiguration.class, RepositoryConfiguration.class, SecurityConfig.class })
public class AppConfiguration extends WebMvcConfigurerAdapter{

}

MvcConfiguration MvcConfiguration

@EnableWebMvc
@Configuration
public class MvcConfiguration extends WebMvcConfigurerAdapter {

    @Bean
    public UrlBasedViewResolver viewResolver() {
        UrlBasedViewResolver viewResolver = new UrlBasedViewResolver();
        viewResolver.setViewClass(TilesView.class);
        return viewResolver;
    }

    @Bean
    public TilesConfigurer tilesConfigurer() {
        TilesConfigurer tilesConfigurer = new TilesConfigurer();
        tilesConfigurer.setDefinitions(new String[] { "/WEB-INF/tiles.xml" });
        tilesConfigurer.setCheckRefresh(true);
        return tilesConfigurer;
    }



    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
         registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
    }

    @Override
    public void configureDefaultServletHandling(
            DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }


    /* Localization section is started */

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localeChangeInterceptor());
    }

    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor(){
        LocaleChangeInterceptor localeChangeInterceptor=new LocaleChangeInterceptor();
        localeChangeInterceptor.setParamName("lang");
        return localeChangeInterceptor;
    }

    @Bean(name = "localeResolver")
    public LocaleResolver getLocaleResolver(){
        return new CookieLocaleResolver();
    }

    @Bean
    public ResourceBundleMessageSource messageSource() {
        ResourceBundleMessageSource source = new ResourceBundleMessageSource();
        source.setBasename("i18n/messages");
        source.setUseCodeAsDefaultMessage(true);
        return source;
    }
}

RepositoryConfiguration RepositoryConfiguration

@Configuration
public class RepositoryConfiguration {

}

SecurityConfig SecurityConfig

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth)
            throws Exception {
        auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN");
        auth.inMemoryAuthentication().withUser("user").password("user").roles("USER");
    }

    @Override
    public void configure( WebSecurity web ) throws Exception
    {
        // This is here to ensure that the static content (JavaScript, CSS, etc)
        // is accessible from the login page without authentication
        web
            .ignoring()
                .antMatchers( "/resources/**" );
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
        // access-denied-page: this is the page users will be
        // redirected to when they try to access protected areas.
        .exceptionHandling()
            .accessDeniedPage( "/403" )
            .and()
        // The intercept-url configuration is where we specify what roles are allowed access to what areas.
        // We specifically force the connection to https for all the pages, although it could be sufficient
        // just on the login page. The access parameter is where the expressions are used to control which
        // roles can access specific areas. One of the most important things is the order of the intercept-urls,
        // the most catch-all type patterns should at the bottom of the list as the matches are executed
        // in the order they are configured below. So /** (anyRequest()) should always be at the bottom of the list.
        .authorizeRequests()
            .antMatchers( "/admin" ).hasRole("ADMIN")
            .antMatchers("/login**").permitAll()
            .antMatchers("/home").permitAll()
            .antMatchers("/404").permitAll()
            .anyRequest().authenticated()
            .and()
        // This is where we configure our login form.
        // login-page: the page that contains the login screen
        // login-processing-url: this is the URL to which the login form should be submitted
        // default-target-url: the URL to which the user will be redirected if they login successfully
        // authentication-failure-url: the URL to which the user will be redirected if they fail login
        // username-parameter: the name of the request parameter which contains the username
        // password-parameter: the name of the request parameter which contains the password
        .formLogin()
            .loginPage( "/login" )
            .failureUrl( "/login?err=1" )
            .defaultSuccessUrl("/private")
            .usernameParameter( "username" )
            .passwordParameter( "password" )
            .permitAll()
            .and()
        // This is where the logout page and process is configured. The logout-url is the URL to send
        // the user to in order to logout, the logout-success-url is where they are taken if the logout
        // is successful, and the delete-cookies and invalidate-session make sure that we clean up after logout
        .logout()
            .logoutUrl("/logout")
            .logoutSuccessUrl("/login?logout=1")
            .invalidateHttpSession(true)
            //.deleteCookies("JSESSIONID,SPRING_SECURITY_REMEMBER_ME_COOKIE")
            .and()
        .csrf()
            .disable()
        // The session management is used to ensure the user only has one session. This isn't
        // compulsory but can add some extra security to your application.
        .sessionManagement()
            .maximumSessions(1);
    }

    @Override
    @Bean
    public UserDetailsService userDetailsServiceBean() throws Exception
    {
        return super.userDetailsServiceBean();
    }
}

SpringSecurityInitializer SpringSecurityInitializer

public class SpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer{
    //do nothing
}

With this config i'm not able to handle 404 error code. 使用此配置,我无法处理404错误代码。

Thanks in advance. 提前致谢。

Updated to add more information about config files 已更新以添加有关配置文件的更多信息

Conclusion seems to be that setting throwExceptionIfNoHandlerFound to true does not throw an exception when no handler is found. 结论似乎是将throwExceptionIfNoHandlerFound设置为true不会在找不到处理程序时抛出异常。

The solution is quite simple. 解决方案非常简单。 From the javadoc @ DispatcherServlet.setThrowExceptionIfNoHandlerFound . 来自javadoc @ DispatcherServlet.setThrowExceptionIfNoHandlerFound It states here that a NoHandlerFoundException will never be thrown if DefaultServletHttpRequestHandler is used. 它声明如果使用DefaultServletHttpRequestHandler,将永远不会抛出NoHandlerFoundException。

Solution hence is to remove the line 解决方案因此是删除线

   configurer.enable();

from your MvcConfiguration. 来自你的MvcConfiguration。 The exception should fire now and your GlobalExceptionHandler should do the rest! 现在应该触发异常,你的GlobalExceptionHandler应该完成其余的工作!

Workaround: Add @RequestMapping("/**") 解决方法:添加@RequestMapping(“/ **”)

@Controller
@ControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @RequestMapping("/**")
    public String handlerNotMappingRequest(HttpServletRequest request, HttpServletResponse response, HttpHeaders httpHeaders)
            throws NoHandlerFoundException {
        throw new NoHandlerFoundException("No handler mapping found.", request.getRequestURL().toString(), httpHeaders);
    }

    @ExceptionHandler(Throwable.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ModelAndView handleControllerException(Throwable ex) {
        logger.error("ErrorLog: ", ex);
        return new ModelAndView("error/exception", "exceptionMsg", "ExceptionHandler msg: " + ex.toString());
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ModelAndView handleNoHandlerFoundException(NoHandlerFoundException ex) {
        logger.error("ErrorLog: ", ex);
        return new ModelAndView("error/exception", "exceptionMsg", "NoHandlerFoundException msg: " + ex.toString());
    }
}

The solution is to extend AbstractAnnotationConfigDispatcherServletInitializer and override this method: 解决方案是扩展AbstractAnnotationConfigDispatcherServletInitializer并覆盖此方法:

@Override
protected DispatcherServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
    final DispatcherServlet dispatcherServlet = (DispatcherServlet) super.createDispatcherServlet(servletAppContext);
    dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
    return dispatcherServlet;
}

OR this one: 或者这一个:

@Override
public void customizeRegistration(ServletRegistration.Dynamic registration) {
    registration.setInitParameter("throwExceptionIfNoHandlerFound", "true");
}

And finally in your ControlerAdvice use this: 最后在你的ControlerAdvice使用:

@ExceptionHandler(NoHandlerFoundException.class)
public String error404(Exception ex) {

    return new ModelAndView("404");
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM