Skip to content

Commit f025d60

Browse files
committed
Merge branch '6.2.x'
2 parents 5aab947 + 0f38c28 commit f025d60

File tree

4 files changed

+167
-2
lines changed

4 files changed

+167
-2
lines changed

spring-context/src/main/java/org/springframework/validation/DataBinder.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1051,7 +1051,7 @@ private boolean hasValuesFor(String paramPath, ValueResolver resolver) {
10511051
}
10521052
int startIdx = paramPath.length() + 1;
10531053
int endIdx = name.indexOf(']', startIdx);
1054-
String nestedPath = name.substring(0, endIdx + 2);
1054+
String nestedPath = ((name.length() > endIdx + 1) ? name.substring(0, endIdx + 2) : "");
10551055
boolean quoted = (endIdx - startIdx > 2 && name.charAt(startIdx) == '\'' && name.charAt(endIdx - 1) == '\'');
10561056
String key = (quoted ? name.substring(startIdx + 1, endIdx - 1) : name.substring(startIdx, endIdx));
10571057
if (map == null) {
@@ -1083,7 +1083,7 @@ private boolean hasValuesFor(String paramPath, ValueResolver resolver) {
10831083
SortedSet<Integer> indexes = null;
10841084
for (String name : valueResolver.getNames()) {
10851085
if (name.startsWith(paramPath + "[")) {
1086-
int endIndex = name.indexOf(']', paramPath.length() + 2);
1086+
int endIndex = name.indexOf(']', paramPath.length() + 1);
10871087
String rawIndex = name.substring(paramPath.length() + 1, endIndex);
10881088
int index = Integer.parseInt(rawIndex);
10891089
indexes = (indexes != null ? indexes : new TreeSet<>());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.web.servlet.samples.spr;
18+
19+
import java.util.List;
20+
import java.util.Map;
21+
22+
import org.junit.jupiter.api.Test;
23+
24+
import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
25+
import org.springframework.test.web.servlet.MockMvc;
26+
import org.springframework.web.bind.annotation.ModelAttribute;
27+
import org.springframework.web.bind.annotation.PostMapping;
28+
import org.springframework.web.bind.annotation.RestController;
29+
import org.springframework.web.context.WebApplicationContext;
30+
31+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
32+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
33+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
34+
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
35+
36+
@SpringJUnitWebConfig(ServletRequestDataBinderIntegrationTests.SpringWebKeyValueController.class)
37+
class ServletRequestDataBinderIntegrationTests {
38+
39+
@Test // gh-34043
40+
void postMap(WebApplicationContext wac) throws Exception {
41+
MockMvc mockMvc = webAppContextSetup(wac).build();
42+
mockMvc.perform(post("/map")
43+
.param("someMap[a]", "valueA")
44+
.param("someMap[b]", "valueB"))
45+
.andExpect(status().isOk())
46+
.andExpect(content().string("valueB"));
47+
}
48+
49+
@Test
50+
void postArray(WebApplicationContext wac) throws Exception {
51+
MockMvc mockMvc = webAppContextSetup(wac).build();
52+
mockMvc.perform(post("/array")
53+
.param("someArray[0]", "valueA")
54+
.param("someArray[1]", "valueB"))
55+
.andExpect(status().isOk())
56+
.andExpect(content().string("valueB"));
57+
}
58+
59+
@Test // gh-34121
60+
void postArrayWithEmptyIndex(WebApplicationContext wac) throws Exception {
61+
MockMvc mockMvc = webAppContextSetup(wac).build();
62+
mockMvc.perform(post("/array")
63+
.param("someArray[]", "valueA")
64+
.param("someArray[]", "valueB"))
65+
.andExpect(status().isOk())
66+
.andExpect(content().string("valueB"));
67+
}
68+
69+
@Test
70+
void postArrayWithoutIndex(WebApplicationContext wac) throws Exception {
71+
MockMvc mockMvc = webAppContextSetup(wac).build();
72+
mockMvc.perform(post("/array")
73+
.param("someArray", "valueA")
74+
.param("someArray", "valueB"))
75+
.andExpect(status().isOk())
76+
.andExpect(content().string("valueB"));
77+
}
78+
79+
@Test
80+
void postList(WebApplicationContext wac) throws Exception {
81+
MockMvc mockMvc = webAppContextSetup(wac).build();
82+
mockMvc.perform(post("/list")
83+
.param("someList[0]", "valueA")
84+
.param("someList[1]", "valueB"))
85+
.andExpect(status().isOk())
86+
.andExpect(content().string("valueB"));
87+
}
88+
89+
@Test // gh-34121
90+
void postListWithEmptyIndex(WebApplicationContext wac) throws Exception {
91+
MockMvc mockMvc = webAppContextSetup(wac).build();
92+
mockMvc.perform(post("/list")
93+
.param("someList[]", "valueA")
94+
.param("someList[]", "valueB"))
95+
.andExpect(status().isOk())
96+
.andExpect(content().string("valueB"));
97+
}
98+
99+
@Test
100+
void postListWithoutIndex(WebApplicationContext wac) throws Exception {
101+
MockMvc mockMvc = webAppContextSetup(wac).build();
102+
mockMvc.perform(post("/list")
103+
.param("someList", "valueA")
104+
.param("someList", "valueB"))
105+
.andExpect(status().isOk())
106+
.andExpect(content().string("valueB"));
107+
}
108+
109+
record PayloadWithMap(Map<String, String> someMap) {}
110+
111+
record PayloadWithArray(String[] someArray) {}
112+
113+
record PayloadWithList(List<String> someList) {}
114+
115+
@RestController
116+
@SuppressWarnings("unused")
117+
static class SpringWebKeyValueController {
118+
119+
@PostMapping("/map")
120+
String postMap(@ModelAttribute("payload") PayloadWithMap payload) {
121+
return payload.someMap.get("b");
122+
}
123+
124+
@PostMapping("/array")
125+
String postArray(@ModelAttribute("payload") PayloadWithArray payload) {
126+
return payload.someArray[1];
127+
}
128+
129+
@PostMapping("/list")
130+
String postList(@ModelAttribute("payload") PayloadWithList payload) {
131+
return payload.someList.get(1);
132+
}
133+
}
134+
135+
}

spring-web/src/main/java/org/springframework/web/bind/ServletRequestDataBinder.java

+4
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,10 @@ protected ServletRequest getRequest() {
241241

242242
protected @Nullable Object getRequestParameter(String name, Class<?> type) {
243243
Object value = this.request.getParameterValues(name);
244+
if (value == null && !name.endsWith ("[]") &&
245+
(List.class.isAssignableFrom(type) || type.isArray())) {
246+
value = this.request.getParameterValues(name + "[]");
247+
}
244248
return (ObjectUtils.isArray(value) && Array.getLength(value) == 1 ? Array.get(value, 0) : value);
245249
}
246250

spring-web/src/test/java/org/springframework/web/bind/ServletRequestDataBinderTests.java

+26
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,32 @@ void testFieldPrefixCausesFieldResetWithIgnoreUnknownFields() {
9393
assertThat(target.isPostProcessed()).isFalse();
9494
}
9595

96+
@Test
97+
public void testFieldWithArrayIndex() {
98+
TestBean target = new TestBean();
99+
ServletRequestDataBinder binder = new ServletRequestDataBinder(target);
100+
binder.setIgnoreUnknownFields(false);
101+
102+
MockHttpServletRequest request = new MockHttpServletRequest();
103+
request.addParameter("stringArray[0]", "ONE");
104+
request.addParameter("stringArray[1]", "TWO");
105+
binder.bind(request);
106+
assertThat(target.getStringArray()).containsExactly("ONE", "TWO");
107+
}
108+
109+
@Test
110+
public void testFieldWithEmptyArrayIndex() {
111+
TestBean target = new TestBean();
112+
ServletRequestDataBinder binder = new ServletRequestDataBinder(target);
113+
binder.setIgnoreUnknownFields(false);
114+
115+
MockHttpServletRequest request = new MockHttpServletRequest();
116+
request.addParameter("stringArray[]", "ONE");
117+
request.addParameter("stringArray[]", "TWO");
118+
binder.bind(request);
119+
assertThat(target.getStringArray()).containsExactly("ONE", "TWO");
120+
}
121+
96122
@Test
97123
void testFieldDefault() {
98124
TestBean target = new TestBean();

0 commit comments

Comments
 (0)