EN
Spring Boot 2 - request mapping with not decoded PathVariable (/path/{pathVariable})
4 points
In this short article, we would like to show how to get not decoded @PathVariable
value from URL in Spring Boot 2 MVC request.
Note: as not decoded
@PathVariable
value we understand: all characters that were escaped/encoded in URI component/segment will be not decoded, e.g.%20
will be not decoded as space (''), etc.
Simple steps:
1. Create ExampleController.java file:
xxxxxxxxxx
1
package example.controllers;
2
3
import example.annotations.PathSegment;
4
import org.springframework.beans.factory.annotation.Autowired;
5
import org.springframework.http.HttpStatus;
6
import org.springframework.http.ResponseEntity;
7
import org.springframework.stereotype.Controller;
8
import org.springframework.web.bind.annotation.*;
9
import org.springframework.web.servlet.HandlerMapping;
10
11
import javax.servlet.ServletException;
12
import javax.servlet.http.HttpServletRequest;
13
import javax.servlet.http.Part;
14
import java.io.IOException;
15
import java.net.URISyntaxException;
16
import java.util.Collection;
17
import java.util.function.Function;
18
19
20
public class ExampleController {
21
22
(
23
method = RequestMethod.GET,
24
value = "/items/{itemName}"
25
)
26
public ResponseEntity<String> getItem(
27
name = "itemName") String itemName // <--- contains value that is not decoded (e.g. it means %20 is not decoded to space), etc. (
28
) {
29
// Some logic here ...
30
}
31
}
2. Create PathSegment.java file:
xxxxxxxxxx
1
package example.annotations;
2
3
import org.springframework.core.MethodParameter;
4
import org.springframework.util.PathMatcher;
5
import org.springframework.web.bind.support.WebDataBinderFactory;
6
import org.springframework.web.context.request.NativeWebRequest;
7
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
8
import org.springframework.web.method.support.ModelAndViewContainer;
9
import org.springframework.web.servlet.HandlerMapping;
10
11
import javax.servlet.http.HttpServletRequest;
12
import java.lang.annotation.*;
13
import java.util.Map;
14
15
ElementType.PARAMETER) (
16
RetentionPolicy.RUNTIME) (
17
public @interface PathSegment {
18
19
String name();
20
21
class Resolver implements HandlerMethodArgumentResolver {
22
23
private PathMatcher pathMatcher;
24
25
public Resolver(PathMatcher pathMatcher) {
26
this.pathMatcher = pathMatcher;
27
}
28
29
30
public boolean supportsParameter(MethodParameter methodParameter) {
31
Annotation annotation = methodParameter.getParameterAnnotation(PathSegment.class);
32
return annotation != null;
33
}
34
35
36
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modeContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
37
PathSegment pathSegment = methodParameter.getParameterAnnotation(PathSegment.class);
38
if (pathSegment == null) {
39
return null;
40
}
41
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
42
if (servletRequest == null) {
43
return null;
44
}
45
String patternAttribute = (String) servletRequest.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
46
String mappingAttribute = (String) servletRequest.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
47
Map<String, String> templateVariables = this.pathMatcher.extractUriTemplateVariables(patternAttribute, mappingAttribute);
48
if (templateVariables.isEmpty()) {
49
return null;
50
}
51
return templateVariables.get(pathSegment.name());
52
}
53
}
54
}
3. MVCConfig.java file:
xxxxxxxxxx
1
package example.config;
2
3
import example.annotations.PathSegment;
4
import org.springframework.beans.factory.annotation.Autowired;
5
import org.springframework.context.annotation.Configuration;
6
import org.springframework.util.PathMatcher;
7
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
8
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
9
10
import java.util.List;
11
12
13
public class MVCConfig implements WebMvcConfigurer {
14
15
16
private PathMatcher pathMatcher; // or replace it with bean creation function
17
18
19
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
20
resolvers.add(new PathSegment.Resolver(this.pathMatcher));
21
}
22
}