Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 68c575a

Browse files
committed
Revise "Skip binding entirely when field is not allowed"
This commit reverts the changes made to WebDataBinder's doBind() implementation in e4d03f6 and instead implements the skipping logic directly in checkFieldDefaults(), checkFieldMarkers(), and adaptEmptyArrayIndices() by preemptively checking if the corresponding field is allowed. This commit also improves the Javadoc and adds missing tests. Fixes gh-36625
1 parent cb32046 commit 68c575a

2 files changed

Lines changed: 135 additions & 52 deletions

File tree

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

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
* @author Juergen Hoeller
5656
* @author Scott Andrews
5757
* @author Brian Clozel
58+
* @author Sam Brannen
5859
* @since 1.2
5960
* @see #registerCustomEditor
6061
* @see #setAllowedFields
@@ -220,28 +221,28 @@ public boolean isBindEmptyMultipartFiles() {
220221
}
221222

222223
/**
223-
* This implementation performs a field default and marker check
224-
* before delegating to the superclass binding process.
225-
* @see #checkFieldDefaults
226-
* @see #checkFieldMarkers
224+
* This implementation checks default fields and marker fields and then adapts
225+
* empty array indices before delegating to the superclass binding process.
226+
* @see #checkFieldDefaults(MutablePropertyValues)
227+
* @see #checkFieldMarkers(MutablePropertyValues)
228+
* @see #adaptEmptyArrayIndices(MutablePropertyValues)
227229
*/
228230
@Override
229231
protected void doBind(MutablePropertyValues mpvs) {
230-
checkAllowedFields(mpvs);
231232
checkFieldDefaults(mpvs);
232233
checkFieldMarkers(mpvs);
233234
adaptEmptyArrayIndices(mpvs);
234-
checkRequiredFields(mpvs);
235-
applyPropertyValues(mpvs);
235+
super.doBind(mpvs);
236236
}
237237

