How can I add ObjectError in BindingResult in SpringBoot Controller - spring-boot

In my code I have a requirement that when user registers itself on the website, apart from standard validation of registration form, I need to ensure that the email provided by the user has not been registered already. I found the method addError(ObjectError) on the existing BindingResult object that I have. My challenge is how to create this new ObjectError and what values should I pass in it's construction mainly, the parameter (String objectName).
I am referring to the following API:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/validation/ObjectError.html
Many Thanks

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 ...

Extending JSF validation in application

We are developing a web application based on JSF (v2.0) framework. We need to have custom validations in our application. We decided to extend the JSF validation framework by implementing the Validator class.
So let us say that we have multiple input fields which needs to be validated. These input fields are First Name, Last Name, Email Address. We need the user to enter information in these fields. And email address field will have two validations - Required and isValidEmailAddress.
We should be able to use the custom Required validation in First Name, Last Name and Email Address fields. But each time I want different error messages to be displayed for each field. For example in case of First Name, I want to display First Nameis required. In case ofLast nameI want to displayLast name is required`.
How can I reuse the same Required validation implementation for multiple fields but with different error message? Is it possible to do that in JSF? Could you please let me know?
First of all, you don't need to write your own validation logic for required input in JSF. It's one of the basic amenities provided by the framework itself.
JSF's validation framework is cleanly abstracted from messages related to validation, so the two don't have to depend on each other. Your options:
Each input component has a validatorMessage attribute that allows you specify the text/string that will be displayed to the user on validation error. For your specific use case, JSF has gone one step further to specify the required and requiredMessage attribute for input components; to enforce required input and show messages specifically for required input validation respectively. What this means in your use case is that you don't need to write custom validation logic for required input.
By principle of better design you can configure all your desired validation/conversion error messages in a resource bundle (example here) and reference the entries in the resource bundle within your jsf view.
Implementing the validator interface requires you implement the validate method with the following signature
public void validate(FacesContext ctxt, UIComponent comp, Object value) throws ValidatorException
comp refers to the component being validated from which you can get it's Class, clientId etc. value will provide the submitted value from the component

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

Best practice for using #SessionAttributes

I am trying to share data between two controllers in a Spring mvc application.
In Controller A I have annotated the class with #SessionAttributes({"mymodel1"}) and in the method which receives the first GET request I add it to the ModelMap:
model.addAttribute("mymodel1", MyModel1);
I now want to read myModel1 from Controller B.
In this Controller I have the following method which intercepts the POST requests and already has a different model in its parameters:
public String processSubmit(#ModelAttribute("mymodel2") MyModel2 mymodel2, BindingResult result, SessionStatus status, HttpServletRequest httpRequest)
Up to this point everything works fine and I am able to read mymodel2 from processSubmit however if I now want to add another #ModelAttribute("mymodel1") MyModel1 mymodel1 to this method signature I would have expected to be able to read the value I was setting in Controller A however I'm getting exceptions that the first model is no longer recognised.
So my question is: how can I read mymodel2 from Controller B?
You can't do that with #SessionAttributes :
Session attributes as indicated using this annotation correspond to a specific handlers model attributes, getting transparently stored in a conversational session. Those attributes will be removed once the handler indicates completion of its conversational session. Therefore, use this facility for such conversational attributes which are supposed to be stored in the session temporarily during the course of a specific handlers conversation.
For example I use this annotation when I want to validate elements with Hibernate validation, and after I submit the page and SOME elements are invalid I want the rest to be still on the page, but this is not your case. I think that the only way to do it would be with:
HttpSession.getAttribute()
The javadoc excerpt above is the most typical way #SessionAttributes is used. However, what Joly is describing should also work. Session attributes are stored via DefaultSessionAttributeStore, which by default does not prefix attribute names when it stores them in the session. That means if ControllerA and ControllerB both list an attribute called "mymodel1", they're actually referring to the same session attribute. You'll need to provide a little more information on the error you're getting and the actual controller code.

Resources