Spring annotations #ModelAttribute and #Valid - spring

What are the advantages of using #ModelAttribute and #Valid?
Which are the differences?
Is it possible to use them together?

#ModelAttribute is used to map/bind a a method parameter or method return type to a named model attribute. See #ModelAttributes JavaDoc. This is a Spring annotation.
#Valid is an annotation that marks an object for JSR-303 bean validation. See #Valids JavaDoc. It is part of JavaEE 6, but I think Hibernate has an earlier implementation that most people use instead.
The advantage of using #ModelAttribute is that you can map a form's inputs to a bean. The advantage of #Valid is that you can utilize JSR-303 bean validation to ensure that the bean that is made is validated against some set of rules.
Yes you can use #ModelAttribute and #Valid together.
The best way to transfer data from a form (sic View) to the a Model object is to follow the typical/traditional MVC design pattern using Spring. My personal preferred way is to have a form in a JSP with Spring JSTL <form:*> tags, with a modelAttribute set. On the Controller, have a handler to accept the POST from the form that has a matching #ModelAttribute that is a bean that represents the form's input. I would then pass that "Form Bean" to a service layer to do some things, including translating the "Form Bean" into any models if needed (not necessary if the form is creating your model objects directly) and saving/updating/etc via a DAO. This is but one way to do things, but it's probably the bulk of what I do with Spring in my day-to-day job.
I would highly recommend reading the Spring reference materials and following the tutorials. The reference materials are very well written, easy to follow, and includes lots of examples on the various ways you can do things in Spring, and there are usually quite a few options on how you do things in Spring.

please check the below part fro spring reference documentation:
In addition to data binding you can also invoke validation using your own custom validator passing the same BindingResult that was used to record data binding errors. That allows for data binding and validation errors to be accumulated in one place and subsequently reported back to the user:
#RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(#ModelAttribute("pet") Pet pet, BindingResult result) {
new PetValidator().validate(pet, result);
if (result.hasErrors()) {
return "petForm";
}
// ...
}
Or you can have validation invoked automatically by adding the JSR-303 #Valid annotation:
#RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(#Valid #ModelAttribute("pet") Pet pet, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}

Related

Does #Valid work without #Validated on #RestController?

In following blog entry.
https://www.baeldung.com/spring-boot-bean-validation
The author mentioned about how Spring Boot works with #Valid annotation.
#RestController
public class UserController {
#PostMapping("/users")
ResponseEntity<String> addUser(#Valid #RequestBody User user) {
// persisting the user
return ResponseEntity.ok("User is valid");
}
// standard constructors / other methods
}
When Spring Boot finds an argument annotated with #Valid, it automatically bootstraps the default JSR 380 implementation — Hibernate Validator — and validates the argument.
Is it true that #Valid works as expected on #RestController without #Validated?
Then what kind of stereo types required to be explicitly annotated with #Validated?
Yes #Valid will work without #Validated in #RestController.
In Spring, we use JSR-303's #Valid annotation for method level
validation. Moreover, we also use it to mark a member attribute for
validation. However, this annotation doesn't support group validation.
Groups help to limit the constraints applied during validation. One
particular use case is UI wizards. Here, in the first step, we may
have a certain sub-group of fields. In the subsequent step, there may
be another group belonging to the same bean. Hence we need to apply
constraints on these limited fields in each step, but #Valid doesn't
support this. In this case, for group-level, we have to use Spring's
#Validated, which is a variant of this JSR-303's #Valid. This is used
at the method-level. And for marking member attributes, we continue to
use the #Valid annotation.
You can read more about this in this link.

How to avoid some fields to be bound on form submit?

I have a simple form that edits my profile on the web. 'Person' bean, that describes user, contains many internal fields that cannot be changed by the form. Therefore I have just a subset of fields available for editing on the form. So far so good. Now what if some advanced user opens developer tools in Chrome browser and adds some other fields on the form or rename some existing fields ... so when submitted those fields will be bound back to the 'Person' bean and stored into database. Such way the user can spoof my form easily and chnage values for not allowed fields. Is there a way how to define (server side) which fields (bean properties) can be bound during the form submit?
Here is how the controller method signature looks like to get an idea:
#RequestMapping(path = "/profile/edit", method = RequestMethod.POST)
public String editProfile(#ModelAttribute("profile") Person doc, BindingResult result, Model m){
... saving doc to database ...
}
I'm using SpringBoot 1.3.5 with Thymeleaf ...
Turned out that solution is quite simple. I just added #InitBinder annotated method to controller and used WebDataBinder provided object to specify list of fields allowed. To do this I can use binder.setAllowedFields(...) method. Field names support "xxx*", "*xxx" and "xxx" patterns so its easy to specify set of fields when named properly in the bean. Now when post request variables are bound to my bean, these allowed fields are preserved and the others are rejected and not bound.
Code example:
#InitBinder // or #InitBinder("profile") with ModelAttribute name information
public void initBinder(WebDataBinder binder) {
binder.setAllowedFields("settings*");
}
See DataBinder docs for detailed info.

Best way to provide validation to spring mvc & hibernate projects