238238
/**
239-
* Check the given property values for field defaults,
240-
* i.e. for fields that start with the field default prefix.
241-
* <p>The existence of a field defaults indicates that the specified
242-
* value should be used if the field is otherwise not present.
239+
* Check the given property values for fields that start with the field default
240+
* prefix.
241+
* <p>The existence of a field default indicates that the specified value should
242+
* be used if the field is allowed and is otherwise not present.
243243
* @param mpvs the property values to be bound (can be modified)
244-
* @see #getFieldDefaultPrefix
244+
* @see #getFieldDefaultPrefix()
245+
* @see #isAllowed(String)
245246
*/
246247
protected void checkFieldDefaults(MutablePropertyValues mpvs) {
247248
String fieldDefaultPrefix = getFieldDefaultPrefix();
@@ -250,7 +251,7 @@ protected void checkFieldDefaults(MutablePropertyValues mpvs) {
250251
for (PropertyValue pv : pvArray) {
251252
if (pv.getName().startsWith(fieldDefaultPrefix)) {
252253
String field = pv.getName().substring(fieldDefaultPrefix.length());
253-
if (getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) {
254+
if (isAllowed(field) && getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) {
254255
mpvs.add(field, pv.getValue());
255256
}
256257
mpvs.removePropertyValue(pv);
@@ -260,14 +261,15 @@ protected void checkFieldDefaults(MutablePropertyValues mpvs) {
260261
}
261262

262263
/**
263-
* Check the given property values for field markers,
264-
* i.e. for fields that start with the field marker prefix.
265-
* <p>The existence of a field marker indicates that the specified
266-
* field existed in the form. If the property values do not contain
267-
* a corresponding field value, the field will be considered as empty
268-
* and will be reset appropriately.
264+
* Check the given property values for fields that start with the field marker
265+
* prefix.
266+
* <p>The existence of a field marker indicates that the specified field existed
267+
* in the form.
268+
* <p>If the field is allowed and the property values do not contain a corresponding
269+
* field value, the field will be considered as empty and will be reset appropriately.
269270
* @param mpvs the property values to be bound (can be modified)
270-
* @see #getFieldMarkerPrefix
271+
* @see #getFieldMarkerPrefix()
272+
* @see #isAllowed(String)
271273
* @see #getEmptyValue(String, Class)
272274
*/
273275
protected void checkFieldMarkers(MutablePropertyValues mpvs) {
@@ -277,7 +279,7 @@ protected void checkFieldMarkers(MutablePropertyValues mpvs) {
277279
for (PropertyValue pv : pvArray) {
278280
if (pv.getName().startsWith(fieldMarkerPrefix)) {
279281
String field = pv.getName().substring(fieldMarkerPrefix.length());
280-
if (getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) {
282+
if (isAllowed(field) && getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) {
281283
Class<?> fieldType = getPropertyAccessor().getPropertyType(field);
282284
mpvs.add(field, getEmptyValue(field, fieldType));
283285
}
@@ -288,19 +290,22 @@ protected void checkFieldMarkers(MutablePropertyValues mpvs) {
288290
}
289291

290292
/**
291-
* Check for property values with names that end on {@code "[]"}. This is
292-
* used by some clients for array syntax without an explicit index value.
293-
* If such values are found, drop the brackets to adapt to the expected way
294-
* of expressing the same for data binding purposes.
293+
* Check the given property values for fields that end with empty brackets
294+
* ({@code []}).
295+
* <p>This is used by some clients for array syntax without an explicit index
296+
* value.
297+
* <p>If such a field is allowed, the brackets will be removed in order to adapt
298+
* to the expected format for expressing the same for data binding purposes.
295299
* @param mpvs the property values to be bound (can be modified)
296300
* @since 5.3
301+
* @see #isAllowed(String)
297302
*/
298303
protected void adaptEmptyArrayIndices(MutablePropertyValues mpvs) {
299304
for (PropertyValue pv : mpvs.getPropertyValues()) {
300305
String name = pv.getName();
301306
if (name.endsWith("[]")) {
302307
String field = name.substring(0, name.length() - 2);
303-
if (getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) {
308+
if (isAllowed(field) && getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) {
304309
mpvs.add(field, pv.getValue());
305310
}
306311
mpvs.removePropertyValue(pv);

spring-web/src/test/java/org/springframework/web/bind/support/WebRequestDataBinderTests.java

Lines changed: 104 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@
2323
import java.util.Map;
2424
import java.util.Set;
2525

26+
import org.junit.jupiter.api.Nested;
2627
import org.junit.jupiter.api.Test;
28+
import org.junit.jupiter.params.Parameter;
29+
import org.junit.jupiter.params.ParameterizedClass;
30+
import org.junit.jupiter.params.ParameterizedTest;
31+
import org.junit.jupiter.params.provider.ValueSource;
2732

2833
import org.springframework.beans.PropertyValue;
2934
import org.springframework.beans.PropertyValues;
@@ -37,9 +42,15 @@
3742
import org.springframework.web.testfixture.servlet.MockMultipartHttpServletRequest;
3843

3944
import static org.assertj.core.api.Assertions.assertThat;
45+
import static org.springframework.web.bind.WebDataBinder.DEFAULT_FIELD_DEFAULT_PREFIX;
46+
import static org.springframework.web.bind.WebDataBinder.DEFAULT_FIELD_MARKER_PREFIX;
4047

4148
/**
49+
* Tests for {@link WebRequestDataBinder}.
50+
*
4251
* @author Juergen Hoeller
52+
* @author Brian Clozel
53+
* @author Sam Brannen
4354
*/
4455
class WebRequestDataBinderTests {
4556

@@ -79,10 +90,13 @@ void bindingWithNestedObjectCreationThroughAutoGrow() {
7990
assertThat(tb.getSpouse().getName()).isEqualTo("test");
8091
}
8192

82-
@Test
83-
void fieldPrefixCausesFieldReset() {
93+
@ParameterizedTest
94+
@ValueSource(booleans = { true, false })
95+
void markerPrefixCausesFieldReset(boolean ignoreUnknownFields) {
8496
TestBean target = new TestBean();
97+
8598
WebRequestDataBinder binder = new WebRequestDataBinder(target);
99+
binder.setIgnoreUnknownFields(ignoreUnknownFields);
86100

87101
MockHttpServletRequest request = new MockHttpServletRequest();
88102
request.addParameter("_postProcessed", "visible");
@@ -96,23 +110,30 @@ void fieldPrefixCausesFieldReset() {
96110
}
97111

98112
@Test
99-
void fieldPrefixCausesFieldResetWithIgnoreUnknownFields() {
113+
void fieldWithArrayIndices() {
100114
TestBean target = new TestBean();
101115
WebRequestDataBinder binder = new WebRequestDataBinder(target);
102-
binder.setIgnoreUnknownFields(false);
103116

104117
MockHttpServletRequest request = new MockHttpServletRequest();
105-
request.addParameter("_postProcessed", "visible");
106-
request.addParameter("postProcessed", "on");
118+
request.addParameter("stringArray[0]", "ONE");
119+
request.addParameter("stringArray[1]", "TWO");
107120
binder.bind(new ServletWebRequest(request));
108-
assertThat(target.isPostProcessed()).isTrue();
121+
assertThat(target.getStringArray()).containsExactly("ONE", "TWO");
122+
}
109123

110-
request.removeParameter("postProcessed");
124+
@Test
125+
void fieldWithMissingArrayIndex() {
126+
TestBean target = new TestBean();
127+
WebRequestDataBinder binder = new WebRequestDataBinder(target);
128+
129+
MockHttpServletRequest request = new MockHttpServletRequest();
130+
request.addParameter("stringArray", "ONE");
131+
request.addParameter("stringArray", "TWO");
111132
binder.bind(new ServletWebRequest(request));
112-
assertThat(target.isPostProcessed()).isFalse();
133+
assertThat(target.getStringArray()).containsExactly("ONE", "TWO");
113134
}
114135

115-
@Test // gh-25836
136+
@Test // gh-25836
116137
void fieldWithEmptyArrayIndex() {
117138
TestBean target = new TestBean();
118139
WebRequestDataBinder binder = new WebRequestDataBinder(target);
@@ -140,8 +161,7 @@ void fieldDefault() {
140161
assertThat(target.isPostProcessed()).isFalse();
141162
}
142163

143-
// SPR-13502
144-
@Test
164+
@Test // SPR-13502
145165
void collectionFieldsDefault() {
146166
TestBean target = new TestBean();
147167
target.setSomeSet(null);
@@ -332,7 +352,7 @@ void doTestTony(PropertyValues pvs) {
332352
for (PropertyValue pv : pvArray) {
333353
Object val = m.get(pv.getName());
334354
assertThat(val).as("Can't have unexpected value").isNotNull();
335-
assertThat(val).as("Val i string").isInstanceOf(String.class);
355+
assertThat(val).as("Val is string").isInstanceOf(String.class);
336356
assertThat(val).as("val matches expected").isEqualTo(pv.getValue());
337357
m.remove(pv.getName());
338358
}
@@ -359,21 +379,80 @@ void multipleValuesForParameter() {
359379
assertThat(Arrays.asList(original)).as("Correct values").isEqualTo(Arrays.asList(values));
360380
}
361381

362-
@Test
363-
void defaultArgumentShouldNotTriggerAutoGrowWhenDisallowed() {
364-
TestBean tb = new TestBean();
365-
tb.setSomeMap(null);
366382

367-
WebRequestDataBinder binder = new WebRequestDataBinder(tb, "person");
368-
binder.setAllowedFields("name");
383+
@ParameterizedClass // gh-36625
384+
@ValueSource(strings = { DEFAULT_FIELD_DEFAULT_PREFIX, DEFAULT_FIELD_MARKER_PREFIX })
385+
@Nested
386+
class DefaultAndMarkerPrefixTests {
369387

370-
MockHttpServletRequest request = new MockHttpServletRequest();
371-
request.addParameter("name", "spring");
372-
request.addParameter("!someMap[key1]", "test");
373-
binder.bind(new ServletWebRequest(request));
388+
@Parameter
389+
String prefix;
390+
391+
@Test
392+
void shouldNotTriggerBindingWhenFieldIsNotAllowed() {
393+
TestBean tb = new TestBean();
394+
395+
WebRequestDataBinder binder = new WebRequestDataBinder(tb, "person");
396+
binder.setAllowedFields("name");
397+
398+
MockHttpServletRequest request = new MockHttpServletRequest();
399+
request.addParameter("name", "spring");
400+
request.addParameter(prefix + "country", "test");
401+
binder.bind(new ServletWebRequest(request));
402+
403+
assertThat(tb.getName()).isEqualTo("spring");
404+
assertThat(tb.getCountry()).isNull();
405+
}
406+
407+
@Test
408+
void shouldNotTriggerBindingWhenFieldIsDisallowed() {
409+
TestBean tb = new TestBean();
410+
411+
WebRequestDataBinder binder = new WebRequestDataBinder(tb, "person");
412+
binder.setDisallowedFields("country");
413+
414+
MockHttpServletRequest request = new MockHttpServletRequest();
415+
request.addParameter("name", "spring");
416+
request.addParameter(prefix + "country", "test");
417+
binder.bind(new ServletWebRequest(request));
418+
419+
assertThat(tb.getName()).isEqualTo("spring");
420+
assertThat(tb.getCountry()).isNull();
421+
}
374422

375-
assertThat(tb.getName()).isEqualTo("spring");
376-
assertThat(tb.getSomeMap()).isNull();
423+
@Test
424+
void shouldNotTriggerAutoGrowWhenFieldIsNotAllowed() {
425+
TestBean tb = new TestBean();
426+
tb.setSomeMap(null);
427+
428+
WebRequestDataBinder binder = new WebRequestDataBinder(tb, "person");
429+
binder.setAllowedFields("name");
430+
431+
MockHttpServletRequest request = new MockHttpServletRequest();
432+
request.addParameter("name", "spring");
433+
request.addParameter(prefix + "someMap[key1]", "test");
434+
binder.bind(new ServletWebRequest(request));
435+
436+
assertThat(tb.getName()).isEqualTo("spring");
437+
assertThat(tb.getSomeMap()).isNull();
438+
}
439+
440+
@Test
441+
void shouldNotTriggerAutoGrowWhenFieldIsDisallowed() {
442+
TestBean tb = new TestBean();
443+
tb.setSomeMap(null);
444+
445+
WebRequestDataBinder binder = new WebRequestDataBinder(tb, "person");
446+
binder.setDisallowedFields("someMap[key1]");
447+
448+
MockHttpServletRequest request = new MockHttpServletRequest();
449+
request.addParameter("name", "spring");
450+
request.addParameter(prefix + "someMap[key1]", "test");
451+
binder.bind(new ServletWebRequest(request));
452+
453+
assertThat(tb.getName()).isEqualTo("spring");
454+
assertThat(tb.getSomeMap()).isNull();
455+
}
377456
}
378457

379458

@@ -404,5 +483,4 @@ public TestBean getConcreteSpouse() {
404483
}
405484
}
406485

407-
408486
}

0 commit comments

Comments
 (0)