Access and modify attributes/objects which are not part of the form backing bean - spring

I use Spring MVC (via Spring Roo) to build a small web application for administering persons. In the page for creating a person the bean Person is used as form backing object (key "person" in the model map).
<form:form action="${form_url}" method="POST" modelAttribute="person">
I would like to add some attributes to the model map which can be altered by the user in the creation form. Basically, I try to add a Boolean, so that I can control which page is displayed next after the user presses the submit button.
I try to modify the Boolean (key "myBoolean" in the model map) using a simple checkbox:
<form:checkbox id="_myboolean_id" path="myBoolean"/>
However, as I am new to Spring MVC I have some difficulties here. The Boolean object is not an attribute of the form backing object. So if I try to access it the following exception is thrown (of course):
Invalid property 'myBoolean' of bean
class [de.cm.model.Person]: Bean
property 'myBoolean' is not readable or
has an invalid getter method: Does the
return type of the getter match the
parameter type of the setter?
Is there are way to access a value of the model map directly? The only solution I can imagine right now is a kind of wrapper object around the class Person and my additional attributes which is used as a new form backing object. However, this is more work for a IMHO simple task. Do you have a better solution?

You may create custom form field:
<input type="checkbox" name="myBoolean"/>
And specify additional parameter in Controller post method:
public ModelAndView savePerson(#ModelAttribute("person") Person person, #RequestParameter ("myBoolean") Boolean myBoolean)

Related

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.

#ModelAttribute returns a new instance on form submit

I am working on a Spring MVC based application. The process flow is as follows:
Fetch the data from DB (table mapped to a POJO)
Display a form backed by the POJO (from step 1). Not all the fields are displayed (like Primary Key etc).
User can update some field value in the form and will then submit.
On receving the updated POJO using #ModelAttribute annotation in my Controller, I found that not all the fields are populated in the POJO received via #ModelAttribute. All the fields which were not mapped on the JSP (like primary key) are set to null or their default value in case of primitives. Due to this I am not able to update the same in the DB.
One solution that I found is I can use fields but that does not sound much efficient solution as I have a large number of attributes which are not displayed on the JSP page.
A model attribute is simply a glorified request attribute. Request attributes only live for the duration of one request-response cycle.
HTTP request -> Get POJO from DB -> Add POJO to model attributes -> Render JSP -> HTTP response
After that, the request attributes are eventually GC'ed since they are no longer reachable by the application (the servlet container makes sure of that).
The next request you send will have its set of new request attributes with no relation to the previous requests'.
When you generate a <form> from a model attribute, Spring creates the <input> elements from the fields of the model attribute which you choose. When you eventually submit the form, only those values will be sent as request parameters in the request. Your application will therefore only have access to those to generate the new model attribute.
You seem to need Session attributes or Flash attributes (which are really just short-lived session attributes).
Please if somebody know a better solution please let me know, but on my application we are always sending back all those id´s and others values that we want to persist in the request response with hidden fields, but I think is a little bit risk, for example in case of id´s. which could be used for SQLInjections attacks.
You could use path variable to transport the primary key (kind of rest url ...) and make use of all the magic of Spring :
create a DefaultFormattingConversionService bean (to keep default conversions)
register (in that ConversionService) a Converter converting a String in your POJO (string -> primary key -> object from database)
directly use the path variable in your controller methods
#RequestMapping(value=".../{data}/edit", method=RequestMethod.GET)
public String edit(#ModelAttribute("data") Pojo pojo) {
return "view_with_form";
}
#RequestMapping(value=".../{data}/edit", method=RequestMethod.POST)
public String update(#ModelAttribute("data") Pojo pojo, BindingResult result) {
if (result.hasErrors()) {
return "view_with_form";
}
return "redirect:.../data/edit";
}
When you give a ModelAttribute to a controller method, Spring tries to find it in the session or in a path variable. And even if you didn't asked for it, the error management is not very expensive ...

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.

Work flow of simpleFormcontroller in spring MVC 3.0

I have seen many examples on how simpleFormcontroller works.
But still I have some confusion.
I want to know when formBackingObject(), referenceData(), onSubmit() methods invoked?
I dont know exact working flow of these methods?
Can anyone explain me?
Workflow is as follows and it is controlled by AbstractFormController class-
The controller receives a request for a new form (typically a GET).
Call to formBackingObject() which by default, returns an instance of the commandClass that has been configured (see the properties the superclass exposes), but can also be overridden to e.g. retrieve an object from the database (that needs to be modified using the form).
Call to initBinder() which allows you to register custom editors for certain fields (often properties of non-primitive or non-String types) of the command class. This will render appropriate Strings for those property values, e.g. locale-specific date strings.
Only if bindOnNewForm is set to true, then ServletRequestDataBinder gets applied to populate the new form object with initial request parameters and the onBindOnNewForm(HttpServletRequest, Object, BindException) callback method is called. Note: any defined Validators are not applied at this point, to allow partial binding. However be aware that any Binder customizations applied via initBinder() (such as DataBinder.setRequiredFields(String[]) will still apply. As such, if using bindOnNewForm=true and initBinder() customizations are used to validate fields instead of using Validators, in the case that only some fields will be populated for the new form, there will potentially be some bind errors for missing fields in the errors object. Any view (JSP, etc.) that displays binder errors needs to be intelligent and for this case take into account whether it is displaying the initial form view or subsequent post results, skipping error display for the former.
Call to showForm() to return a View that should be rendered (typically the view that renders the form). This method has to be implemented in subclasses.
The showForm() implementation will call referenceData(), which you can implement to provide any relevant reference data you might need when editing a form (e.g. a List of Locale objects you're going to let the user select one from).
Model gets exposed and view gets rendered, to let the user fill in the form.
The controller receives a form submission (typically a POST). To use a different way of detecting a form submission, override the isFormSubmission method.
If sessionForm is not set, formBackingObject() is called to retrieve a form object. Otherwise, the controller tries to find the command object which is already bound in the session. If it cannot find the object, it does a call to handleInvalidSubmit which - by default - tries to create a new form object and resubmit the form.
The ServletRequestDataBinder gets applied to populate the form object with current request parameters.
Call to onBind(HttpServletRequest, Object, Errors) which allows you to do custom processing after binding but before validation (e.g. to manually bind request parameters to bean properties, to be seen by the Validator).
If validateOnBinding is set, a registered Validator will be invoked. The Validator will check the form object properties, and register corresponding errors via the given Errors object.
Call to onBindAndValidate() which allows you to do custom processing after binding and validation (e.g. to manually bind request parameters, and to validate them outside a Validator).
Call processFormSubmission() to process the submission, with or without binding errors. This method has to be implemented in subclasses.
Source
For more details and diagrammatic representation to understand the flow you can refer to below link -
SimpleFormController is deprecated since Spring 3.0
In Spring 3.0 use one controller with two methods for the creation process (and a third one for the show page). It typical looks like that:
/**
* Shows a form for car creation.
*/
#RequestMapping(params = "form", method = RequestMethod.GET)
public ModelAndView createForm() {
ModelMap uiModel = new ModelMap();
uiModel.addAttribute("carCreateFormBackingObject", new CarCreateFormBackingObject()); //formBackingObject - often called command object
uiModel.addAttribute("manufactureres", this.manufactureresDao.readAll()); //referenceData
return new ModelAndView("car/create", uiModel);
}
/**
* Creates the car and redirects to its detail page.
*
*/
#RequestMapping(method = RequestMethod.POST)
public ModelAndView create(final #Valid CarCreateFormBackingObject carCreateFormBackingObject,
final BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
ModelMap uiModel = new ModelMap();
uiModel.addAttribute("carCreateFormBackingObject", carCreateFormBackingObject);
uiModel.addAttribute("manufactureres", this.manufactureresDao.readAll()); //referenceData
return new ModelAndView("car/create", uiModel);
}
Car car = this.carService.create(carCreateFormBackingObject.name, ...);
return new ModelAndView(new RedirectView("/cars/" + car.getId(), true)); //redirect to show page
}
still i want to know formBackingObject(),refernceData() method get invoked automatically by whom and when?
Back to your question "still i want to know formBackingObject(),refernceData() method get invoked automatically by whom and when?"
All these methods get invoked by SimpleFormController (and its superclass AbstractFormController), the follow the Template-Method-Pattern. - SimpleFormController defines the process and your concrete subclass "plugsin" in some hooks of this process to gain the business value.
formBackingObject in invoked by AbstractFormController when the controller needs to handle a Submit (POST), or build the Command object for the initial "new" view.
referenceData is always invoked AbstractFormController when it need to build the model for the view.
formBackingObject() method, is used when you want to take some action before rendering page. i.e. like default value in HTML components.
refereceData() method, is used for add reference data in your form, i.e. populating dropdowns
OnSubmit() method, is called whe you submit form.
But, if you are using Spring 3.0
Follow following approach using annotation
#RequestMapping(value = "/index.htm", method = RequestMethod.GET)
public String showLogin() {
return "user/login";
}
This will same as formBackingObject. and in this method use modelMap() and add reference data.
Add methods same way with method = POST which will be same as OnSubmit()
rfe folling link
http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/web/servlet/mvc/SimpleFormController.html
Read Workflow.. you can simply understand your doubts..
FormBackingObjectMethod()---> #RequestMapping(requestMethod.GET)
while first time form shown to the screen formBackingObject is the reason
initBinder()---> normally used for suppose you want date field should be for example (custom date example : dd**MM***yyyy) needed means use initBinder method
onSubmit() -->#RequestMapping(requestMethod.POST)
while submitting the form onSubmit() method get called
i hope this helps

<form:form modelAttribute .. using two beans in JSP

This is a spring based application. My form definition in JSP is as below,
<form:form modelAttribute="article" enctype="multipart/form-data" method="POST"
action="${sendEmailUrl}" name="postAd" id="postAd">
I want to display some data from 'article' bean on my form so I've defined modelAttribute='article'. Till here it is all fine. But on submit of the form, I want to collect data in different bean than article. Since I can define modelAttribute only once in the form, can someone please advise how can I use two beans in JSP?
P.S. In case I am not clear, let me give more details. On submit of form, data entered by user will be collected in bean 'X' and email will be sent using java email. But bean 'Y' (article in this case) is holding some values which needs to be displayed on the form.
Hope I am clear.
You could create a FormBean class, containing the two beans you said. Use this new class as a modelAttribute in your form and you will be able to access to both object's properties.
public class FormBean {
public Article article;
public YourOtherObject yourOtherObject;
}

Resources