Spring MVC 3 nested object validation is failing with #Valid - spring

I have following nested objects. I am using #Valid for validation in my controller. Here BindingResult object is not validating name field of Child object. Am I missing anything?
class Parent{
#Valid
private Child child;
//getter and setter for child object
}
class Child{
#NotNull(messag="Name cannot be null")
private String name;
//getter and setter for name
}
My controller validate method
#RequestMapping(value = "/validate", method = RequestMethod.POST)
public #ResponseBody String validate(#Valid #ModelAttribute("parent") Parent parent, BindingResult bindingResult) {
//Here I can see child name value if I say parent.getChild().getName()
// But if parent.getChild().getName() is null, bindingResult.hasErrors() is returning false
}

As far as I know, #NotNull isn't quite right for String validation, since the Spring-model often mapping "no object received" to "blank string" instead.
Please try #NotBlank and sees if the BindingResults return errors.

You can't do it this way. You can't validate nested objects like that.
You have to use a validator.

I was also facing similar problem before sometime.
And after doing 2-3 days R&D I succeeded with validating nested object. I tried to do custom validations for the nested object.
You need to create a custom validator class and autowire it in the controller and in the method call validator.validate(parent, bindingResult) and it will return you the error binded with the nested object field in bindingResult object and then you can display it on jsp page as usual.
Hope this helps you. Cheers.

Related

Spring MVC: how to avoid duplicating validation at controller and entity

Let's say I have a "Person" #Entity managed via JPA, which has a series of validations applied at entity-level (#NotBlank, #NotNull etc).
#Entity
public class Person {
#NotBlank
private String name;
#Email
private String email;
...
}
For various reasons, we shouldn't directly use an entity as the controller method argument, but rather create a custom "form" (taking the example from https://spring.io/guides/gs/validating-form-input/)
#PostMapping("/person/save")
public String savePerson(#Valid PersonForm personForm, BindingResult bindingResult) {
// map fields to Person entity individually and save
...
}
But now, to make use of Spring's built-in form validation / BindingResult, it appears I have to duplicate all my validation logic on both the Person and PersonForm classes. I don't just want to define them on PersonForm, because there might be other routes in the application to update a Person.
Ideally there would be some way that Spring could lift up the validation constraints on the #Entity and apply them to the form (e.g. if the properties had the same name).
Am I missing something here with validation?

Throw error when properties marked with #JsonIgnore are passed

I have a requirement to mark certain properties in my REST beans as ignored using #JsonIgnore. (I am using Spring Boot). This helps in avoiding these properties in my Swagger REST documentation.
I also would like to ensure that if the client passes these properties, an error is sent back. I tried setting spring.jackson.deserialization.fail-on-unknown-properties=true, but that works only for properties that are truly unknown. The properties marked with #JsonIgnore passes through this check.
Is there any way to achieve this?
I think I found a solution -
If I add #JsonProperty(access = Access.READ_ONLY) to the field that is marked as #JsonIgnore, I get back a validation error. (I have also marked the property with #Null annotation. Here is the complete solution:
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Employee {
#Null(message = "Id must not be passed in request")
private String id;
private String name;
//getters and setters
}
#JsonInclude(JsonInclude.Include.NON_NULL)
public class EmployeeRequest extends Employee {
#Override
#JsonIgnore
#JsonProperty(access = Access.READ_ONLY)
public void setId(String id) {
super.setId(id);
}
}
PS: By adding #JsonProperty(access = Access.READ_ONLY), the property started showing up in Swagger model I had to add #ApiModelProperty(hidden = true) to hide it again.
The create method takes EmployeeRequest as input (deserialization), and the get method returns Employee as response (serialization). If I pass id in create request, with the above solution, it gives me back a ConstraintViolation.
PS PS: Bummer. None of these solutions worked end-to-end. I ended up creating separate request and response beans - with no hierarchical relationship between them.

Request body Object Not being Validated

