Include field name inside error message using Hibernate Validator - java

I'm using Hibernate Validator 4.2.0.Final and I'm looking for the simplest way to include class field name in my error message.
What I found is the following thread Using a custom ResourceBundle with Hibernate Validator. According to this I should create my custom annotation for each constraint annotation adding one property to each one.
Is there a cleaner way to achieve this?
The following code:
#Size(max = 5)
private String myField;
produces default error: size must be between 0 and 5.
I would like it to be: myField size must be between 0 and 5.

You can get the name of the field with the getPropertyPath() method from the ConstraintViolation class.
A good default error message can be:
violation.getPropertyPath() + " " + violation.getMessage();
Which will give you "foo may not be null", or "foo.bar may not be null" in the case of nested objects.

If your messages are in .properties file then there is no interpolation variable for accessing property name but one way you can achieve that is
//in ValidationMessages.properties
app.validation.size.msg=size must be between {min} and {max}
#Size(min=10, max=15, message = "myField {app.validation.size.msg})
private String myField;
OR
//in ValidationMessages.properties
app.validation.size.msg=size must be between {min} and {max} but provided ${validatedValue}
#Size(min=10, max=15, message = "myField {app.validation.size.msg})
private String myField;
Reference: message interpolation

I put every field validation message into the properties file, like this:
field.student.name.size.message = Student name size is not valid.
and in the bean, use it like this:
#Size(max = 5, message = "${field.student.name.size.message}")
private String myField;
I know it isn't a perfect solution, but I also don't find a better way.

I am not aware of any generic way but you can define custom error message and include field name in it.
#Size(max = 5, message = "myField size must be between 0 and 5")
private String myField;

A better way so that internationalization is supported.
//in ValidationMessages.properties
app.FieldName=MyField
app.validation.size.msg=size must be between {min} and {max} but provided ${validatedValue}
#Size(min=10, max=15, message = "{app.FieldName}"+" "+ "{app.validation.size.msg}")
private String myField;

use oval this has good number of annotations and possible ways to display messages.

For all those who are looking for a way to access class inside your validator. Putting hibernate annotating on a class level instead of variable level gives you access to a class object (assuming that you have defined a custom validator).
public class myCustomValidator implements ContraintValidator <MyAnnotation, MyAnnotatedClass> {
public void initialize (...){ ... };
public boolean isValid (MyAnnotatedClass myAnnotatedClass) {
// access to elements of your myAnnotatedClass
}
}

use this method(ex is ConstraintViolationException instance):
Set<ConstraintViolation<?>> set = ex.getConstraintViolations();
List<ErrorField> errorFields = new ArrayList<>(set.size());
ErrorField field = null;
for (Iterator<ConstraintViolation<?>> iterator = set.iterator();iterator.hasNext(); ) {
ConstraintViolation<?> next = iterator.next();
System.out.println(((PathImpl)next.getPropertyPath())
.getLeafNode().getName() + " " +next.getMessage());
}

If validating a REST call in a controller and using a controller advice, you can combine field and default message from MethodArgumentNotValidException like this:
#ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
#Override
public ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException exception, HttpHeaders headers, HttpStatus status, WebRequest request) {
String errorMessage = exception
.getBindingResult()
.getFieldErrors()
.stream()
.map(fieldError -> fieldError.getField() + " " + fieldError.getDefaultMessage())
.collect(Collectors.joining(", "));
return new ResponseEntity<>(errorMessage, HttpStatus.BAD_REQUEST);
}

Related

Spring #PreAuthorize hasAuthority Exception Failed to convert from type [java.lang.String] to type [java.lang.Boolean] for value 'hasAuthority