I'm new to spring & hibernate, please suggest a best way to provide validation to spring mvc & hibernate projects. I tried hibernate validator but i don't know how to use it when there is relationship(like #OneToOne) between entity objects. If there is any examples, please provide the link.
Your #OneToOne relationship is not clear enough. Could you please give a concrete scenario?
In Spring MVC you have three types of validations:
JSR-303 bean validation (hibernate validator)
Custom validation
Spring validation
JSR-303 works great for simple validations on single fields. You have standard annotations like (#Max, #Min, #NULL, #Pattern, etc.). It does not seem enough for you in this case.
Custom validation is used in a more complex scenario. For instance, what if we need to validate that the newly added product ID is not the same as any of the existing product IDs?
Spring validation represents cross-field validation.
For instance, it's used when we want to compare two or more fields to see if
their values can be considered as valid when combined.
you can use spring validation method.
using valid annotion in controller classes infront of modelattribute annotaion.
then you can use validaions in the model class.
below is an example for controller and bean classes.
this is a many to one configuration inside a bean class:
#ManyToOne(targetEntity = UserType.class)
#JoinColumn(name = "user_type", referencedColumnName = "id")
#NotNull
private UserType userTypeTb;
this is a method inside controller class with valid and modelattribute annotaion:
public ModelAndView home(#Valid #ModelAttribute("user") User user, BindingResult result,
HttpServletRequest request) {
//
//
}

Java Spring #ModelAttribute method model name

I have been reading this forum for quite awhile and find it VERY useful, thank you to the contributors. I have a question that has plagded me for several weeks. And here it goes.
#RequestMapping(value="updateNote.htm",method=RequestMethod.POST)
public String updateNote(#ModelAttribute("note")NoteBean nb, BindingResult res,Model model){
daoobj.updateNote(nb.getName(),nb.getPath(), nb.getNote());
model.addAttribute("note",daoobj.getByName(nb.getName()));
return("success");
}
#RequestMapping(value="updateNote.htm",method=RequestMethod.GET)
public String updateNote(#ModelAttribute("note")NoteBean nb,Model model){
populateNoteBean();
model.addAttribute("note",daoobj.getByName(nb.getName()));
return("editNote");
}
#ModelAttribute("WHAT")
public NoteBean populateNoteBean() {
NoteBean nnb = new NoteBean();
return nnb;
}
With the method populateNoteBean() the model attribute is "WHAT". But, the name that I use is "note". So when I run the code, the NoteBean is correctly saved to the data base. My question is HOW?? It seems that the name "WHAT" should be "note" or that the model attribute is saving it as no name.
Thank for your time.
With your current code you will have two instances of your notebean in the model!
First spring invokes all modelattribute annotated methods in your controller and places the results in the model. Second it evaluates the ones from your requestmapping method.
The point of a modelattribute annotated method is that you can choose how to create your bean. Load it for example from a database.
We use this approach like that:
modelattr method (name="note")
Loads beans from db
requestmapping method with modelattr param (name="note")
Merges the note bean created by the first method with the request paramters from a submit for example and you habe directly access to the modifed one.
One nice effect:
We do not want to put hidden input fields for all attributes in a form just to be able to merge the entity with the entitymanager. This way you can have a form with only one attribute (plus one for the id to be able to fetch the entity)
Or another one:
If your note bean is an abstract class spring has no possibility to instanciate the bean because it does not know what to instanciate. You can for example add a requestparam parameter in the modelattr annotated method and decide what to do yourself.
This is very well described in the documentation. Either the reference or in the api of either controller, reqestmapping or modelattribute i believe.

How do I use #Valid with Spring MVC's #RequestBody parameters?

I'm having an issue with using #Valid on a parameter to a handler method on my #Controller. My code looks like this:
#RequestMapping(value=BIBBLE_BOBBLE_URI_PATTERN + "/widgets", method=RequestMethod.POST)
#ResponseBody
#ResponseStatus(HttpStatus.CREATED)
public Widget saveNewWidget(
#PathVariable final String username,
#PathVariable final String bibbleBobbleName,
#Valid #RequestBody final Widget widget,
final BindingResult results,
final HttpServletRequest request,
final HttpServletResponse response)
where Widget is the class for one of my domain objects. I'm using the #RequestBody annotation to indicate that the payload of the request maps to widget (in my tests, the requests are JSON, though Jackson is on my classpath so I could also use XML).
As you can see, the BindingResult parameter follows directly after the Widget parameter, but I get the following error:
java.lang.IllegalStateException: Errors/BindingResult argument declared without preceding model attribute. Check your handler method signature!
How do I apply the #Valid annotation to a #RequestBody parameter and then get the results?
P.S. I'm using annotation-driven to handle wiring up the controllers, do content-negotiation, etc.
Are you using Spring 3.1? It is a newly added feature in Spring version 3.1. Please see Validation For #RequestBody Method Arguments
This is an issue in spring 3.0 and it is fixed in 3.1 M2.
https://jira.springsource.org/browse/SPR-6709
If you are using spring 3.0, based on some threads and extensive reading, here is the best approach for this problem. An aspect oriented approach so that we can back out when you upgrade it to the version of spring which fixes this issue.
An aspect which intercepts calls to method which uses #RequestMapping annotation and for each parameter which has #Valid annotation on it, call the corresponding validator in the aspect.

Resources