Validating with Spring that a Boolean element comes explicitly defined - java

We got this REST endpoint in which one of the field is mapped to a Boolean (The wrapper class) instance. We are using a Boolean instead of a boolean because design decision, so this is non-negotiable.
This Boolean value is mandatory and it must be specified by the sender ("whateverValue":"" should return a 400 error), but when arriving to the endpoint, the value is automatically converted to a correct false value.
So, the question is: Can this be done? Are we not understanding the contract of using a "Boolean" object instead of the primitive?
EDIT: Just to clarify, we are already validating "whateverValue":null, and the value can be either true or false, so, as far as I know, neither #NotNull or #AssertTrue/False can be used here.

If you want to validate the Object Type Boolean you should use #NotNull
Here is a question where this has been asked before.
I use #NotNull if a boolean is mendatory to be set ans #AssertTrue/false to verify the incoming value has a specific state.
Hope this helps

I coded your scenario as follows and it was ok!
Please correct me if my understanding (from your scenario) is incorrect.
Create DTO
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class TestDto {
#NotNull
Boolean testValue;
//getter and setter
}
Create Endpoint
#RestController
public class testController {
#RequestMapping(value = "/test", method = RequestMethod.POST)
public void accountTrades(#RequestBody #Valid TestDto testDto) {
System.out.println(testDto.getTestValue());
}
}
Using Postman, when I send testValue:"", it throws a notnull exception, that means the server has received null, and there is no null to false conversion. (It worked correctly!)
Server response is as follows

Related

Validating Input Data in Spring Boot

I am working on a spring boot project. I am building a PUT/POST API which takes boolean data type in the request body. The issue is whenever I pass an integer value like 12,13,-15... etc it converts it to true (which is the default behavior of boolean), but my doubt is, is their a way to validate here such that it takes only 0,1, true and false. I tried adding the regex to validation.properties and #ESAPI annotations to my model, but I've read that data canonicalization happens before validation occurs. so I am stuck with this issue, How do I resolve this?
This is a PUT Request.
Here is the Request body
{
"access": true,
"prefLanguage": "English",
"prefTimeZone": "USA"
}
My Model is defined as
public class Consent implements Serializable {
#ESAPIPattern(validateWithPattern = "acess", required = true)
private String acess;
#JsonInclude(JsonInclude.Include.NON_NULL)
#ESAPIPattern(allowNull = true,validateWithPattern = "prefTimeZone")
private String prefTimeZone;
#JsonInclude(JsonInclude.Include.NON_NULL)
#ESAPIPattern(allowNull = true,validateWithPattern = "prefLanguage")
private String prefLanguage;
... getters and setters
}
Here I want "access" to accept only true|false or 0|1, I don't want it to accept any other integer value.
You can use a context validator which is provided by spring. You can create an annotation #BoolenValidator and put it in the request param or path variable.
public boolean isValid(String value, ConstraintValidatorContext context) {
boolean isValid = true;
if (isNull(value) || !(value==0|| value==1)) {
isValid = addViolation(value, "must be 0 or 1");
}
return isValid;
}

How to post boolean variable to server side as a field of a ModelAttribute object?

