Log warning if @RequestMapping method has no explicit mapping

Commit c0b52d09f5 introduced support for
throwing an exception if a @RequestMapping handler method in a Spring
MVC controller was mapped to an empty path. This had negative side
effects for applications that intentionally mapped to an empty path,
potentially alongside a mapping to an explicit path for the same
handler method.

This commit addresses this by logging a warning (instead of throwing an
exception) if a @RequestMapping method is mapped only to empty paths.

This commit also introduces the same support for WebFlux-based
@RequestMapping handler methods.

Closes gh-22543
This commit is contained in:
Sam Brannen
2019-04-05 19:48:30 +02:00
parent e4525cf4c1
commit 72027b1746
7 changed files with 104 additions and 29 deletions

View File

@@ -62,7 +62,7 @@ import org.springframework.web.servlet.HandlerMapping;
* @author Sam Brannen
* @since 3.1
* @param <T> the mapping for a {@link HandlerMethod} containing the conditions
* needed to match the handler method to incoming request.
* needed to match the handler method to an incoming request.
*/
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
@@ -496,7 +496,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
protected abstract T getMappingForMethod(Method method, Class<?> handlerType);
/**
* Extract and return the URL paths contained in a mapping.
* Extract and return the URL paths contained in the supplied mapping.
*/
protected abstract Set<String> getMappingPathPatterns(T mapping);
@@ -616,11 +616,11 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
}
private void validateMethodMapping(HandlerMethod handlerMethod, T mapping) {
// Assert that the supplied mapping maps the supplied HandlerMethod
// to explicit, non-empty paths.
if (!getMappingPathPatterns(mapping).stream().allMatch(StringUtils::hasText)) {
throw new IllegalStateException(String.format("Missing path mapping. " +
"Handler method '%s' in bean '%s' must be mapped to a non-empty path. " +
// Log a warning if the supplied mapping maps the supplied HandlerMethod
// only to empty paths.
if (logger.isWarnEnabled() && getMappingPathPatterns(mapping).stream().noneMatch(StringUtils::hasText)) {
logger.warn(String.format(
"Handler method '%s' in bean '%s' is not mapped to an explicit path. " +
"If you wish to map to all paths, please map explicitly to \"/**\" or \"**\".",
handlerMethod, handlerMethod.getBean()));
}

View File

@@ -75,7 +75,7 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
/**
* Get the URL path patterns associated with this {@link RequestMappingInfo}.
* Get the URL path patterns associated with the supplied {@link RequestMappingInfo}.
*/
@Override
protected Set<String> getMappingPathPatterns(RequestMappingInfo info) {

View File

@@ -797,11 +797,28 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
}
@Test
public void unmappedPathMapping() {
assertThatThrownBy(() -> initServletWithControllers(UnmappedPathController.class))
.isInstanceOf(BeanCreationException.class)
.hasCauseInstanceOf(IllegalStateException.class)
.hasMessageContaining("Missing path mapping");
public void unmappedPathMapping() throws Exception {
initServletWithControllers(UnmappedPathController.class);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/bogus-unmapped");
MockHttpServletResponse response = new MockHttpServletResponse();
getServlet().service(request, response);
assertEquals("get", response.getContentAsString());
}
@Test
public void explicitAndEmptyPathsControllerMapping() throws Exception {
initServletWithControllers(ExplicitAndEmptyPathsController.class);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
MockHttpServletResponse response = new MockHttpServletResponse();
getServlet().service(request, response);
assertEquals("get", response.getContentAsString());
request = new MockHttpServletRequest("GET", "");
response = new MockHttpServletResponse();
getServlet().service(request, response);
assertEquals("get", response.getContentAsString());
}
@Test
@@ -2733,11 +2750,22 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
}
@Controller
@RequestMapping // path intentionally omitted
// @RequestMapping intentionally omitted
static class UnmappedPathController {
@GetMapping // path intentionally omitted
public void get(@RequestParam(required = false) String id) {
public void get(Writer writer) throws IOException {
writer.write("get");
}
}
@Controller
// @RequestMapping intentionally omitted
static class ExplicitAndEmptyPathsController {
@GetMapping({"/", ""})
public void get(Writer writer) throws IOException {
writer.write("get");
}
}