Cannot use spring validation with MongoDB [duplicate] - spring

This question already has answers here:
How to I get Spring-Data-MongoDB to validate my objects?
(4 answers)
Closed 6 years ago.
I searched for solutions to validate data models in spring in Kotlin. But am not able to.
The following is the Tag data class.
data class Tag(#field:NotNull var name: String) {
lateinit #Id var id: ObjectId
}
I have enabled the configuration to use bean validation
#Configuration
open class ValidatorConfig {
#Bean
open fun validator() = LocalValidatorFactoryBean()
}
As per my knowledge, Spring Boot loads all the Bean Configuration automatically. And at runtime, when POSTed with empty Json, proper validation error should have been thrown but the following error is thrown
Instantiation of [simple type, class kagaz.sano.model.Tag] value failed for
JSON property name due to missing (therefore NULL) value for creator parameter
name which is a non-nullable type\n at [Source:
org.apache.catalina.connector.CoyoteInputStream#66f3b65c; line: 3, column: 1]
(through reference chain: kagaz.sano.model.Tag[\"name\"])

Spring validates objects by inspecting it's fields or properties values. To validate an object you need to first create it. When the json deserializer tries to parse input and create Tag instance it encounters and reports the problem:
Instantiation of [simple type, class kagaz.sano.model.Tag] value failed for
JSON property name
In order for Spring Validation to even start its work an instance of object is required - even if it is invalid.
This means that you'll need to allow for json deserializer to create an object in invalid state so that it can be inspected by validator like so:
data class TagDto(#get:NotNull var name: String? = null) {
#get:NotNull var id: ObjectId? = null
}

Related

Impact of #JsonTypeInfo in REST endpoints when concrete implementations are used

I'm wondering about the effects of adding a #JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#class") to an interface.
My use case is that I have an interface Message with many subtypes. I want to be able to deserialize and serialize lists of messages in one endpoint.
My classes:
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#class")
interface Message: Serializable
data class Message1(
val name: String,
val age: Int
)
data class Message2(
val name: String,
val nickName: String
)
And the respective endpoint:
#RestController
class MessageController {
#GetMapping("/messages")
fun getEndpoints(): List<Message> {
return listOf(
Message1("Marco", 22),
Message2("Polo", "Poli")
)
}
}
So far so good - but now I want another endpoint that uses one of the explicit classes and I get a serialization error in my test that #class is missing - I don't want to send that when I'm using a concrete class anyhow.
#RestController
class MessageController {
#PostMapping("/add1")
fun add(#RequestBody content: Message1) {
// do something
}
}
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Missing type id when trying to resolve subtype of [simple type, class com.tractive.tap.message.request.RequestDataDTO]: missing type id property '#class'; nested exception is com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Missing type id when trying to resolve subtype of [simple type, class com.tractive.tap.message.request.RequestDataDTO]: missing type id property '#class'
at [Source: (PushbackInputStream); line: 1, column: 51]
Why is the #class expected even though I'm using a concrete class? Is this expected behavior or am I doing something wrong?
Well, it is expected because by annotating the class with #JsonTypeInfo - via class-interface inheritance - you have explicitly instructed your Jackson to expect this information.
#JsonTypeInfo accepts parameter defaultImpl of type Class<?> that will be used
if type identifier is either not present, or can not be mapped to a registered type
You could use that to default your deserialisation to one type of message, preferably the most widely used explicitly in your api. For other types, you would still have to include the class info for Jackson.

Jackson deserializer priority?

I have a Spring Boot app that is modeling ActityStreams objects and for the most part Jackson's Polymorphic Deserialization works well.
There are 'objects' in the JSON which are references (links) and not JSON objects with type information. For instance
"actor":"https://some.actors.href/ rather than
"actor":{
"type":"Actor",
"name":"SomeActor"
}
I've written custom deserializers and and placed them on the fields to deal with this
#JsonDeserialize (using = ActorOrLinkDeserializer.class)
private Actor actor;
However my ActorOrLinkDeserializer is instantiated but never called and Jackson complains with Missing type id when trying to resolve subtype of [simple type, class org.w3.activity.streams.Actor]: missing type id property 'type' (for POJO property 'actor') which is from the polymorphic deserializer.
It appears that the polymorphic deserialization code takes precedence over my local #JsonDeserialize annotation and I need a way to force my code to run first.
I've tried using my own ObjectMapper rather than Boot's and there's no difference.
I'd appreciate pointers and suggestions.
It turns-out there's a fairly simple solution to this problem using a DeserializationProblemHandler.
What I've implemented that works for all test cases so far is
1.
objectMapper.addHandler(new DeserProblemHandler());
or register with Spring Boot.
2.
public class DeserProblemHandler extends DeserializationProblemHandler {
public JavaType handleMissingTypeId(DeserializationContext ctxt, JavaType baseType, TypeIdResolver idResolver, String failureMsg) {
return TypeFactory.defaultInstance().constructType(baseType.getRawClass());
}
}
Add a constructor to each of the polymorphic classes that takes a string argument which is the href.

Spring boot validation annotation not working for Kotlin

I got a simple data class for Kotlin
data class Person(val name: String, #get: Min(18) val age: Int)
I actually build this class from a CSV file and I read the CSV using apache CSV parser. Even I have some data which is less than 18 for age field, the test still passed no error.
Looks like this annotation is not working for Kotlin?
You're currently annotating the constructor parameter with the Min annotation - I believe you should annotate the field instead, with a use-site target like this:
data class Person(val name: String, #field:Min(18) val age: Int)
Try adding the annotation#Validated to the class (might just work for spring beans though). These annotations do not prevent the value being set they simply allow getting a BindingResult with a list of validation errors from an existing instance. If you need to prevent a value being set you can use a custom delegate.
val age: Int by MyDelegates.min(18)

Optional form fields in spring-mvc liferay portlet application

I have a big form with a lot of date fields and int fields
my action phase methos is like this:
public void createCamapign(WebRequest request,ActionResponse response ,
Model model, #ModelAttribute("searchForm") CreateCampaignForm form) throws Exception{
where the form is my wrapper bean
anyway if i dont fill all the fields in the rendered form my application crashed with this error
[org.springframework.web.portlet.DispatcherPortlet] - Handler execution resulted in exception - forwarding to resolved error view
org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'searchForm' on field 'activationDate': rejected value []; codes [typeMismatch.searchForm.activationDate,typeMismatch.activationDate,typeMismatch.java.util.Date,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [searchForm.activationDate,activationDate]; arguments []; default message [activationDate]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Date' for property 'activationDate'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [java.util.Date] for property 'activationDate': no matching editors or conversion strategy found]
how or where do i specify that these fields are optional
by the way in the case of int fields i kinda resolved it by using Integer class, but i dont want to do the same with Date type attributes
I am not sure if I understood your problem correctly. I am not an expert in Spring.
Just a suggestion, like can you make use of dependency check and #Required annotation to make a few fields necessary and other ignorable.
You could try to put the DateTimeFormat annotation on you date fields, like this:
#DateTimeFormat(pattern = "dd/mm/yyyy") // put any pattern you want
private Date myDate;
Note: If you are using a version of Spring older than 3.2, you need to add joda date library to your project classpath

Spring-MVC bean Validation Type mismtach error

I am trying to validate an object using Spring JSR303 validation, i have a form object which have some nested objects along with some form properties here is my Form signature
public class PaymentDetailsForm
{
private AddressForm billingAddress;
// other properties and getter and setters
}
In my AddressForm bean i have used Bean validation annotations to validate data, but i am not using any #Valid annotation inside my PaymentDetailsForm for billingAddress.
This is the signature of my Controller method
public String createUpdatePaymentInfos(final Model model,
#ModelAttribute("paymentInfo") #Valid final PaymentDetailsForm form, final BindingResult bindingResult)
{
}
If i am sending correct data from the form everything is working perfectly fine, but if i omit any field from billingAddress which is marked as required or not null i am getting following binding error exception
org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'paymentInfo' on field 'billingAddress':
rejected value [com.xxx.storefront.forms.AddressForm#e39f6f1,true];
codes [typeMismatch.paymentInfo.billingAddress,typeMismatch.billingAddress,typeMismatch.com.xxx.storefront.forms.AddressForm,typeMismatch];
arguments [org.springframework.context.support.DefaultMessageSourceResolvable:
codes [paymentInfo.billingAddress,billingAddress]; arguments []; default message [billingAddress]];
default message [Failed to convert property value of type 'java.lang.String[]'
to required type 'com.xxx.storefront.forms.AddressForm' for property 'billingAddress';
nested exception is java.lang.IllegalStateException:
Cannot convert value of type [java.lang.String[]] to required type [com.xxx.storefront.forms.AddressForm] for property 'billingAddress':
no matching editors or conversion strategy found]
I was expecting that since i have not used #valid annotation for billingAddress property, it should not be validated but even in case it get validated i am not able to understand above mentioned exception/error
The bindingResult that you are seeing doesn't look like it is because of validation errors, it is likely because of the binding errors - not being able to bind the UI fields to the inner billingAddress field. Even the binding errors would end up showing in the immediately following bindingresult argument like you are seeing.
That was due to some wrong mapping from UI,in my JSP page i was mapping address fields to billingAddress object but there was one hidden field like
<form:hidden path="billingAddress" id="billingAddress"/>
This was the cause of error since it was trying to send String array and Spring binding was unable to distinguish what i am trying to do

Resources