So I created a class with two simple public strings
public final class Right {
private Right() {
super();
}
public static final String AUTH = "hasAuthority('admin') or hasAuthority('mod')";
}
When I used it together with the #PreAuthorize annotation at my controllers it works like a charm. I do not like that it is hardcoded. For this reason I've put the roles in the properties and I tried to use it as a component:
#Component("authRule")
public class AuthRule {
#Value("${role.administrator}")
private String roleAdmin;
#Value("${role.moderator}")
private String roleMod;
public String getRightAccess() {
return "hasAuthority('" + roleAdmin+ "')" + " or hasAuthority('" + roleMod+ "')";
}
}
When i use it in my PreAuthorize as :
#PreAuthorize("#authRule.getRightAccess()")
I am getting back an exception of Failed to convert from type [java.lang.String] to type [java.lang.Boolean] for value 'hasAuthority('admin') or hasAuthority('mod')
if I hardcoded in the PreAuthorize. I am quite confused with this. Anyone any ideas?
Thanks in advance for all the responses.
I've had a quick read of the documentation; https://docs.spring.io/spring-security/site/docs/current/reference/html5/#el-pre-post-annotations
It's likely that the value provided to the annotation is parsed only once. I think you need it to parse twice; once to trigger your custom component's method getRightAccess(). And a second time to parse the String result returned by that method. There are examples here if you do a general search for "Boolean"; https://docs.spring.io/spring/docs/4.3.10.RELEASE/spring-framework-reference/html/expressions.html
// evaluates to true
boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(
societyContext, Boolean.class);
So you probably need something like this in your getRightAccess() method;
return parser.parseExpression("hasAuthority('"+roleAdmin+"') or hasAuthority('"+roleMod+"')").getValue(Boolean.class);

Hibernate validation: give the ability to user to only enter a digits(number) not instead

