Refine empty path handling in MvcUriComponentsBuilder

Return "" from methods that obtain controller or method mapping to
avoid side effect of a trailing slash appearing.

Closes gh-29897
This commit is contained in:
rstoyanchev
2023-02-02 13:22:06 +00:00
parent 84bc1dfa89
commit 7851994a17
2 changed files with 30 additions and 10 deletions

View File

@@ -180,6 +180,7 @@ public class MvcUriComponentsBuilder {
builder.path(prefix);
String mapping = getClassMapping(controllerType);
mapping = (!StringUtils.hasText(prefix + mapping) ? "/" : mapping);
builder.path(mapping);
return builder;
@@ -280,10 +281,10 @@ public class MvcUriComponentsBuilder {
* @RequestMapping("/people/{id}/addresses")
* class AddressController {
*
* @RequestMapping("/{country}")
* @GetMapping("/{country}")
* public HttpEntity<Void> getAddressesForCountry(@PathVariable String country) { ... }
*
* @RequestMapping(value="/", method=RequestMethod.POST)
* @PostMapping
* public void addAddress(Address address) { ... }
* }
* </pre>
@@ -546,6 +547,9 @@ public class MvcUriComponentsBuilder {
if (StringUtils.hasLength(path) && !path.startsWith("/")) {
path = "/" + path;
}
else if (!StringUtils.hasText(prefix + path)) {
path = "/";
}
builder.path(path);
return applyContributors(builder, method, args);
@@ -577,11 +581,11 @@ public class MvcUriComponentsBuilder {
Assert.notNull(controllerType, "'controllerType' must not be null");
RequestMapping mapping = AnnotatedElementUtils.findMergedAnnotation(controllerType, RequestMapping.class);
if (mapping == null) {
return "/";
return "";
}
String[] paths = mapping.path();
if (ObjectUtils.isEmpty(paths) || !StringUtils.hasLength(paths[0])) {
return "/";
return "";
}
if (paths.length > 1 && logger.isTraceEnabled()) {
logger.trace("Using first of multiple paths on " + controllerType.getName());
@@ -597,7 +601,7 @@ public class MvcUriComponentsBuilder {
}
String[] paths = requestMapping.path();
if (ObjectUtils.isEmpty(paths) || !StringUtils.hasLength(paths[0])) {
return "/";
return "";
}
if (paths.length > 1 && logger.isTraceEnabled()) {
logger.trace("Using first of multiple paths on " + method.toGenericString());
@@ -845,7 +849,7 @@ public class MvcUriComponentsBuilder {
public MethodArgumentBuilder(@Nullable UriComponentsBuilder baseUrl, Class<?> controllerType, Method method) {
Assert.notNull(controllerType, "'controllerType' is required");
Assert.notNull(method, "'method' is required");
this.baseUrl = baseUrl != null ? baseUrl : UriComponentsBuilder.fromPath(getPath());
this.baseUrl = (baseUrl != null ? baseUrl : UriComponentsBuilder.fromPath(getPath()));
this.controllerType = controllerType;
this.method = method;
this.argumentValues = new Object[method.getParameterCount()];
@@ -854,7 +858,7 @@ public class MvcUriComponentsBuilder {
private static String getPath() {
UriComponentsBuilder builder = ServletUriComponentsBuilder.fromCurrentServletMapping();
String path = builder.build().getPath();
return path != null ? path : "";
return (path != null ? path : "");
}
public MethodArgumentBuilder arg(int index, Object value) {

View File

@@ -234,12 +234,19 @@ public class MvcUriComponentsBuilderTests {
}
@Test
public void fromMethodNameNotMapped() {
UriComponents uriComponents = fromMethodName(UnmappedController.class, "unmappedMethod").build();
public void fromMethodNameInUnmappedController() {
UriComponents uriComponents = fromMethodName(UnmappedController.class, "requestMappingMethod").build();
assertThat(uriComponents.toUriString()).isEqualTo("http://localhost/");
}
@Test // gh-29897
public void fromMethodNameInUnmappedControllerMethod() {
UriComponents uriComponents = fromMethodName(UnmappedControllerMethod.class, "getMethod").build();
assertThat(uriComponents.toUriString()).isEqualTo("http://localhost/path");
}
@Test
public void fromMethodNameWithCustomBaseUrlViaStaticCall() {
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("https://example.org:9090/base");
@@ -535,7 +542,16 @@ public class MvcUriComponentsBuilderTests {
private class UnmappedController {
@RequestMapping
public void unmappedMethod() {
public void requestMappingMethod() {
}
}
@RequestMapping("/path")
private class UnmappedControllerMethod {
@GetMapping
public void getMethod() {
}
}