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 classclass: org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration
public class WebServicesAutoConfiguration {
private final WebServicesProperties properties;
@Bean
public ServletRegistrationBean 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);
So 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....till next time