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:
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
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.
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.
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.
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!
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());
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With