org.springframework.web.bind.MethodArgumentNotValidException - java

I am using spring boot and I have a RestController with method
addBook(#Validated #RequestBody BookDto bookDto)
BookDto has a field annotated with write only
#JsonProperty(access = Access.WRITE_ONLY)
#NotNull(message = "cannot be empty")
#Size(min = 1, max = 20)
private String isdnNo;
This works as expected and I have to provide isdnNo while i do a post request.
And when I get it don't bring the isdnNo in reponse which is fine.
Issue here is in JUnit test case. I am using stand alone setup to test controller.
MockMvcBuilders.standaloneSetup(bookController).build();
MockHttpServletRequestBuilder postRequest = MockMvcRequestBuilders.post("/books/").contentType(JSON)
.accept(JSON);
postRequest.content(asJsonString(bookDto));
ResultActions result = mvc.perform(postRequest);
This JUnit test case gives exception org.springframework.web.bind.MethodArgumentNotValidException and says idnNo cannot be empty even though I have provided isdnNo in bookDto on performing post. What can be the issue here.
if I remove #JsonProperty(access = Access.WRITE_ONLY) the test case works fine.
Thanks in advance !

You want to keep Access.WRITE_ONLY removed because, according to the documentation, during serialization (ie writing it to a string) it will not read the value for serialization. You're method asJsonString is serializing the object thus removing the value.
AUTO: Access setting which means that visibility rules are to be used to automatically determine read- and/or write-access of this property.
READ_ONLY:
Access setting that means that the property may only be read for serialization, but not written (set) during deserialization.
READ_WRITE
Access setting that means that the property will be accessed for both serialization (writing out values as external representation) and deserialization (reading values from external representation), regardless of visibility rules.
WRITE_ONLY
Access setting that means that the property may only be written (set) for deserialization, but will not be read (get) on serialization, that is, the value of the property is not included in serialization.
https://fasterxml.github.io/jackson-annotations/javadoc/2.6/index.html?com/fasterxml/jackson/annotation/JsonProperty.Access.html

Related

Using Lombok's SuperBuilder with Hibernate Validator (jakarta.validation.x) annotation on a container type leads to "Type mismatch"

I have a class ActivitiesModel which uses Lombok's SuperBuilder.
import jakarta.validation.NotBlank;
// other imports and statements omitted for brevity.
#Data
#SuperBuilder
#NoArgsConstructor
public class ActivitiesModel {
public static final String ACTIVITIES_NOT_NULL_MESSAGE = "Activities cannot be null";
public static final String ACTIVITY_NOT_BLANK_MESSAGE = "Activity cannot be blank";
#NotNull(message = ACTIVITIES_NOT_NULL_MESSAGE)
private List<#NotBlank(message = ACTIVITY_NOT_BLANK_MESSAGE) String> activities;
}
I am using this builder to create an object of ActivitiesModel, and then validating it using Hibernate's Validator interface:
// Somewhere else in the application.
// Create an object using the builder method.
ActivitiesModel activitiesModel = ActivitiesModel.builder()
.activities(List.of("hello", "world")) // <----- Point A
.build();
// Validate the object using Hibernate's validator.
validator.validate(activitiesModel);
However, running this code gives me the following error:
java.lang.Error:
Unresolved compilation problem:
Type mismatch: cannot convert from List<String> to List<E>
The stack trace seems to be pointing at Point A.
I have tried the following approaches:
Replacing the #SuperBuilder with #Builder and #AllArgsConstructor.
Replacing the message attribute with a string literal instead of a static final variable, i.e:
private List<#NotBlank(message = "Activity cannot be blank") String> activities;
1st approach seems to fix this error, however, it's not something I can use as I need to extend the builder functionality to a subclass of ActivitiesModel. Also, this issue is also present in another abstract class, so the super builder functionality for parent classes is definitely required.
2nd approach also works in solving the error. However, going with it is a bit problematic because I then need to have the same message string in the validation test for this model class, which is something I would like to avoid as it duplicates the string.
Another thing to note is that this error only seems to occur in the presence of an annotation on the generic type parameter of the container, which is NotBlank in this case. It is not influenced by any annotations which are present directly on the field itself (NotNull in this case).
So, all in all, these are the questions that I would like to get some answers to:
Somehow, Lombok is able to figure out the types in case of a string literal but not in case of a static final String. Why is that?
Am I going about this totally wrong? The problem occurs because I'm trying to store the message string in a variable, and I'm trying to re-use the same variable at two places: the annotation's message attribute, and in the validation test for the model class. Should I not be checking for the presence of the message in my validation tests, but be checking for something else instead?
For anyone who comes across this later on, the research for this issue has led me to believe that comparing message strings in tests is not the way to go about writing validation test cases. Another downside to this approach is that you might have different validation messages for different locales. In that case, the message string itself might be a template e.g. my.message.key with its values in a ResourceBundle provided to Hibernate, i.e. files such as ValidationMessages.properties and ValidationMessages_de.properties.
In such a scenario, you could compare message for one locale in your validation test case, however, a better approach might be to check the annotation and the field for which the validation has failed. We can get both of these pieces of information via the ConstraintViolation and subsequently the ConstraintDescriptor types, provided by Hibernate. This way we can circumvent checking the message itself, but rely on the actual validation annotation which has failed.
As for the solution to this question, it seems it was a build cache issue. Cleaning maven's build cache results in this code working perfectly fine, but VSCode still seems to have an issue. For now, I will choose to ignore that.

Bean Validation - constructor/factory parameter

I have a dto object which keeps an IP Range using first and last fields. Simple CRUD operations are made with this class using dropwizard (jersey-jackson-hibernate validator)
public class IpRangeDto {
#JsonCreator
public static IpRangeDto fromCidr(#JsonProperty("cidr") String cidr) {
//Resolve CIDR and assign first and last fields
}
#NotNull
#IpAddress // My custom validator
private String first;
#NotNull
#IpAddress
private String last;
}
For the sake of user-friendliness I had decided to add an alternative way to create this object, which is by using CIDR. So the client could send either first and last fields in JSON or only the cidr field. So the way to do it is as above, using #JsonCreator. And it works just fine.
"ipRange":{
"first": "15.0.0.1",
"last": "15.0.0.255",
}
"ipRange":{
"cidr": "15.0.0.0/24"
}
I want to validate this CIDR value that it's the right format so I can return 422 with proper error message. If I throw exception in the constructor/factory method then jersey-jackson returns 400 directly (even if I throw ConstraintViolationException, it's encapsulated by JsonProcessingException).
I could simply ignore the exceptions, and leave the fields empty which will return 422 because of #NotNull constraints but then the error message will not be as clear as it should be.
I tried adding my #Cidr validator next to the #JsonProperty parameter but that doesn't seem to be effective. My understanding is that validation occurs after Jackson is finished with creating Dtos, so with my #JsonCreator approach there might not be any solution to this problem. So I'm open to refactoring suggestions as well.
I am not an expert on the exact integration of Bean Validation into jackson, but I think it is just doing actual property validation. This means as you already pointed out, the entities are created first and then the properties are validated.
Bean Validation (as of version 1.1) also offers so called method validation, in which case you could place your Cidr constraint onto the string parameter of the method, but as said, I don't think that there is an integration in jackson for that.
And one more thing ;-) - static methods and properties are generally excluded from validation in Bean Validation (see also http://beanvalidation.org/1.1/spec/#d0e2815).
Regarding a workaround, one thing comes to mind (even though it feels a bit complicated). Write a custom class level IpRange constraint. In a class constraint you would get passed a IpRangeDto instance and it is up to you to validate the whole object and select the right error message for any violations. Provided you would add a cidr property to the dto which gets set when fromCidr is called, you would have then all information you need for the validation and selection of a proper error message.

How to validate a single parameter in DropWizard

I want to validate some input parameters in DropWizard as documented here: https://dropwizard.github.io/dropwizard/manual/core.html#validation
This example validates an object's properties, which works fine, but my service doesn't receive an object, but the parameters directly. This is a simplification of my code:
#GET
#Path("/MyResource/{myresourceId}")
public MyResource detail(#PathParam("myresourceId") #Valid #Size(min = 2, max = 5) String myresourceId) {
The #Valid and #Size annotations seem to be ignored in this case.
Anyone knows if this is possible and if so how to do it right? Thanks.
It's not possible with those annotations as method parameters. Have a look at this thread.
You might be able to write some AOP code to support this (depending on how you're initializing your resources).

Hibernate's validateValue() throws illegal argument exception if a property has no rules?

I have the following class:
class Foo
{
#NotEmpty
private String member1;
#NotEmpty
private String member2;
private String member3; //this one is optional, so has no rules
}
I have a library to which I add all the property names and corresponding UI fields, each time the UI's onChange event occurs, I call validateValue() on the given field name for that field, to validate it and show error/success message.
The problem is, in this case where I have no rules for member3, if I try to validate it by doing this:
String value = event.getValue(); //whatever the new value is now
validator.validateValue(Foo.class, "member3", value);
On the 2nd line, I get the following exception:
Caused by: java.lang.IllegalArgumentException: member3 is not a valid property of
com.xxx.Foo
Both member1 and member2 within the same class, are validated correctly.
Is there anything I can do to avoid getting this exception on the fields that don't have any rules set on them? If not, is there a way (without reflection or specifying it manually for each field) to check if a rule has no rules set on it, so i can avoid calling validateValue on it?
Which version of Hibernate Validator are you using? I checked with the latest version (5.1.0.Final) and there it works. If you can I recommend you upgrade.
You can also create an issue in the Validator issue tracker, reporting your problem and in particular which Validator version you are using.
Last but not least, to answer your question about alternatives. You could use the Bean Validation metadata API to find the constrained properties:
validator.getConstraintsForClass(Foo.class).getConstrainedProperties()
This will allow you to process only the properties which are acually constrained.

Jackson - Required property?

I'm using Jackson's readValue() method on an object mapper to read from a JSON file and convert it into my java object.
eg.
mapperObject.readValue( node, MyTargetClass.class )
Are there any annotations that I can set on MyTargetClass to enforce required attributes? For example, if I have a JSON object with properties ABC,DEF and GHI, and my Json is the following
{
"ABC" : "somevalue"
"DEF" : "someothervalue"
}
I want it to fail somehow, and only succeed on the readValue if it contained ABC, DEF and GHI.
You can mark a property as required with the #JsonProperty(required = true) annotation, and it will throw a JsonMappingException during deserialization if the property is missing or null.
Edit: I received a downvote for this without comment. I'd love to know why, since it does exactly the right thing.
Jackson does not include validation functionality, and this is by design (i.e. that is considered out-of-scope). But what is usually used is Bean Validation API implementation.
The nice thing about this is decoupling between data format handling, and validation logic.
This is what frameworks like DropWizard use; and it's the direction JAX-RS (like Jersey) are taking things for JAX-RS 2.0.
If you want to make sure a json field is provided, you have to use the #JsonProperty(value = "fieldName", required = true) annotation as a parameter to the constructor. But this is not enough, also the Constructor should have #JsonCreator annotation.
For example, if you have a field named 'endPoint' and you want o make sure it is provided in the JSON file, then the following code will throw an exception if it is not provided.
#JsonCreator
public QuerySettings(#JsonProperty(value = "endPoint", required = true) String endPoint) {
this.endPoint = endPoint;
}
I found this link helpful to understand the Jackson annotations. It also well explains why required=true is not enough and counter-intuitive to its name.
If you are neither satisfied with using #JsonProperty(required = true) as it works only with #JsonCreator nor with the use of bean validation then one more way of tackling it would be to catch this in your setter methods for the relevant variables.
You can simply check if the variable is null before setting it and throw an IllegalArgumentException or NullPointerException (as preferred by few people)
Note: It depends on how your POJO is defined too, so please make sure that it is going the setter method route for this solution to work.

Categories