Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

Spring MVC version is 4.1.5

I have read this:

  • Stackoverflow Example 1
  • Stackoverflow Example 1 continuation

But unfortunately it does not work for me.

Here you have my conf:

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)

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

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

AppConfiguration

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

}

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

@Configuration
public class RepositoryConfiguration {

}

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

public class SpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer{
    //do nothing
}

With this config i'm not able to handle 404 error code.

Thanks in advance.

Updated to add more information about config files

like image 867
moyanojv Avatar asked May 28 '15 11:05

moyanojv


People also ask

How do I fix error 404 in API?

You fix this by opening the listen step in your VSM file, and changing the base path in there, so you don't get a 404 error. You could change that to "/api/" so any api requests are dealt-with, or "/api/retrieveId/" so only retrieveId messages are dealt-with, or "/" so all requests are dealt-with.

Why am I getting a 404 on API?

A HTTP 404 error happens when a resource is unavailable. The client (web browser) received a message from the server (remote computer) that the specific resource (web page/URL) is unavailable.

How do I fix error 404 in Tomcat?

This error indicates that the server could not find the desired resource. This resource can be any file such as JSP, HTML, or image resource. Usually, the resource is present, but it is referenced incorrectly. In most cases, you can fix this by correcting the URL.


2 Answers

Conclusion seems to be that setting throwExceptionIfNoHandlerFound to true does not throw an exception when no handler is found.

The solution is quite simple. From the javadoc @ DispatcherServlet.setThrowExceptionIfNoHandlerFound. It states here that a NoHandlerFoundException will never be thrown if DefaultServletHttpRequestHandler is used.

Solution hence is to remove the line

   configurer.enable();

from your MvcConfiguration. The exception should fire now and your GlobalExceptionHandler should do the rest!

like image 193
s.ijpma Avatar answered Oct 31 '22 14:10

s.ijpma


Workaround: Add @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());
    }
}
like image 45
KhanhLV Avatar answered Oct 31 '22 13:10

KhanhLV