public ProtobufValidator protoValidator() {
return ProtobufValidator.createBuilder()
.registerValidator(
MessageValidator.createBuilder(User.getDescriptor())
.addFieldConstraint("firstName", FieldConstraints.IS_SET)
.addFieldConstraint("firstName", FieldConstraints.length(1, 50))
.addFieldConstraint("lastName", FieldConstraints.IS_SET)
.addFieldConstraint("lastName", FieldConstraints.length(1, 50))
.addFieldConstraint("address", FieldConstraints.IS_SET)
.addFieldConstraint("address", FieldConstraints.VALID)
.addMessageConstraint(MessageConstraints.oneOfIsSet("phoneNumber"))
.build())
.registerValidator(
MessageValidator.createBuilder(Address.getDescriptor())
.addFieldConstraint("number", FieldConstraints.IS_SET)
.addFieldConstraint("number", FieldConstraints.POSITIVE)
.addFieldConstraint("name", FieldConstraints.IS_SET)
.addFieldConstraint("name", FieldConstraints.length(1, 100))
.addFieldConstraint("unit", FieldConstraints.POSITIVE)
.build())
.build();
}
There are Field level constraints that implement FieldConstraint
and Message level constraints that implement MessageConstraint
.
Used to declare a constraint that a field on a message must adhere to.
Used to declare a constraint that a message must adhere to.
field.violations.AssertFalse.message = must be false
field.violations.AssertTrue.message = must be true
field.violations.Future.message = must be a future date
field.violations.Max.message = must be less than or equal to {max}
field.violations.Min.message = must be greater than or equal to {min}
field.violations.Negative.message = must be less than 0
field.violations.NegativeOrZero.message = must be less than or equal to 0
field.violations.NotBlank.message = must not be blank
field.violations.NotEmpty.message = must not be empty
field.violations.IsSet.message = must be set
field.violations.IsNotSet.message = must not be set
field.violations.Past.message = must be a past date
field.violations.Pattern.message = must match '{regexp}'
field.violations.Positive.message = must be greater than 0
field.violations.PositiveOrZero.message = must be greater than or equal to 0
field.violations.Size.message = size must be between {min} and {max}
field.violations.Length.message = length must be between {min} and {max}
field.violations.HasKey.message = must have key '{key}'
message.violations.OneOfIsSet.message = one of {fields} must be set
Custom constraints can be created by implementing either FieldConstraint
or MessageConstraint
.
Some convenience abstract types can be extended which automatically performs the unwrapping of certain proto message types:
AbstractBooleanConstraint
(auto unwrapsBoolValue
or null if not set)AbstractNumberConstraint
(auto unwrapsDoubleValue
,FloatValue
,Int32Value
, andInt64Value
or null if not set)AbstractStringConstraint
(auto unwrapsStringValue
or null if not set)AbstractTimestampConstraint
(convertsTimestamp
toInstant
or null if not set)AbstractRepeatedConstraint
(converts repeated field toList
type)AbstractMapConstraint
(converts map field toMap
type)
Messages for custom constraints can be defined in src/resources/ProtobufValidationMessages.properties
. You may also override predefined constraint messages here.
Conditionally applying a constraint is supported via the ApplyCondition
interface.
.addFieldConstraint(
"buzzerCode",
FieldConstraints.IS_SET,
ApplyConditions.when(ApplyConditions.fieldIsTrue("hasBuzzer")))
constraint.condition.Expression.message = when {expression}
constraint.condition.FieldIsTrue.message = when '{field}' is true
constraint.condition.FieldIsNotTrue.message = when '{field}' is not true
constraint.condition.FieldIsEqualTo.message = when '{field}' is equal to {value}
Custom ApplyConditions can be created by implementing the ApplyCondition
interface.
Some convenience abstract types can be extended to provide additional support:
AbstractBooleanFieldCondition
(applies when a boolean field on the message has some value)AbstractFieldCondition
(applies when some field on the message has some value)
Constraint violations are determined by calling the following on ProtobufValidator
:
public List<MessageViolation> validate(Message message);
Where MessageViolation
looks like:
public class MessageViolation {
private String path;
private ErrorMessage error;
private Optional<ConditionMessage> condition;
private String defaultMessage;
@Nullable private Object value;
//Constructor and Getters ...
public class ErrorMessage {
private String code;
private Map<String, Object> params;
//Constructor and Getters ...
}
public class ConditionMessage {
private String code;
private Map<String, Object> params;
//Constructor and Getters ...
}
}