This was an error that I encountered a few years back and it's still alive and well to trap programmers who use this feature unknowingly.
I had following simple code in which first method /services_1 worked and /services didn't work. It simply returns 405 Method Not Allowed status with empty response.
After debugging further I decided to list all the Servlets registered by Spring Boot and print there url-mappings to find out offending servlet using following code
Now why does it happen? It's because messageDispatcherServlet's default url-mapping is done to /services.
It's done in following property configuration class
I had following simple code in which first method /services_1 worked and /services didn't work. It simply returns 405 Method Not Allowed status with empty response.
@RestController @RestController class TestRest { @GetMapping("/services") public String test() { return "Hello World!"; } @GetMapping("/services_1") public String test2() { return "Hello World!"; } }So I started looking around and found that I had extra dependency in POM for web services, which was not being used and /services endpoint worked if I removed it. Following is the dependency in question
<dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web-services</artifactid> </dependency>This is a dependency used for SOAP web services support in Spring Boot. But why would Spring stop /services from being accessed in presence of this dependency? I was sure at this point that there was probably an extra servlet loaded by Spring which was hogging all requests to /services and thus intended REST method was not being called.
After debugging further I decided to list all the Servlets registered by Spring Boot and print there url-mappings to find out offending servlet using following code
@Autowired private List<ServletRegistrationBean>I know it's crude but it works. Following is the output that I got,servlets; ... for(ServletRegistrationBean servlet: servlets) { System.out.println(servlet.getServletName() + ": " + Arrays.toString(servlet.getUrlMappings().toArray())); }
dispatcherServlet: [/] messageDispatcherServlet: [/services/*]So there's the extra messageDispatcherServlet that I didn't need and was overriding /services method declared by my REST class.
Now why does it happen? It's because messageDispatcherServlet's default url-mapping is done to /services.
It's done in following property configuration class
package org.springframework.boot.autoconfigure.webservices; @ConfigurationProperties(prefix = "spring.webservices") public class WebServicesProperties { /** * Path that serves as the base URI for the services. */ private String path = "/services";and used in following class
class: org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration public class WebServicesAutoConfiguration { private final WebServicesProperties properties; @Bean public ServletRegistrationBeanSo that took a while to figure out, I must say a message or an error from Spring would have been nice and helpful to figure out what was going on.messageDispatcherServlet( ApplicationContext applicationContext) { MessageDispatcherServlet servlet = new MessageDispatcherServlet(); servlet.setApplicationContext(applicationContext); String path = this.properties.getPath(); String urlMapping = (path.endsWith("/") ? path + "*" : path + "/*"); ServletRegistrationBean registration = new ServletRegistrationBean<>( servlet, urlMapping);
...till next time