Let's say I have a class that has 3 members:
Class A {
private String string1; /** Cannot be null */
private String string2; /** Cannot be null */
private String string3; /** Can be null */
}
I have 2 method that accepts an object of this class as a parameter. One of the methods needs to check that the non nullable fields are present while in the other one, it doesn't matter:
public int func1(A object); /** Check non nullable fields */
public int func2(A object); /** No check required */
Is there any clean way to do it? Using #NonNull annotations or something?
I have tried various ways but none of them work. All the NonNull only help make sure that the setter doesn't get null as the parameter. Or that the object itself isn't null.
I can't seem to find something that does this kind of recursive null check on the object.
It'd be great if any of you could help. :)
You need a bean Validator, a class used to check that the bean is OK. In Spring there are a number of implementations. For example, see SmartValidatorand LocalValidatorFactoryBean
The #valid annotation is a nice way to call automagically the validator. As you are using Spring you can avoid the manual creation of the validator. It only works if the method is called by Spring (or any equivalent container). You may get the validation results in a BindingResult object. For example:
#RequestMapping(value = "/MyPath", method = RequestMethod.POST)
public String postCostForm(Model model, #Valid MyForm myForm, BindingResult result){
if(result.hasErrors()){
for(ObjectError error : result.getAllErrors()){
// Do something
}
return "/error";
}else{
return "/success";
}
}
Validation is very powerfull and sometimes complex. You may create groups of validation and check just one group of them. You can create your custom constraint tags, you can call validation methods and you may customize and internationalize the messages returned if the validation fails.
class A {
#NotNull
private String string1; /** Cannot be null */
#NotNull
private String string2; /** Cannot be null */
private String string3; /** Can be null */
}
And in the method signature have #Valid
public int function(#Valid A object)
Use #Required annotation in Spring's
private String string1;
#Required
public void setString1(String string1) {
this.string1= string1;
}
Related
Is there some way in Spring Boot that I can perform validation on properties that depend on each other's values, and have the error message be associated with the property?
I want to return the errors to the user in a nice JSON structure:
{
"errors": {
"name": "is required if flag is true"
}
}
Example:
#Entity
public class MyEntity {
private boolean nameRequiredFlag;
// Required if "nameRequiredFlag" is set to true:
private String name;
}
One solution that doesn't solve my problem of associating the error message with the name property is to create a validator annotation for the entity:
#ValidEntity
public class MyEntity {
private boolean nameRequiredFlag;
// Required if "nameRequiredFlag" is set to true:
private String name;
}
#Constraint( validatedBy = { MyEntityValidator.class } )
#Documented
#Target( { ElementType.TYPE } )
#Retention( RetentionPolicy.RUNTIME )
public #interface ValidEntity{
Class<?>[] groups () default {};
String message () default "name is required if 'nameRequiredFlag' is true";
Class<? extends Payload>[] payload () default {};
}
public class MyEntityValidator implements Validator<ValidEntity, MyEntity> {
#Override
public boolean isValid ( MyEntity entity, ConstraintValidatorContext context ) {
if ( !entity.nameRequiredFlag ) return true;
return !StringUtils.isBlank( entity.getName() );
}
}
This is laughably cumbersome and doesn't solve my problem. Isn't there any way I can do this with the framework validation?
Edit: This is for a JSON API, and the consumer really needs to be able to associate the error message to a best guess at which field has an issue. It is not helpful to send the consumer an error message for the whole object, or a computed property.
Solution given by #EvicKhaosKat is one way of doing it. However, when there are too many fields dependent on each other in a complicated way, your class becomes full of annotations and I personally struggle a lot relating them.
A simpler approach is to create a method(s) in your pojo which does the cross field validations and returns a boolean. On the top of this method annotate it with #AssertTrue(message = "your message"). It will solve your problem in a cleaner fashion.
public class SampleClass {
private String duration;
private String week;
private String month;
#AssertTrue(message = "Duration and time attributes are not properly populated")
public boolean isDurationCorrect() {
if (this.duration.equalsIgnoreCase("month")) {
if (Arrays.asList("jan", "feb", "mar").contains(month))
return true;
}
if (this.duration.equalsIgnoreCase("week")) {
if (Arrays.asList("1-7", "8-15", "16-24", "25-31").contains(week))
return true;
}
return false;
}
}
Note: I have not tested this code but have used this approach in multiple places and it works.
Possible reason is that name validation operates on not-yet-fully constructed object, so nameRequiredFlag is not filled yet.
As an option there is a #GroupSequence annotation, which allows to group and perform validations in an order you specify.
For example it is possible to add to MyEntity annotations:
#ValidEntity(groups = DependentValidations.class)
#GroupSequence({MyEntity.class, DependentValidations.class})
So all the other validation annotations on MyEntity class gonna be performed first, and after that DependentValidations group, which consists of ValidEntity.
Thus ValidEntity will be called on fully created object, and the last in order.
(DependentValidations.class - just an empty interface created somewhere nearby, like any other marker interface)
https://www.baeldung.com/javax-validation-groups will possibly describe that in much more details.
p.s. answer provided by #Innovationchef will possibly suit the case more :)
Suppose I have a class, what is the order of validation in a SpringBoot class object. After an object gets created then the fields are populated or does the validation happens before the objects are populated, at the time of setting of the field values this validation happens. Or after the object is created then by a get call we validate the object field values.
package com.bablo.google.request;
import java.io.Serializable;
import java.util.Set;
import javax.validation.constraints.NotNull;
public class SomeRequest implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#NotNull
private Long userId;
private String resNote; //this is not annotated with #NotNull
#NotNull
private String revTag;
public Long getUserId() {
return userId;
}
public void setUserId(final Long userId) {
this.userId = userId;
}
public String getResNote() {
return responseNote;
}
public void setResNote(final String resNote) {
this.resNote = resNote.trim(); //Call to trim() method of the String object.
}
public String getRevTag() {
return revTag;
}
public void setRevTag(final String revTag) {
this.revTag = revTag.trim(); //Call to the trim() method of the String object.
}
}
What is the way that validation will happen in a class? What is the mechanism of validating the fields, does the #NotNull validation or for that matter any validation depends on the getter methods to do the validation?
Do they first call the setter methods to do the validation?
Splitting your questions and adding answers.
What is the order of validation in a SpringBoot class object?
Validation happens as part of data binding process. Every request parameter/path variable will be validated as per the marked annotation and only when the validation passes, the value will be assigned to the class object.
What is the way that validation will happen in a class?
Validation process differs for each binding mechanism. If the method parameter is ModelAttribute/request parameter/path variable/Map etc. Spring uses different argument resolvers for each method parameter. If #Valid is added, then it enables validation during argument resolution process (Look out for RequestMappingHandlerAdapter where the whole magic is wired).
Does the #NotNull validation or for that matter any validation depends on the getter methods to do the validation? Do they first call the setter methods to do the validation?
Spring uses reflection to construct/validate the method argument class. Data binding and validation happens even without getters/setters.
You can validate manually by calling
#Autowired
private javax.validation.Validator validator;
...
validator.validate(new SomeRequest()); // you can also pass method's argument
or you can use auto validation
Here is an example https://www.baeldung.com/spring-boot-bean-validation of using #Valid + #ExceptionHandler
Here is an example https://spring.io/guides/gs/validating-form-input/ of using #Valid + BindingResult
I have a simple method to get a list of documents for a given companyId. Here is the method:
#Override
public List<Documents> getDocumentList(#NotNull Integer companyId) {
Company company = new Company(companyId);
return this.documentRepository.findByCompany(company);
}
I wanted to use Javax validation constraints to ensure that the companyId being passed in, is not null. But it seems to not have any effect, as I'm able to pass in a null value, and it flows down to the findByCompany call on the repository. I also added #Valid before #NotNull to force validation, but that too didn't do anything.
I could always write a couple of lines to check for a null value, but wanted to use javax.validation annotations to make the code more readable and concise. Is there a way to make the annotations work on method params?
To activate parameter validation, simply annotate the class with #Validated
import org.springframework.validation.annotation.Validated;
From The Java EE 6 Tutorial:
The Bean Validation model is supported by constraints in the form of
annotations placed on a field, method, or class of a JavaBeans
component, such as a managed bean.
You should place your validation of a field related to a declared bean, something like this:
#Entity
#Table(name="users")
public class BackgammonUser {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long userId;
#Column(name="username")
#NotBlank
private String userName;
#NotBlank
private String password;
#NotNull
private Boolean enabled;
}
The BackgammonUser is considered to be a bean.
If you #Inject a class with your method, its working as expected.
#Stateless
public class MyBean{
#Inject
TestClass test;
}
and
public class TestClass {
public List<Documents> getDocumentList(#NotNull Integer companyId)
{
//...
}
}
ConstraintViolationException when you call your method with null parameter:
WFLYEJB0034: EJB Invocation failed on component MyBean for method ...:
javax.ejb.EJBException: javax.validation.ConstraintViolationException:
1 constraint violation(s) occurred during method validation.
#NotNull Annotation,
A method should not return null.
A variable (like fields, local variables, and parameters) cannot hold null value.
I do validation with JSR-303 in my Spring app, it works as needed.
This is an example:
#Column(nullable = false, name = "name")
#JsonProperty("customer_name")
#NotEmpty
#Size(min = 3, max = 32)
private String name;
And REST API clients use customer_name as name of input field that send to API bud validation field error org.springframework.validation.FieldError returns name as name of the field.
Is there some way hot to get JSON-ish name that is specified in #JsonProperty? Or do I have to implement own mapper to map class fields name into its JSON alternative?
Edit1: Renaming class fields into names that correspond to JSON names is not alternative (for many reasons).
This can now be done by using PropertyNodeNameProvider.
There is no way to achieve this currently. We have an issue for this in the reference implementation: HV-823.
This would address the issue on the side of Hibernate Validator (i.e. return the name you expect from Path.Node#getName()), it'd require some more checking whether Spring actually picks up the name from there.
Maybe you'd be interested in helping out with implemeting this one?
For MethodArgumentNotValidException and BindException I have written a method that tries to access the private ConstraintViolation from Spring ViolationFieldError via reflection.
/**
* Try to get the #JsonProperty annotation value from the field. If not present then the
* fieldError.getField() is returned.
* #param fieldError {#link FieldError}
* #return fieldName
*/
private String getJsonFieldName(final FieldError fieldError) {
try {
final Field violation = fieldError.getClass().getDeclaredField("violation");
violation.setAccessible(true);
var constraintViolation = (ConstraintViolation) violation.get(fieldError);
final Field declaredField = constraintViolation.getRootBeanClass()
.getDeclaredField(fieldError.getField());
final JsonProperty annotation = declaredField.getAnnotation(JsonProperty.class);
//Check if JsonProperty annotation is present and if value is set
if (annotation != null && annotation.value() != null && !annotation.value().isEmpty()) {
return annotation.value();
} else {
return fieldError.getField();
}
} catch (Exception e) {
return fieldError.getField();
}
}
This code can be used in methods handling BindExceptions #ExceptionHandler(BindException.class) within a Class with #ControllerAdvice:
#ControllerAdvice
public class ControllerExceptionHandler {
#ExceptionHandler(BindException.class)
public ResponseEntity<YourErrorResultModel> handleBindException(final BindException exception) {
for (FieldError fieldError : exception.getBindingResult().getFieldErrors()) {
final String fieldName = getJsonFieldName(fieldError);
...
}
I worked out a concept to conditionally validate using JSR 303 groups. "Conditionally" means that I have some fields which are only relevant if another field has a specific value.
Example: There is an option to select whether to register as a person or as a company. When selecting company, the user has to fill a field containing the name of the company.
Now I thought I use groups for that:
class RegisterForm
{
public interface BasicCheck {}
public interface UserCheck {}
public interface CompanyCheck {}
#NotNull(groups = BasicCheck.class)
private Boolean isCompany
#NotNull(groups = UserCheck.class)
private String firstName;
#NotNull(groups = UserCheck.class)
private String lastName;
#NotNull(groups = CompanyCheck.class)
private String companyName;
// getters / setters ...
}
In my controller, I validate step by step depending on the respective selection:
#Autowired
SmartValidator validator;
public void onRequest(#ModelAttribute("registerForm") RegisterForm registerForm, BindingResult result)
{
validator.validate(registerForm, result, RegisterForm.BasicCheck.class);
if (result.hasErrors()
return;
// basic check successful => we can process fields which are covered by this check
if (registerForm.getIsCompany())
{
validator.validate(registerForm, result, RegisterForm.CompanyCheck.class)
}
else
{
validator.validate(registerForm, result, RegisterForm.UserCheck.class);
}
if (!result.hasErrors())
{
// process registration
}
}
I only want to validate what must be validated. If the user selects "company" fills a field with invalid content and then switches back to "user", the invalid company related content must be ignored by the validator. A solution would be to clear those fields using Javascript, but I also want my forms to work with javascript disabled. This is why I totally like the approach shown above.
But Spring breaks this idea due to data binding. Before validation starts, Spring binds the data to registerForm. It adds error to result if, for instance, types are incompatible (expected int-value, but user filled the form with letters). This is a problem as these errors are shown in the JSP-view by <form:errors /> tags
Now I found a way to prevent Spring from adding those errors to the binding result by implementing a custom BindingErrorProcessor. If a field contains null I know that there was a validation error. In my concept null is not allowed - every field gets annotated with #NotNull plus the respective validation group.
As I am new to Spring and JSR-303 I wonder, whether I am totally on the wrong path. The fact that I have to implement a couple of things on my own makes me uncertain. Is this a clean solution? Is there a better solution for the same problem, as I think this is a common problem?
EDIT
Please see my answer here if you are interested in my solution in detail: https://stackoverflow.com/a/30500985/395879
You are correct that Spring MVC is a bit picky in this regard,and it is a common problem. But there are work-arounds:
Make all your backing fields strings, and do number/date etc conversions and null checks manually.
Use JavaScript to set fields to null when they become irrelevant.
Use JavaScript to validate fields when they are entered. This will fix almost all of your problems.
Good luck!
I know this question is old, but I came upon it looking for an answer for a different situation.
I think for your situation you could use inheritance for the forms and then use two controller methods:
The forms would look like this:
public class RegistrationForm
{
// Common fields go here.
}
public class UserRegistrationForm
extends RegistrationForm
{
#NotNull
private String firstName;
#NotNull
private String lastName;
// getters / setters ...
}
public class CompanyRegistrationForm
extends RegistrationForm
{
#NotNull
private String companyName;
// getters / setters ...
}
The controller methods would look like this:
#RequestMapping(method = RequestMethod.POST, params = "isCompany=false")
public void onRequest(
#ModelAttribute("registerForm") #Valid UserRegistrationForm form,
BindingResult result)
{
if (!result.hasErrors())
{
// process registration
}
}
#RequestMapping(method = RequestMethod.POST, params = "isCompany=true")
public void onRequest(
#ModelAttribute("registerForm") #Valid CompanyRegistrationForm form,
BindingResult result)
{
if (!result.hasErrors())
{
// process registration
}
}
Notice that the #RequestMapping annotations include a params attribute so the value of the isCompany parameter determines which method is called.
Also notice that the #Valid annotation is place on the form parameter.
Finally, no groups are needed in this case.