Excluding certain paths in a request mapping for a

2019-08-02 17:05发布

问题:

I am currently trying to create a Controller to handle all unknown URLs globally. So what I did was create a controller similar to the one below

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping("/**")
public class UnknownUrlController {
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String getError404() {
        return "/error404";
    }
}

My intention was to ensure that the default servlet which just returns a String "Not Found" to the browser was not invoked. This strategy worked since all unknown URL was handled by this Controller.

However the issue was that the controller was also invoked for all my static resources (images, js and css files) I had configured in my WebMvcConfigurerAdapter as follows

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

so instead of my static files served to the browser, my error pages were served in its place. Although I later understood that controllers mappings take precedence over static resources from this answer . This is the reason I would like to know how to exclude my resources url mappings from being handled by this controller so it would only be concerned with trapping the unknown URLs.

Before proceeding with this strategy I had tried some other things that I could not get to work (I am probably missing something)

  1. Setting the throwExceptionIfNoHandlerFound field of my DispatcherServlet to true so that the exception that should be thrown when no handler for a URL mapping is found and then handle it with globally as described here . However it seems a default handler is always assigned for unknown paths (/**) and so no exception is ever thrown.
  2. created an application.properties files and set spring.mvc.throw-exception-if-no-handler-found=true.

All my configuration are Java based without any xml files and would prefer to keep it that way.

回答1:

There is a better way to write custom error handlers in Spring. See this below:

@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public String handleResourceNotFoundException() {
    return "notfound";
}

It as a nice blog entry about exception handlers in Spring MVC.

If the dispatcherServlet does not throw an exception in the case of 404, then you should turn it on using the setThrowExceptionIfNoHandlerFound() method of the DispatcherServlet.

Here you can find more information about, how to turn it on



回答2:

Spring Boot 1.4+ supports declarative error pages based on HTML status code.

http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-error-handling-custom-error-pages



回答3:

Finally found out how to fix the issue. The solution was to remove the default servlet handler in my Java configuration. In my Java configuration class (the one that extends WebMvcConfigurerAdapter) I had earlier enabled the default Servlet as follows

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

This configures a DefaultServletHttpRequestHandler with a URL mapping of /** as explained in this section of the Spring mvc documentation. This was the reason why no exception was thrown when no handler was found for a URL even though I had set the throwExceptionIfNoHandlerFound of my DispatcherServlet to true as I stated in my question.

After removing the configuration from my class. I created a global Exception handler class and added a method for NoHandlerFoundException cases. This is the exception that is thrown when no Controller handler is found for a URL mapping. The sample code is shown below. This is also well explained in this section of the spring mvc documentation.

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @RequestMapping( value = {"/*","/**"},method = RequestMethod.GET)
    public String handle(HttpServletRequest request, NoHandlerFoundException ex) {
        return "error-404";
    }
}