|
23 | 23 | import org.apache.commons.logging.LogFactory;
|
24 | 24 |
|
25 | 25 | import org.springframework.core.MethodParameter;
|
26 |
| -import org.springframework.core.ReactiveAdapter; |
27 |
| -import org.springframework.core.ReactiveAdapterRegistry; |
28 | 26 | import org.springframework.core.convert.ConversionService;
|
29 |
| -import org.springframework.core.convert.TypeDescriptor; |
30 | 27 | import org.springframework.lang.Nullable;
|
| 28 | +import org.springframework.util.Assert; |
31 | 29 | import org.springframework.util.StringUtils;
|
32 | 30 | import org.springframework.web.bind.annotation.PathVariable;
|
33 | 31 |
|
| 32 | + |
34 | 33 | /**
|
35 | 34 | * An implementation of {@link HttpServiceMethodArgumentResolver} that resolves
|
36 | 35 | * request path variables based on method arguments annotated
|
|
42 | 41 | */
|
43 | 42 | public class PathVariableArgumentResolver implements HttpServiceMethodArgumentResolver {
|
44 | 43 |
|
45 |
| - private static final Log LOG = LogFactory.getLog(PathVariableArgumentResolver.class); |
46 |
| - private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class); |
47 |
| - @Nullable |
| 44 | + private static final Log logger = LogFactory.getLog(PathVariableArgumentResolver.class); |
| 45 | + |
| 46 | + |
48 | 47 | private final ConversionService conversionService;
|
49 | 48 |
|
50 |
| - public PathVariableArgumentResolver(@Nullable ConversionService conversionService) { |
| 49 | + |
| 50 | + public PathVariableArgumentResolver(ConversionService conversionService) { |
| 51 | + Assert.notNull(conversionService, "ConversionService is required"); |
51 | 52 | this.conversionService = conversionService;
|
52 | 53 | }
|
53 | 54 |
|
| 55 | + |
| 56 | + @SuppressWarnings("unchecked") |
54 | 57 | @Override
|
55 |
| - public void resolve(@Nullable Object argument, MethodParameter parameter, |
56 |
| - HttpRequestDefinition requestDefinition) { |
| 58 | + public void resolve( |
| 59 | + @Nullable Object argument, MethodParameter parameter, HttpRequestDefinition requestDefinition) { |
| 60 | + |
57 | 61 | PathVariable annotation = parameter.getParameterAnnotation(PathVariable.class);
|
58 | 62 | if (annotation == null) {
|
59 | 63 | return;
|
60 | 64 | }
|
61 |
| - String resolvedAnnotationName = StringUtils.hasText(annotation.value()) |
62 |
| - ? annotation.value() : annotation.name(); |
63 |
| - boolean required = annotation.required(); |
64 |
| - Object resolvedArgument = resolveFromOptional(argument); |
65 |
| - if (resolvedArgument instanceof Map<?, ?> valueMap) { |
66 |
| - if (StringUtils.hasText(resolvedAnnotationName)) { |
67 |
| - Object value = valueMap.get(resolvedAnnotationName); |
68 |
| - Object resolvedValue = resolveFromOptional(value); |
69 |
| - addUriParameter(requestDefinition, resolvedAnnotationName, resolvedValue, required); |
70 |
| - return; |
71 |
| - } |
72 |
| - valueMap.entrySet() |
73 |
| - .forEach(entry -> addUriParameter(requestDefinition, entry, required)); |
74 |
| - return; |
75 |
| - } |
76 |
| - String name = StringUtils.hasText(resolvedAnnotationName) |
77 |
| - ? resolvedAnnotationName : parameter.getParameterName(); |
78 |
| - addUriParameter(requestDefinition, name, resolvedArgument, required); |
79 |
| - } |
80 | 65 |
|
81 |
| - private void addUriParameter(HttpRequestDefinition requestDefinition, @Nullable String name, |
82 |
| - @Nullable Object value, boolean required) { |
83 |
| - if (name == null) { |
84 |
| - throw new IllegalStateException("Path variable name cannot be null"); |
| 66 | + if (Map.class.isAssignableFrom(parameter.getParameterType())) { |
| 67 | + if (argument != null) { |
| 68 | + Assert.isInstanceOf(Map.class, argument); |
| 69 | + ((Map<String, ?>) argument).forEach((key, value) -> |
| 70 | + addUriParameter(key, value, annotation.required(), requestDefinition)); |
| 71 | + } |
85 | 72 | }
|
86 |
| - String stringValue = getStringValue(value, required); |
87 |
| - if (LOG.isTraceEnabled()) { |
88 |
| - LOG.trace("Path variable " + name + " resolved to " + stringValue); |
| 73 | + else { |
| 74 | + String name = StringUtils.hasText(annotation.value()) ? annotation.value() : annotation.name(); |
| 75 | + name = StringUtils.hasText(name) ? name : parameter.getParameterName(); |
| 76 | + Assert.notNull(name, "Failed to determine path variable name for parameter: " + parameter); |
| 77 | + addUriParameter(name, argument, annotation.required(), requestDefinition); |
89 | 78 | }
|
90 |
| - requestDefinition.getUriVariables().put(name, stringValue); |
91 |
| - } |
92 |
| - |
93 |
| - @Nullable |
94 |
| - private String getStringValue(@Nullable Object value, boolean required) { |
95 |
| - validateForNull(value, required); |
96 |
| - validateForReactiveWrapper(value); |
97 |
| - return value != null |
98 |
| - ? convertToString(TypeDescriptor.valueOf(value.getClass()), value) : null; |
99 | 79 | }
|
100 | 80 |
|
101 |
| - private void addUriParameter(HttpRequestDefinition requestDefinition, |
102 |
| - Map.Entry<?, ?> entry, boolean required) { |
103 |
| - Object resolvedName = resolveFromOptional(entry.getKey()); |
104 |
| - String stringName = getStringValue(resolvedName, true); |
105 |
| - Object resolvedValue = resolveFromOptional(entry.getValue()); |
106 |
| - addUriParameter(requestDefinition, stringName, resolvedValue, required); |
107 |
| - } |
| 81 | + private void addUriParameter( |
| 82 | + String name, @Nullable Object value, boolean required, HttpRequestDefinition requestDefinition) { |
108 | 83 |
|
109 |
| - private void validateForNull(@Nullable Object argument, boolean required) { |
110 |
| - if (argument == null) { |
111 |
| - if (required) { |
112 |
| - throw new IllegalStateException("Required variable cannot be null"); |
113 |
| - } |
| 84 | + if (value instanceof Optional) { |
| 85 | + value = ((Optional<?>) value).orElse(null); |
114 | 86 | }
|
115 |
| - } |
116 | 87 |
|
117 |
| - private void validateForReactiveWrapper(@Nullable Object object) { |
118 |
| - if (object != null) { |
119 |
| - Class<?> type = object.getClass(); |
120 |
| - ReactiveAdapterRegistry adapterRegistry = ReactiveAdapterRegistry.getSharedInstance(); |
121 |
| - ReactiveAdapter adapter = adapterRegistry.getAdapter(type); |
122 |
| - if (adapter != null) { |
123 |
| - throw new IllegalStateException(getClass().getSimpleName() + |
124 |
| - " does not support reactive type wrapper: " + type); |
125 |
| - } |
| 88 | + if (value == null) { |
| 89 | + Assert.isTrue(!required, "Missing required path variable '" + name + "'"); |
| 90 | + return; |
126 | 91 | }
|
127 | 92 |
|
128 |
| - } |
129 |
| - |
130 |
| - @Nullable |
131 |
| - private Object resolveFromOptional(@Nullable Object argument) { |
132 |
| - if (argument instanceof Optional) { |
133 |
| - return ((Optional<?>) argument).orElse(null); |
| 93 | + if (!(value instanceof String)) { |
| 94 | + value = this.conversionService.convert(value, String.class); |
134 | 95 | }
|
135 |
| - return argument; |
136 |
| - } |
137 | 96 |
|
138 |
| - @Nullable |
139 |
| - private String convertToString(TypeDescriptor typeDescriptor, @Nullable Object value) { |
140 |
| - if (value == null) { |
141 |
| - return null; |
| 97 | + if (logger.isTraceEnabled()) { |
| 98 | + logger.trace("Resolved path variable '" + name + "' to " + value); |
142 | 99 | }
|
143 |
| - if (value instanceof String) { |
144 |
| - return (String) value; |
145 |
| - } |
146 |
| - if (this.conversionService != null) { |
147 |
| - return (String) this.conversionService.convert(value, typeDescriptor, STRING_TYPE_DESCRIPTOR); |
148 |
| - } |
149 |
| - return String.valueOf(value); |
| 100 | + |
| 101 | + requestDefinition.getUriVariables().put(name, (String) value); |
150 | 102 | }
|
151 | 103 |
|
152 | 104 | }
|
0 commit comments