Merge branch '6.2.x'

This commit is contained in:
Simon Baslé
2024-12-27 11:00:10 +01:00
4 changed files with 167 additions and 2 deletions

View File

@@ -1051,7 +1051,7 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
}
int startIdx = paramPath.length() + 1;
int endIdx = name.indexOf(']', startIdx);
String nestedPath = name.substring(0, endIdx + 2);
String nestedPath = ((name.length() > endIdx + 1) ? name.substring(0, endIdx + 2) : "");
boolean quoted = (endIdx - startIdx > 2 && name.charAt(startIdx) == '\'' && name.charAt(endIdx - 1) == '\'');
String key = (quoted ? name.substring(startIdx + 1, endIdx - 1) : name.substring(startIdx, endIdx));
if (map == null) {
@@ -1083,7 +1083,7 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
SortedSet<Integer> indexes = null;
for (String name : valueResolver.getNames()) {
if (name.startsWith(paramPath + "[")) {
int endIndex = name.indexOf(']', paramPath.length() + 2);
int endIndex = name.indexOf(']', paramPath.length() + 1);
String rawIndex = name.substring(paramPath.length() + 1, endIndex);
int index = Integer.parseInt(rawIndex);
indexes = (indexes != null ? indexes : new TreeSet<>());

View File

@@ -0,0 +1,135 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.web.servlet.samples.spr;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
@SpringJUnitWebConfig(ServletRequestDataBinderIntegrationTests.SpringWebKeyValueController.class)
class ServletRequestDataBinderIntegrationTests {
@Test // gh-34043
void postMap(WebApplicationContext wac) throws Exception {
MockMvc mockMvc = webAppContextSetup(wac).build();
mockMvc.perform(post("/map")
.param("someMap[a]", "valueA")
.param("someMap[b]", "valueB"))
.andExpect(status().isOk())
.andExpect(content().string("valueB"));
}
@Test
void postArray(WebApplicationContext wac) throws Exception {
MockMvc mockMvc = webAppContextSetup(wac).build();
mockMvc.perform(post("/array")
.param("someArray[0]", "valueA")
.param("someArray[1]", "valueB"))
.andExpect(status().isOk())
.andExpect(content().string("valueB"));
}
@Test // gh-34121
void postArrayWithEmptyIndex(WebApplicationContext wac) throws Exception {
MockMvc mockMvc = webAppContextSetup(wac).build();
mockMvc.perform(post("/array")
.param("someArray[]", "valueA")
.param("someArray[]", "valueB"))
.andExpect(status().isOk())
.andExpect(content().string("valueB"));
}
@Test
void postArrayWithoutIndex(WebApplicationContext wac) throws Exception {
MockMvc mockMvc = webAppContextSetup(wac).build();
mockMvc.perform(post("/array")
.param("someArray", "valueA")
.param("someArray", "valueB"))
.andExpect(status().isOk())
.andExpect(content().string("valueB"));
}
@Test
void postList(WebApplicationContext wac) throws Exception {
MockMvc mockMvc = webAppContextSetup(wac).build();
mockMvc.perform(post("/list")
.param("someList[0]", "valueA")
.param("someList[1]", "valueB"))
.andExpect(status().isOk())
.andExpect(content().string("valueB"));
}
@Test // gh-34121
void postListWithEmptyIndex(WebApplicationContext wac) throws Exception {
MockMvc mockMvc = webAppContextSetup(wac).build();
mockMvc.perform(post("/list")
.param("someList[]", "valueA")
.param("someList[]", "valueB"))
.andExpect(status().isOk())
.andExpect(content().string("valueB"));
}
@Test
void postListWithoutIndex(WebApplicationContext wac) throws Exception {
MockMvc mockMvc = webAppContextSetup(wac).build();
mockMvc.perform(post("/list")
.param("someList", "valueA")
.param("someList", "valueB"))
.andExpect(status().isOk())
.andExpect(content().string("valueB"));
}
record PayloadWithMap(Map<String, String> someMap) {}
record PayloadWithArray(String[] someArray) {}
record PayloadWithList(List<String> someList) {}
@RestController
@SuppressWarnings("unused")
static class SpringWebKeyValueController {
@PostMapping("/map")
String postMap(@ModelAttribute("payload") PayloadWithMap payload) {
return payload.someMap.get("b");
}
@PostMapping("/array")
String postArray(@ModelAttribute("payload") PayloadWithArray payload) {
return payload.someArray[1];
}
@PostMapping("/list")
String postList(@ModelAttribute("payload") PayloadWithList payload) {
return payload.someList.get(1);
}
}
}

View File

@@ -241,6 +241,10 @@ public class ServletRequestDataBinder extends WebDataBinder {
protected @Nullable Object getRequestParameter(String name, Class<?> type) {
Object value = this.request.getParameterValues(name);
if (value == null && !name.endsWith ("[]") &&
(List.class.isAssignableFrom(type) || type.isArray())) {
value = this.request.getParameterValues(name + "[]");
}
return (ObjectUtils.isArray(value) && Array.getLength(value) == 1 ? Array.get(value, 0) : value);
}

View File

@@ -93,6 +93,32 @@ class ServletRequestDataBinderTests {
assertThat(target.isPostProcessed()).isFalse();
}
@Test
public void testFieldWithArrayIndex() {
TestBean target = new TestBean();
ServletRequestDataBinder binder = new ServletRequestDataBinder(target);
binder.setIgnoreUnknownFields(false);
MockHttpServletRequest request = new MockHttpServletRequest();
request.addParameter("stringArray[0]", "ONE");
request.addParameter("stringArray[1]", "TWO");
binder.bind(request);
assertThat(target.getStringArray()).containsExactly("ONE", "TWO");
}
@Test
public void testFieldWithEmptyArrayIndex() {
TestBean target = new TestBean();
ServletRequestDataBinder binder = new ServletRequestDataBinder(target);
binder.setIgnoreUnknownFields(false);
MockHttpServletRequest request = new MockHttpServletRequest();
request.addParameter("stringArray[]", "ONE");
request.addParameter("stringArray[]", "TWO");
binder.bind(request);
assertThat(target.getStringArray()).containsExactly("ONE", "TWO");
}
@Test
void testFieldDefault() {
TestBean target = new TestBean();