I have a Spring-framework based web application, and I post data from client side to server side as an object using ModelAttribute annotation.
I have an Entity bean that contains data (and stored to database later). One of the fields is a boolean field:
#Entity
#Table(name="PLAYER")
public class Player implements Serializable, JSONAble{
...
#Column(name="IS_HUNGARIAN")
private boolean isHungarian
...
}
I send the data to server side with an AJAX call using jQuery:
$.ajax({
type : "POST",
url : contextPath + 'saveEditedPlayer',
data: $('#editPlayerForm').serialize(),
success : function(result) {
alert('Success');
},error:function (xhr, ajaxOptions, thrownError){
}
});
In the Controller I receive the data like this:
#RequestMapping("/saveEditedPlayer")
#ResponseBody
public String saveEditedPlayer(#ModelAttribute final Player player, #RequestParam(required = false) final String tsiAlso) {
final JSONObject result = new JSONObject();
//some more source code
return result;
}
All the fields in the Player object are filled fine except the boolean field. According to this link the reason is that boolean values posted to server side as String value, and that is true. I have a workaround solution: I have a String field in the Entity bean, where I can store the boolean data as a String value ("true" or "false"), and in the field's set method I can set the real boolean field's value (thanks to Spring it's done automatically). But I don't think this is the right way to solve this problem.
Is there a way to post my boolean variable without any kind of helper variable?
Thank you for any kind of help in advance, if you need some more information, please ask me.
You need to name your stuff like this:
Player.java:
...
private boolean hungarian;
public boolean isHungarian() {...}
public void setHungarian(boolean hungarian) {...}
...
Your request variable is then hungarian

Object value as null in Jackson Parsing

I am using Jackson library and trying to achieve that is mentioned here
BaseOperationRequest.java
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "command"
)
#JsonSubTypes({
#JsonSubTypes.Type(name = "ZADD", value = ZAddBaseOperationRequest.class)
})
public class BaseOperationRequest {
public short operationId;
public Command command;
public String gameId;
public String key;
}
ZAddBaseOperationRequest.java
public class ZAddBaseOperationRequest extends BaseOperationRequest{
public Map<String, Double> members;
}
Command.java
public enum Command{
ZADD,
HSET
}
The problem is here when I try to pass the Object from REST call which is something like this:
#RestController
public class MyController{
//keeping just for now as GET, will change it to POST and take it in RequesBody later on
#RequestMapping(value = "/process/{object}", method = RequestMethod.GET, produces = "application/json")
public #ResponseBody ResponseEntity process(#Pathvariable String object){
System.out.println(object);//I am getting this as correct--->(A)
BaseOperationRequest[] baseOperationRequestArray = new ObjectMapper().readValue(object, BaseOperationRequest[].class);//getting exception --->(B)
System.out.println(baseOperationRequestArray);
}
}
Now, I am calling this as follows:
1st scenario
CALLING WITHOUT MEMBERS MAP:
<server>:<port>/.../process/[{"operationId":1,"command":"ZADD","gameId":"t5","key":"abc"}]
The process method is getting called and since Jackson is told to create the Object of ZAddBaseOperationRequest when getting ZADD in command, it is doing it but the value of command itself is assigned as null in the resultant object.
Please explain why? Where did the value of command went?
2nd scenario
CALLING WITH MEMBERS MAP:
:/.../process/[{"members":{"a":1.0},"operationId":1,"command":"ZADD","gameId":"t5","key":"abc"}]
then in this case, the equation (A) is showing [{"members":{"a":1.0,b that's it, where did the other part of the GET went.
This is making me mad. :).
Thanks in advance..
Please help.
It is not a good practice to send json as path parameter.
To fix your problem add visible=true in JsonTypeInfo annotation. Your declaration will become:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "command",
visible = true
)
As per jackson documentation for visible:
Property that defines whether type identifier value will be passed as part of JSON stream to deserializer (true), or handled and removed by TypeDeserializer (false).
Default value is false, meaning that Jackson handles and removes the type identifier from JSON content that is passed to JsonDeserializer.

Spring MVC with hibernate Validator to validate single basic type

Below is the mapped method that I am having trouble with, no matter what value I pass to it the validation returns "passed validation."
#RequestMapping(value = "test", method = RequestMethod.POST)
#ResponseBody
public String getTest(#RequestBody #Valid #Max(32) long longValue, BindingResult result) {
if (result.hasErrors()) {
return "failed validation";
} else {
return "passed validation";
}
}
I know the #Max works with my application because I use it for validation on custom objects that return a lot of data to the controller. It only doesn't run the validation in this case where I call #Valid and the type of validation on the object in the method parameters.
Is this not allowed with hibernate-validator?
I am hoping to not have to define an object that only contains a long value just so that I can run validation on it.
I am hoping to not have to define an object that only contains a long
value just so that I can run validation on it.
Defining a wrapping bean would IMHO be the smartest move, as hibernate-validator is completly centered around the notion of the bean, and is, after all, a reference implementation of the bean validation specification. One of the primary motivators of the spec is to acknowledge validation as a cross-cutting concern that spans across different app layers, and provide a mechanism to gracefully handle this. That is the reason why it is centered around beans, its the objects that get passed through the layers.
On the other hand, validating primitves programtically is not a big deal after all, your code can simply be something like
#RequestMapping(value = "test", method = RequestMethod.POST)
#ResponseBody
public String getTest(#RequestBody long longValue, BindingResult result) {
if (longValue > 32) {
result.rejectValue("longValue", "error.longValue", "longValue max constrained failed");
return "failed validation";
} else {
return "passed validation";
}
}
So in my opinion, either go for the programatic validation if its simple enough, or simply wrap the value.
No, it is not allowed. I see your point however the JSR-303 specification (which hibernate validator implements) are meant for beans validation, see here
First, as stated by other answers, Hibernate Validator doesn't allow to validate directly primitives like the example in the question. In order to use Hibernate Validator, defining a new class containing a long value is exactly what is required in order to use Hibernate Validator.
Second, and most important, whereas programatic validation is something that works and it is quite simple, it is not clean and maintanable. Let me illustrate this with some examples:
#RequestMapping(value = "testA", method = RequestMethod.POST)
#ResponseBody
public String getTestA(#RequestBody long longValue, BindingResult result) {
if (longValue > 32) {
result.rejectValue("longValue", "error.longValue", "longValue max constrained failed");
return "failed validation for test A";
} else {
return "passed validation for test A";
}
}
#RequestMapping(value = "testB", method = RequestMethod.POST)
#ResponseBody
public String getTestB(#RequestBody long longValue, BindingResult result) {
if (longValue > 32) {
result.rejectValue("longValue", "error.longValue", "longValue max constrained failed");
return "failed validation for test B";
} else {
return "passed validation for test B";
}
}
#RequestMapping(value = "testC", method = RequestMethod.POST)
#ResponseBody
public String getTestC(#RequestBody long longValue, BindingResult result) {
if (longValue > 32) {
result.rejectValue("longValue", "error.longValue", "longValue max constrained failed");
return "failed validation for test C";
} else {
return "passed validation for test C";
}
}
First thing to notice, is withing 3 simple functions, all the validation code is duplicated.
Secondly, if at some point your validations requirements change, and now all the long values must be bigger than 35, you need to change every single validation function, and in this example is really simple, because there is only 3 functions with the same validation, but imagine for one moment it is 100 functions where you perform the same validation, now that is painful.
So just defining a new class, with the long value and the validation annotation, and using that class on every method, you removed code duplication, and also when your validation requirements change, applying changes in one single place, you have mantained the code.
Also, there is nothing wrong about having to define classes for one specific task, in fact, that is exactly what Single Responsability Principle tells you to do.
Edit: added a link to a SRP description.

My custom accessor is never used and only the field default value is used by hibernate validator instead

I have the following code inside a javabean:
#AssertTrue
private boolean addressReferenceValid;
public boolean isAddressReferenceValid() {
if (addressType.equals(AddressType.ON_THE_FLY_ADDRESS) && StringUtils.isEmpty(addressReference)) {
return false;
}
return true;
}
The issue I have is that the isAddressReferenceValid accessor is never used and it seems that only the default value for addressReferenceValid is used (i.e. false).
I have double checked that Spring Roo did not generate its own accessor for that variable.
Can anyone please help?
P.S. I can't put the #AssertTrue annotation directly on the method because then the following key in ValidationMessages.properties file would not be resolved: AssertTrue.familyAdvertisementSearchCriteriaInfo.addressReferenceValid
edit 1:
Full bean:
#RooEquals
#RooJavaBean
public class FamilyAdvertisementSearchCriteriaInfo {
#Valid
private FamilyAdvertisementSearchCriteria searchCriteria;
private Address currentAddress;
private String addressReference;
#NotNull
private AddressType addressType;
#AssertTrue(groups=Default.class)
private boolean addressReferenceValid;
public boolean isAddressReferenceValid() {
if (addressType.equals(AddressType.ON_THE_FLY_ADDRESS) && StringUtils.isEmpty(addressReference)) {
return false;
}
return true;
}
}
Validation occurs in the following controller:
#RequestMapping(value = "/familyAdvertisementSearch", method = RequestMethod.POST, produces = "text/html")
public String familyAdvertisementSearchResults(#ModelAttribute #Validated(Default.class) FamilyAdvertisementSearchCriteriaInfo familyAdvertisementSearchCriteriaInfo, BindingResult bindingResult, Model model) {
if(bindingResult.hasErrors()){
populateModel(model);
familyAdvertisementSearchCriteriaInfo.setCurrentAddress(memberService.retrieveCurrentMemberAddress());
return "search/familyAdvertisementSearchForm";
}
...
I think I understand now what you are trying to do. You want to place the constraint on the field, but during validation you expect the method isAddressReferenceValid to be called/used. That's not going to work. If you place a constraint on a field access is used to get the property to validate (using reflection). If you place it on a method/getter method access is used. So he placement of the annotation matters. As you already seem to have discovered placing the annotation on the method works. Of course this leads to inconsistent placement of annotations. You could:
Just place the annotation for this single constraint
switch to method annotations completely
update the Boolean every time the address type changes (and get rid of isAddressReferenceType method)
create a custom constraint for verifying the addressReference
Just some ideas. It all depends on your use case and personal preferences.

Categories