2

How can I define a default handler, or controller action, that will be invoked when no other handler is found for a given URL?

I tried by using a catch-all pattern /** (syntax is Kotlin):

@Controller
class DefaultController {

    @RequestMapping("/**")
    fun default(...) {
        ...
    }
}

But this gets matched with higher precedence that Spring's own handlers, for example the static file path configured in spring.mvc.static-path-pattern is no longer available. I need my default handler to have the lowest precedence.

Andronicus
  • 25,419
  • 17
  • 47
  • 88
Tobia
  • 17,856
  • 6
  • 74
  • 93

1 Answers1

3

You would have to add your implementation of HandlerMapping and add it to list of handlers. You need to specify the order of handlers taking care of request as well:

@Bean
public SimpleUrlHandlerMapping simpleUrlHandlerMapping() {
    SimpleUrlHandlerMapping simpleUrlHandlerMapping
      = new SimpleUrlHandlerMapping();
    Map<String, Object> urlMap = new HashMap<>();
    urlMap.put("/**", defaultController());
    simpleUrlHandlerMapping.setUrlMap(urlMap);
    simpleUrlHandlerMapping.setOrder(1);
    return simpleUrlHandlerMapping;
}

Here the defaultController() method returns a @Controller, that you have defined for the given mapping, so DefaultController. The setOrder method defines a priority (order) of handlers, starting from 0. Of course some default HandlerMapping must be defined as a @Bean as well. More about such configuration you can find here.

Edit with some thoughts from @Tobia:

You need to remove the @RequestMapping annotation so that the controller is not picked up by RequestMappingHandlerMapping and implement the AbstractController interface to override the definition of controller in handleRequestInternal().

Andronicus
  • 25,419
  • 17
  • 47
  • 88
  • Wait, I'm not sure it works. I used `setOrder(Ordered.LOWEST_PRECEDENCE)` instead of 1, but it still overrides the resource handler. I need it to be considered *after* the resource handler. – Tobia Feb 23 '19 at 13:03
  • I'm using Spring Boot, so there are several default handlers. I just need to add mine. I have inspected the application beans (using `applicationContext.getBeansOfType(HandlerMapping.class)` and I can see that mine is loaded with order=2147483647, which is a higher number (lower precedence) than order=2147483646 resourceHandlerMapping. That one is the default SimpleUrlHandlerMapping for static files, with a clear urlMap of `/static/**`. But whenever I call a static URL, my controller is invoked instead. – Tobia Feb 23 '19 at 13:34
  • I think I know what's going on. My controller is still being picked up by requestMappingHandlerMapping because it still has the @RequestMapping annotation. But if I remove the annotation, I get `javax.servlet.ServletException: No adapter for handler [...Controller@6da127ce]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler` – Tobia Feb 23 '19 at 13:51
  • So I had to remove @RequestMapping, implement AbstractController and override `handleRequestInternal()` "the old way" (Spring and Spring Boot have different definitions of what is a "Controller"...) Now it works. Thank you for your support. – Tobia Feb 23 '19 at 14:27
  • Damn, that is quite a good job. Honestly, I have never implemented it myself, I didn't know there is that much to it. – Andronicus Feb 23 '19 at 14:36
  • No problem. I've learn a bit myself. I've edited the answer with your information, so that it's complete. – Andronicus Feb 23 '19 at 14:42