I have one REST API which is using a POST Call to create a record, I'm expecting certain Object to be passed in post call, if anything is missing i have to reject straight away from their only,
#RequestMapping(value="/saveEssentialDetails",produces={"application/json"},method=RequestMethod.POST)
ResponseEntity<?> saveEssentialDetails(#ApiParam(value="Body Parameters")#RequestBody #Validated EssentialDetails essentialDetails, BindingResult bindingResult)throws Exception;
and the Essential Model class is as follow
#Data
#NoArgsConstructor
#Document(collection="essentialDetails")
public class EssentialDetails {
#NotNull
Integer dpId;
#Id
#NotEmpty
String tpId;
#NotEmpty
List<FamousFor> famousFor;
#NotEmpty
List<OpenHours> openHours;
#NotEmpty
Pictures uploadedImages;
#NotEmpty
List<FloorDescription> floorDescriptions;
#NotEmpty
List<Outlets> mallOutlets;
}
But while making a Post Call with Missing attributes i'm allowed to make an entry in MongoDB, which i don't want to persist as it's not a proper request,#Validation is not working for me, i'm using spring boot 2.0.6 with MongoDb 4.0.4,
any help would be highly appreciated. Thanks well in advance
#Validated can be used to validate a object with a custom validation object. Example usage:
#RequestMapping(value = "/")
public String request(#Validated(Account.ValidationStepOne.class) Account account)
Instead of using #Validated use #Valid which does check for the validation annotations that you are using in your entity.

Spring: How do I construct this command object through a GET HTTP request?

I'm using Spring 3.1.0.RELEASE with Hibernate 4.0.1.Final. I want to invoke a search method in a controller that takes as input a search bean (the Event bean below) ...
#RequestMapping(value = "/search_results.jsp")
public ModelAndView processSearch(final HttpServletRequest request, final Event searchBean, final BindingResult result) {
...
}
The event bean contains the following field ...
#Entity
#Table(name = "EVENTS")
public class Event implements Comparable {
...
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="EVENT_FEED_ID")
private EventFeed eventFeed;
...
}
in which the EventFeed object contains the following fields ...
#Entity
#Table(name = "EVENT_FEEDS")
public class EventFeed {
#Id
#Column(name = "ID")
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
#NotEmpty
#Column(name = "TITLE")
private String title;
...
}
How do I construct a URL such that the search bean's Event.getEventFeed().getId() field is populated?
I realize I could submit a GET request with a parameter like "eventFeedId=2" and populate everything manually, but since other pages are submitting requests that populate the command object, I'd like to continue to use the same logic.
It would be
/search_results.jsp?event.eventFeed.id=...&event.eventFeed.title=...
event is a default model attribute name as defined in #ModelAttribute, other binding rules are described in 5.4.1 Setting and getting basic and nested properties.
Note, however, that this approach can cause problems if you'll associate these bean with Hibernate session later. For example, if you want to attach new Event to the existing EventFeed by calling merge() it would also override the title property. Thus, in such a case it would be better to avoid overuse of data binding and pass primitives as parameters instead.

How can I validate a complex model object in Spring 3 using #Valid annotations?

I have a model object modelling a purchase order. The purchase order has a few fields (such as ID and date) and a list of line-items as ArrayList. I can validate the parent purchase order ok, but it chokes when validating the line-items.
Can anyone help me with validation of complex objects? If I cannot validate complex objects auto-magically, how can I write a custom validator that relies upon the constraint annotations in the parent and then iterates over the child line-items? This Validator instance needs to be able to call something.validate(purchaseOrder) and (for each line-item) something.validate(lineItem). Where do I get "something" from?
I have specified <mvc:annotation-driven /> in dispatcher-servlet. I am not using #InitBinder. And I am using #Valid annotation for validation in controller's method like
#RequestMapping(params="confirmPurchaseOrder")
public String confirm(
#ModelAttribute("purchaseOrder") #Valid PurchaseOrder purchaseOrder,
BindingResult result,
#RequestParam("account") String accountString,
#RequestParam("division") String divisionString,
Model model)
{
if (result.hasErrors()) {
return PURCHASE_ORDER_CREATE_VIEW;
}
The domain classes look like this: -
public class PurchaseOrder implements Comparable<PurchaseOrder> {
/** Based on GUID */
private String id;
/** SOP */
#NotNull
private Integer SOP;
/** Reference from client */
#NotBlank
private String purchaseOrderReference;
/** PO date */
#DateTimeFormat(style="S-")
#NotNull
private Date date;
#Valid
private final Collection<LineItem> lineItems = new ArrayList<LineItem>();
And
public class LineItem {
...Elided
/** Generated from GUID */
private String id;
#NotNull
#DateTimeFormat(style="S-")
#Future
private Date expiry;
private String softwareVersion;
#NotNull
#NumberFormat(style = Style.NUMBER)
#Min(1)
private Integer licenceCount;
When committing a Purchase Order with an empty expiry date, I get the following exception:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.beans.InvalidPropertyException: Invalid property 'lineItems[]' of bean class [com.nit.ols.domain.PurchaseOrder]: Invalid index in property path 'lineItems[]'; nested exception is java.lang.NumberFormatException: For input string: ""
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:730)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:634)
javax.servlet.http.HttpServlet.service(HttpServlet.java:641)
javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
In your PurchaseOrder class, try changing your lineItems collection to a List. It looks like you are having the same problem addressed in this question.
It's like David said, declaring lineItems of type List should do the trick. In Hibernate Validator 4.2.0 CR1 (not yet released atm, you could use the latest snapshot build if you are interested) also Collection should work, see HV-468 for more details.

Resources