I have a jsf form and I have an input which normally accept only number(integer) .
I want to custom the error message when the user enter a string or char in this field. I want the validation in the data layer thats mean with hibernate annotation.
I don't want use this default message if the user enter a string instead of integer, I want using my custom error message.
: '10S' must be a number between -2147483648 and 2147483647 Example:
9346
Please the attached image can explain well.
How could I achieve this please.
Thank you in advance.
You should implement your own javax.validation.MessageInterpolator
(from https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#section-validator-factory-message-interpolator)
Message interpolators are used by the validation engine to create user
readable error messages from constraint message descriptors.
In case the default message interpolation algorithm described in
Chapter 4, Interpolating constraint error messages is not sufficient
for your needs, you can pass in your own implementation of the
MessageInterpolator interface via Configuration#messageInterpolator()
as shown in the example below:
package org.hibernate.validator.referenceguide.chapter09;
public class MyMessageInterpolator implements MessageInterpolator {
#Override
public String interpolate(String messageTemplate, Context context) {
//...
return null;
}
#Override
public String interpolate(String messageTemplate, Context context, Locale locale) {
//...
return null;
}
}
You can configure your validator to use your custom interpolator like that:
ValidatorFactory validatorFactory = Validation.byDefaultProvider()
.configure()
.messageInterpolator( new MyMessageInterpolator() )
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
You can achieve this with the #Range annotation
#Range(min = -2147483648, max = 2147483647, message= ": '10S' must be a number between -2147483648 and 2147483647 Example: 9346")
long value;

Bean validation specification prioritizing

I am using bean validation specification to validate my form on spring-boot thymeleaf project.
My entity property is as follow.
#NotEmpty(message = "{Password should not be empty}")
#Pattern(regexp = //Pattern for range 1-20, message = "{Wrong Input}")
private String password;
When I run and inputed to password field of my form with empty value, both of Validation Error Messages were shown.
My expectation is, while empty value is inputed, only #NotEmpty annotation should work and on the other hand, only #Pattern should be shown upon user input is wrong.
How can I do with Bean Validation Specification for that?
Regards.
1. Validation groups
#NotEmpty(groups = First.class), message = ...,
#Pattern(groups = Second.class, regexp = ...)
private String password;
Create the validation groups:
//Validation Groups - Just empty interface used as Group identifier
public interface First {
}
public interface Second {
}
and validate the model this way:
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<Model>> violations = validator.validate(model, First.class);
if(violations.isEmpty()){
violations = validator.validate(model, Second.class);
}
2. Groups Sequences
I've never used them, but it seems it does just what you want
Check this other answer (https://stackoverflow.com/a/7779681/641627). I've added below a quote from this answer (from #Gunnar), which ironically also uses First and Second group names:
#GroupSequence({First.class, Second.class})
public interface Sequence {}
#Size(min = 2, max = 10, message = "Name length improper", groups = { First.class })
#Pattern(regexp = "T.*", message = "Name doesn't start with T" , groups = { Second.class })
private String name;
When now validating a Bean instance using the defined sequence
(validator.validate(bean, Sequence.class)), at first the #Size
constraint will be validated and only if that succeeds the #Pattern
constraint.
With this solution, you wouldn't need to manually call validator.validate(...), the validations would be performed in the order defined in the Sequence with short-circuit if one fails.

Interpolate validation-specific parameters in bean validation message

I have a custom bean validator which checks if a given field on an entity is unique for some conditions. If the validation fails, the message should include a field (e.g. the ID) of the already existing entity. So for example the message should be:
"Product 42 already has such a value defined, choose a unique value."
Is this possible using bean validation?
AFAICS, the message format may include parameters, such as:
"Length must be between {min} and {max}."
But this can only reference the "static" attributes of the validation annotation, in this case:
#Size(min=1, max=16)
private String name;
In my case, the value is only known within isValid of my custom validator.
You are right!, And for what you want!, you can build constraint violation message inside the isValid() method. For this the constraints Annotation should be specific for particular class on which it has been applied and it should be a class level validation constraints. Inside isValid before returning false on failure of validation you can create message consisting value of class instance. For example:
#check class Test{ int id; #validations...on fields}.
public boolean isValid(Test value, ConstraintValidatorContext context)
{
// your check logic
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("It should be different for(custom message) .."+ value.id).addConstraintViolation();
return false; // based on constraint filure.
}
But i think you want to do this with Field level annotations! I don't have idea about that looking forward to your results.
It's not really the nicest solution, but what we ended up doing was adding something like the following to our top-level exception handling code:
String getConstraintViolationMessages(ConstraintViolationException e) {
StringBuilder sb = new StringBuilder();
for (ConstraintViolation<?> violation : e.getConstraintViolations()) {
sb.append(getMessage(violation));
sb.append("\n");
}
sb.setLength(sb.length() - 1);
return sb.toString();
}
String getMessage(ConstraintViolation<?> violation) {
String key = violation.getMessageTemplate();
String messageFormat = localize(key);
Object entity = violation.getRootBean();
String identifier;
if (entity instanceof PrimaryKeyed) {
identifier = String.valueOf(((PrimaryKeyed) entity).getId());
} else {
identifier = entity.toString();
}
return MessageFormat.format(messageFormat, identifier);
}
Note that PrimaryKeyed is a custom interface that is used on our entities. We also have some other interfaces and custom handling not shown above.

Java annotations

I've created simple annotation in Java
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface Column {
String columnName();
}
and class
public class Table {
#Column(columnName = "id")
private int colId;
#Column(columnName = "name")
private String colName;
private int noAnnotationHere;
public Table(int colId, String colName, int noAnnotationHere) {
this.colId = colId;
this.colName = colName;
this.noAnnotationHere = noAnnotationHere;
}
}
I need to iterate over all fields, that are annotated with Column and get name and value of field and annotation. But I've got problem with getting value of each field, since all of them are of different data type.
Is there anything that would return collection of fields that have certain annotation?
I managed to do it with this code, but I don't think that reflection is good way to solve it.
Table table = new Table(1, "test", 2);
for (Field field : table.getClass().getDeclaredFields()) {
Column col;
// check if field has annotation
if ((col = field.getAnnotation(Column.class)) != null) {
String log = "colname: " + col.columnName() + "\n";
log += "field name: " + field.getName() + "\n\n";
// here i don't know how to get value of field, since all get methods
// are type specific
System.out.println(log);
}
}
Do I have to wrap every field in object, which would implement method like getValue(), or is there some better way around this? Basicly all I need is string representation of each field that is annotated.
edit: yep field.get(table) works, but only for public fields, is there any way how to do this even for private fields? Or do I have to make getter and somehow invoke it?
Every object should has toString() defined. (And you can override this for each class to get a more meaningful representation).
So you where your "// here I don't know" comment is, you could have:
Object value = field.get(table);
// gets the value of this field for the instance 'table'
log += "value: " + value + "\n";
// implicitly uses toString for you
// or will put 'null' if the object is null
Reflection is exactly the way to solve it. Finding out things about types and their members at execution time is pretty much the definition of reflection! The way you've done it looks fine to me.
To find the value of the field, use field.get(table)
Reflection is exactly the way to look at annotations. They are a form of "metadata" attached to the class or method, and Java annotations were designed to be examined that way.
Reflection is one way to process the object (probably the only way if the fields are private and don't have any kind of accessor method). You'll need to look at Field.setAccessible and perhaps Field.getType.
Another approach is to generate another class for enumerating the annotated fields using a compile-time annotation processor. This requires a com.sun API in Java 5, but support is better in the Java 6 JDK (IDEs like Eclipse may require special project configuration).

Categories

Resources