I have the following method in my controller:
#PostMapping("/register")
public String registerNewUser(#Valid User user, BindingResult result, Model model, RedirectAttributes redirectAttributes) {
System.out.println(result);
System.out.println(user);
if(result.hasErrors()) {
System.out.println("***ERROR***");
System.out.println(result.getAllErrors());
return result.getAllErrors().toString();
} else {
//userRepository.save(user);
System.out.println("user saved!");
return "user saved!";
}
}
And my user entity specifies:
#NonNull
#Column(nullable = false, unique = true)
#Valid
public String alias;
Now if I make a simple post request (I use the Advanced REST client for chrome extension) I get:
org.springframework.validation.BeanPropertyBindingResult: 0 errors
User(id=null, email=null, password=null, enabled=false, firstName=null, lastName=null, fullName=null null, alias=null, roles=[], links=[])
user saved!
Where it seems to validate despite #NonNull alias being null.
If I change #NonNull to #NotEmpty
Then validation works as expected:
[Field error in object 'user' on field 'alias': rejected value [null]; codes [NotEmpty.user.alias,NotEmpty.alias,NotEmpty.java.lang.String,NotEmpty]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.alias,alias]; arguments []; default message [alias]]; default message [must not be empty]]
BUT what I don't understand is why #NonNull allows Nulls?
There's no #NonNull annotation in the JSR-303 validation API. The annotation is called #NotNull. Make sure you actually use the javax.validation.constraints.NotNull annotation to mark the field.
You should use NotNull from javax.validation package and not from lombok (those are to be deleted, when java starts supporting validation - see here). It validates the beans. More info here. You can also use hibernate's #NotNull from org.hibernate.validator. This also does validation.
javax.validation.constraints
#NotNull: The annotated element must not be null.Accepts any type
#NotEmpty: The annotated element must not be null nor empty. Supported types are:
CharSequence (length of character sequence is evaluated)
Collection (collection size is evaluated)
Map (map size is evaluated)
Array (array length is evaluated)
#NotBlank:The annotated element must not be null and must contain at least one non-whitespace character. Accepts CharSequence
#NonNull refer to Lombok
Here are the Great Details which you may like Click Here
Related
I want to implement some simple endpoint in spring, trying to be as much Restful as possible and reduce the number of URL to use. Here are the GET url I want to call: (this is a simplified version)
GET /users
GET /users?id=123
GET /users?username=xyz
I used this controller:
#GetMapping()
public #ResponseBody
OutputUserDTO getUserByParameter(#RequestParam(required = false) String id,
#RequestParam(required = false) String username) {
if (id != null && !id.isEmpty()) {
return userService.getUserById(id);
}
if (username != null && !username.isEmpty()) {
return userService.getUserByUsername(username);
}
throw new MissingParameterException("...some message...");
}
#GetMapping()
public #ResponseBody
List<OutputUserDTO> getUsers() {
return userService.getUsers();
}
Of course I get an error, that is Ambiguous mapping.
I thought to always return a List so that I can merge the 2 endpoints and, in case you pass some parameters, return a Singleton... even though I don't know if it's a correct practice.
Or else, create one endpoint for each parameter, GET /users/{userId}, GET /users/{username}, ... but I don't like it neither (If I have 10 different way to get a user then I'll have to implement 10 endpoints :S)
What are some good practices in this case??
Thanks.
Replace MissingParameterException with return userService.getUsers();, and get rid of the other method, you know, the one with exactly the same mapping as the first method.
To make that work, you'd have to change return type to Object, which is not going to be a problem, since it's the actual object returned that controls the effect of #ResponseBody, not the declared type.
#GetMapping()
#ResponseBody
public Object getUserByParameter(#RequestParam(required = false) String id,
#RequestParam(required = false) String username) {
if (id != null && ! id.isEmpty()) {
return userService.getUserById(id);
}
if (username != null && ! username.isEmpty()) {
return userService.getUserByUsername(username);
}
return userService.getUsers();
}
FYI: #ResponseBody is a method-level annotation, so it should be listed before any keyword modifiers.
The Java Language Specification, section 8.3.1. Field Modifiers, says:
FieldModifier:
(one of)
Annotation public protected private
static final transient volatile
[...]
If two or more (distinct) field modifiers appear in a field declaration, it is customary, though not required, that they appear in the order consistent with that shown above in the production for FieldModifier.
It should be like #GetMapping("/users") on respective method
http://www.appsdeveloperblog.com/pathvariable-spring-mvc/
I suppose that the reason for that is, in getUserByParameter, both parameters are optional but if both the parameters are not passed it will conflict with your second getMapping.
more over, what is returned changes in the three scenarios. scenario 1 returns a list of DTOs while scenarios 2 & 3 return a single DTO
i dont think you can handle all three scenarios using your request path /users unless you want to wrap even a single DTO in a list, in which case you can simply merge your two methods. call getUsers() when both parameters are missing, in other cases, do what you currently do but wrap the response in a list.
if you want to keep them separate and return DTO or List, you should probably separate them out into /users and /user by specifying #GetMapping("/user") on method one and #GetMapping("/users") on method two
hope this helps
I have following POJO:
import java.util.Optional;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import org.immutables.value.Value;
#Value.Immutable
public interface ABC {
Optional<#NotBlank String> test();
Optional<#Size(max = 280) String> testSize();
}
I am using javax validation to validate objects of class ABC like following:
public static Set<TestConstraintViolation> validateInternalTest(final Object type, final Class<?>... groups) {
Set<TestConstraintViolation> violations = new HashSet<>();
for (Method method : type.getClass().getInterfaces()[0].getDeclaredMethods()) {
try {
VALIDATOR.validateReturnValue(
type,
method,
method.invoke(type),
groups).forEach(constraint -> {
TestConstraintViolation testConstraintViolation = new TestConstraintViolation(
method.getName(),
constraint.getMessageTemplate()
);
violations.add(testConstraintViolation);
});
} catch (IllegalAccessException | InvocationTargetException e) {
throw new IllegalStateException("", e);
}
}
return violations;
}
Now, when I try to validate with this validator function objects of ABC, I am seeing a weird issue:
#Test
public void test() {
ABC abc = ABCType.builder().build();
assertThat(validateInternalTest(abc))
.hasViolation("test", "{javax.validation.constraints.NotBlank.message}");
ABC abc2 = ABCType.builder().test("test").build();
assertThat(validateInternalTest(abc2))
.hasNoViolations();
}
With abc object, it returns violations if test is not passed even if it is optional while not passing testSize works fine.
According to me, with Optional, both of them should work. Isn't it?
Is it some issue with Immutables or javax validation? Please help.
What is the Optional type?
Optional is a new container type that wraps a single value, if the value is available. So it's meant to
convey the meaning that the value might be absent. Take for example
this method:
If you are using Optional which means value might be absent and using #NotBlank in conjunction with Optional doesn't seem to be a wise idea to me.
The JSR 380 spec (Bean Validation 2.0) treats empty optionals (Optional.empty()) as null.
Most constraints (including #Size) do not consider null to be a constraint violation which is why the empty optional (treated as null) passes validation. It is also the reason that one often comes across #NotNull #Size(min = 1, max = 2) String x;(i.e. check for both size and notnull).
The #NotBlank constraint however is one of the exceptions that does not allow nulls (considers them to be violations). That is why #NotBlank fails where most others would not.
A similar question was raised as a Hibernate Validator issue here. Please see the first response in the issue (by Guillaume Smet):
This is a choice we made when writing the Bean Validation 2.0 specification. We consider the value of an empty Optional as null.
Most of the constraints are not concerned as they don't fail for null but indeed for #NotBlank, we consider null as an invalid value.
I'm using OpenAPI 2.0 contract for my Rest service, written in Yaml, and I'm using swagger-codegen to generate the POJOs. I activated the useBeanValidation and performBeanValidation, so that the generated code (in JAVA) include the validation annotations...
In my contract I have a field of type boolean that I marked as required:
cancellationInd:
description: "indicates if this is a cancellation"
type: boolean
required: true
and the generated code is as follow:
/**
* indicates if this is a cancellation.
* #return cancellationInd
**/
#NotNull
#ApiModelProperty(required = true, value = "indicates if this is a cancellation")
public Boolean isCancellationInd() {
return cancellationInd;
}
public void setCancellationInd(Boolean cancellationInd) {
this.cancellationInd= cancellationInd;
}
The problem is that in my JsonREquest to the serviec, when I used
cancellationInd: null, it passes.... I was expecting some HttpStatus_400
because of the required field. Same thing when field is emtpy instead of null...
{
...
"timstamp": "2019-04-29T13:23:56.123-04:00",
"cancellationInd": "false",
...
}
It looks like the reason is that when the attribute is of type boolean, the generated one in java is Boolean (which is a wrapper, not primitive, and thus accepts a null)... am I right? Is this is a bug?
Any suggestions please?
I built a form validation system using Spring mvc.
This is my object User
public class User{
#NotEmpty
#NotBlank
#Size(min=3,max=20)
private String name;
.....
}
So far, if you don't fill the "name" field you get 3 messages:
may not be empty
may not be blank
size must be between 3 and 20
Is there a way, for example, to get the message 2 just if the message 1 is not sent?
I explain better: if a user doesn't fill a field I just want to print the message "may not be empty". Then, if the user fills a field with just spaces I want to print the message "may not be blank".
Then, if the field is filled (then is not empty) and doesn't contain just spaces (then is not blank) I want to print the size message.
Is there a way to handle this issue with annotation or I have to create my own validator class?
I looked into it on the internet but I didn't find a solution, probably because it's difficult to explain Google what to look for.
You can use group and #GroupSequence to achive order with your validator:
http://docs.jboss.org/hibernate/validator/4.1/reference/en-US/html/validator-usingvalidator.html#validator-usingvalidator-validationgroups
You can create your own annotation and use the meta constraint #ReportAsSingleViolation.
have a look at these websites
http://docs.jboss.org/hibernate/validator/4.0.1/reference/en/html/validator-customconstraints.html
http://docs.jboss.org/hibernate/validator/4.0.1/reference/en-US/html/validator-customconstraints.html
So your code could look
#ReportAsSingleViolation
#NotEmpty
#NotBlank
#Length(min = 3, max = 20)
public #interface CustomAnnotation {
public abstract String message() default "{customkey}";
public abstract Class<?>[] groups() default {};
public abstract Class<?>[] payload() default {};
}
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.