Constructor-provided field values get recorded for failed binding result

Also, TypeMismatchExceptions get registered via BindingErrorProcessor.

Issue: SPR-16449
This commit is contained in:
Juergen Hoeller
2018-02-02 11:33:56 +01:00
parent 9c069f6cb1
commit 4a1cc9ced7
8 changed files with 184 additions and 78 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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.
@@ -19,6 +19,7 @@ package org.springframework.validation;
import java.beans.PropertyEditor;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
@@ -51,6 +52,10 @@ public abstract class AbstractBindingResult extends AbstractErrors implements Bi
private final List<ObjectError> errors = new LinkedList<>();
private final Map<String, Class<?>> fieldTypes = new HashMap<>(0);
private final Map<String, Object> fieldValues = new HashMap<>(0);
private final Set<String> suppressedFields = new HashSet<>();
@@ -63,6 +68,7 @@ public abstract class AbstractBindingResult extends AbstractErrors implements Bi
this.objectName = objectName;
}
/**
* Set the strategy to use for resolving errors into message codes.
* Default is DefaultMessageCodesResolver.
@@ -90,7 +96,6 @@ public abstract class AbstractBindingResult extends AbstractErrors implements Bi
return this.objectName;
}
@Override
public void reject(String errorCode, @Nullable Object[] errorArgs, @Nullable String defaultMessage) {
addError(new ObjectError(getObjectName(), resolveMessageCodes(errorCode), errorArgs, defaultMessage));
@@ -115,11 +120,6 @@ public abstract class AbstractBindingResult extends AbstractErrors implements Bi
addError(fe);
}
@Override
public void addError(ObjectError error) {
this.errors.add(error);
}
@Override
public void addAllErrors(Errors errors) {
if (!errors.getObjectName().equals(getObjectName())) {
@@ -128,19 +128,6 @@ public abstract class AbstractBindingResult extends AbstractErrors implements Bi
this.errors.addAll(errors.getAllErrors());
}
@Override
public String[] resolveMessageCodes(String errorCode) {
return getMessageCodesResolver().resolveMessageCodes(errorCode, getObjectName());
}
@Override
public String[] resolveMessageCodes(String errorCode, @Nullable String field) {
Class<?> fieldType = getFieldType(field);
return getMessageCodesResolver().resolveMessageCodes(
errorCode, getObjectName(), fixedField(field), fieldType);
}
@Override
public boolean hasErrors() {
return !this.errors.isEmpty();
@@ -231,14 +218,19 @@ public abstract class AbstractBindingResult extends AbstractErrors implements Bi
@Nullable
public Object getFieldValue(String field) {
FieldError fieldError = getFieldError(field);
// Use rejected value in case of error, current bean property value else.
Object value = (fieldError != null ? fieldError.getRejectedValue() :
getActualFieldValue(fixedField(field)));
// Apply formatting, but not on binding failures like type mismatches.
if (fieldError == null || !fieldError.isBindingFailure()) {
value = formatFieldValue(field, value);
// Use rejected value in case of error, current field value otherwise.
if (fieldError != null) {
Object value = fieldError.getRejectedValue();
// Do not apply formatting on binding failures like type mismatches.
return (fieldError.isBindingFailure() ? value : formatFieldValue(field, value));
}
else if (getTarget() != null) {
Object value = getActualFieldValue(fixedField(field));
return formatFieldValue(field, value);
}
else {
return this.fieldValues.get(field);
}
return value;
}
/**
@@ -250,11 +242,13 @@ public abstract class AbstractBindingResult extends AbstractErrors implements Bi
@Override
@Nullable
public Class<?> getFieldType(@Nullable String field) {
Object value = getActualFieldValue(fixedField(field));
if (value != null) {
return value.getClass();
if (getTarget() != null) {
Object value = getActualFieldValue(fixedField(field));
if (value != null) {
return value.getClass();
}
}
return null;
return this.fieldTypes.get(field);
}
@@ -287,7 +281,7 @@ public abstract class AbstractBindingResult extends AbstractErrors implements Bi
@Override
@Nullable
public Object getRawFieldValue(String field) {
return getActualFieldValue(fixedField(field));
return (getTarget() != null ? getActualFieldValue(fixedField(field)) : null);
}
/**
@@ -320,6 +314,29 @@ public abstract class AbstractBindingResult extends AbstractErrors implements Bi
return null;
}
@Override
public String[] resolveMessageCodes(String errorCode) {
return getMessageCodesResolver().resolveMessageCodes(errorCode, getObjectName());
}
@Override
public String[] resolveMessageCodes(String errorCode, @Nullable String field) {
Class<?> fieldType = (getTarget() != null ? getFieldType(field) : null);
return getMessageCodesResolver().resolveMessageCodes(
errorCode, getObjectName(), fixedField(field), fieldType);
}
@Override
public void addError(ObjectError error) {
this.errors.add(error);
}
@Override
public void recordFieldValue(String field, Class<?> type, Object value) {
this.fieldTypes.put(field, type);
this.fieldValues.put(field, value);
}
/**
* Mark the specified disallowed field as suppressed.
* <p>The data binder invokes this for each field value that was
@@ -333,7 +350,7 @@ public abstract class AbstractBindingResult extends AbstractErrors implements Bi
/**
* Return the list of fields that were suppressed during the bind process.
* <p>Can be used to determine whether any field values were targetting
* <p>Can be used to determine whether any field values were targeting
* disallowed fields.
* @see DataBinder#setAllowedFields
*/

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -71,7 +71,7 @@ public abstract class AbstractPropertyBindingResult extends AbstractBindingResul
*/
@Override
public PropertyEditorRegistry getPropertyEditorRegistry() {
return getPropertyAccessor();
return (getTarget() != null ? getPropertyAccessor() : null);
}
/**
@@ -90,7 +90,8 @@ public abstract class AbstractPropertyBindingResult extends AbstractBindingResul
@Override
@Nullable
public Class<?> getFieldType(@Nullable String field) {
return getPropertyAccessor().getPropertyType(fixedField(field));
return (getTarget() != null ? getPropertyAccessor().getPropertyType(fixedField(field)) :
super.getFieldType(field));
}
/**
@@ -161,7 +162,7 @@ public abstract class AbstractPropertyBindingResult extends AbstractBindingResul
PropertyEditor editor = super.findEditor(field, valueTypeForLookup);
if (editor == null && this.conversionService != null) {
TypeDescriptor td = null;
if (field != null) {
if (field != null && getTarget() != null) {
TypeDescriptor ptd = getPropertyAccessor().getPropertyTypeDescriptor(fixedField(field));
if (ptd != null && (valueType == null || valueType.isAssignableFrom(ptd.getType()))) {
td = ptd;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -259,11 +259,6 @@ public class BindException extends Exception implements BindingResult {
return this.bindingResult.getPropertyEditorRegistry();
}
@Override
public void addError(ObjectError error) {
this.bindingResult.addError(error);
}
@Override
public String[] resolveMessageCodes(String errorCode) {
return this.bindingResult.resolveMessageCodes(errorCode);
@@ -274,6 +269,16 @@ public class BindException extends Exception implements BindingResult {
return this.bindingResult.resolveMessageCodes(errorCode, field);
}
@Override
public void addError(ObjectError error) {
this.bindingResult.addError(error);
}
@Override
public void recordFieldValue(String field, Class<?> type, Object value) {
this.bindingResult.recordFieldValue(field, type, value);
}
@Override
public void recordSuppressedField(String field) {
this.bindingResult.recordSuppressedField(field);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -82,8 +82,7 @@ public interface BindingResult extends Errors {
* Extract the raw field value for the given field.
* Typically used for comparison purposes.
* @param field the field to check
* @return the current value of the field in its raw form,
* or {@code null} if not known
* @return the current value of the field in its raw form, or {@code null} if not known
*/
@Nullable
Object getRawFieldValue(String field);
@@ -107,15 +106,6 @@ public interface BindingResult extends Errors {
@Nullable
PropertyEditorRegistry getPropertyEditorRegistry();
/**
* Add a custom {@link ObjectError} or {@link FieldError} to the errors list.
* <p>Intended to be used by cooperating strategies such as {@link BindingErrorProcessor}.
* @see ObjectError
* @see FieldError
* @see BindingErrorProcessor
*/
void addError(ObjectError error);
/**
* Resolve the given error code into message codes.
* <p>Calls the configured {@link MessageCodesResolver} with appropriate parameters.
@@ -133,13 +123,37 @@ public interface BindingResult extends Errors {
*/
String[] resolveMessageCodes(String errorCode, String field);
/**
* Add a custom {@link ObjectError} or {@link FieldError} to the errors list.
* <p>Intended to be used by cooperating strategies such as {@link BindingErrorProcessor}.
* @see ObjectError
* @see FieldError
* @see BindingErrorProcessor
*/
void addError(ObjectError error);
/**
* Record the given value for the specified field.
* <p>To be used when a target object cannot be constructed, making
* the original field values available through {@link #getFieldValue}.
* In case of a registered error, the rejected value will be exposed
* for each affected field.
* @param field the field to record the value for
* @param type the type of the field
* @param value the original value
* @since 5.0.4
*/
default void recordFieldValue(String field, Class<?> type, Object value) {
}
/**
* Mark the specified disallowed field as suppressed.
* <p>The data binder invokes this for each field value that was
* detected to target a disallowed field.
* @see DataBinder#setAllowedFields
*/
void recordSuppressedField(String field);
default void recordSuppressedField(String field) {
}
/**
* Return the list of fields that were suppressed during the bind process.
@@ -147,6 +161,8 @@ public interface BindingResult extends Errors {
* disallowed fields.
* @see DataBinder#setAllowedFields
*/
String[] getSuppressedFields();
default String[] getSuppressedFields() {
return new String[0];
}
}