Spring MVC Validation custom message - spring

my problem is that I have a form which has html select element with some choosing option value & I want to validate those value using :
org.hibernate.validator.constraints
or
javax.validation.constraints
annotations.
here you may see my form & my select element:
<form:form action="../agents/add" method="POST" commandName="myAgent">
<form:select path="state">
<form:option value="ACTIVE" path="state">ACTIVE</form:option>
<form:option value="LISTEN" path="state">LISTEN</form:option>
<form:option value="DOWN" path="state">DOWN</form:option>
</form:select>
</form:form>
I have defined my controller method like this:
#RequestMapping(value = "agents/add", method = RequestMethod.POST)
public String addAgentSubmit(#ModelAttribute("myAgent") #Valid final AgentValidator agent, BindingResult result, RedirectAttributes redirect) {
if (result.hasErrors()) {
return "admin/agent/add";
}
...
}
and I also define a ModelAttribute like this:
#ModelAttribute("myAgent")
public AgentValidator getLoginForm() {
return new AgentValidator();
}
Here is my AgentValidator class also:
public class AgentValidator {
#NotEmpty(message = "your state can not be empty !")
private AgentState state;
getter & setter ...
}
and here is my AgentState:
public enum AgentState {
ACTIVE, DOWN, PAUSED
}
when I input a wrong value to my form (something like this):
<form:form action="../agents/add" method="POST" commandName="myAgent">
<form:select path="state">
<form:option value="ACTIVE!NVALID" path="state">ACTIVE</form:option>
<form:option value="LISTEN" path="state">LISTEN</form:option>
<form:option value="DOWN" path="state">DOWN</form:option>
</form:select>
</form:form>
after I submit my form I don't have a custom message showing in my JSP, instead I will see this message:
Failed to convert property value of type java.lang.String to required type tm.sys.validator.AgentState for property state; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type #javax.validation.constraints.NotNull tm.sys.validator.AgentState for value ACTIVE!NVALID; nested exception is java.lang.IllegalArgumentException: No enum constant tm.sys.validator.AgentState.ACTIVE!NVALID
I searched a lot for this problem but none of the solution helped me to provide my customized message showing to the user.
If you have any solution for this please provide it complete because I'm not so advance programmer yet.

You need to add one of the following keys: typeMismatch, typeMismatch.state or typeMismatch.agentValidator.state into your Messages.properties (or ValidationMessages.properties - whichever messages properties file you have configured with your validator).
typeMismatch.state=You provided invalid state
Exact key name(s) that you need add to message properties can be found from BindingResult.
Take a look at result.getFieldError().getCodes() (this should contain codes for the first field error if there is any).
This will override default message (the one which you are seeing) for situations when form submitted value cannot be converted to required type (enum in your case).

Related

Thymeleaf nested object form binding not generating good input name

I have a following object structure:
public class A{
private int id;
private B objB;
public A(){}
--- setters & getters ---
}
This is my class B:
public class B{
private int id;
private int test;
public B(){}
--- setters & getters ---
}
Im passing an A object to my view and i want to create a form to my B object:
<form id="bForm" th:object=${A.objB} th:action="#{/save}">
<input th:field=*{test} type="text"/>
</form>
However the above code will work it will generate the input name like this: objB.test and for this my controller cannot bind it to a B object.
This is the receiving method in my controller:
#RequestMapping("/save")
#ResponseBody
public String setB(#ModelAttribute("bForm") B b, BindingResult result) {
aService.setB(b);
return "...";
}
How can i set Thymeleaf to name my fields without the prefix so instead of: objB.test just test?
Any help is greatly appreciated.
According to thymeleaf-spring docs:
Values for th:object attributes in form tags must be variable expressions (${...}) specifying only the name of a model attribute, without property navigation. This means that an expression like ${seedStarter} is valid, but ${seedStarter.data} would not be.
Try using a th:with attribute before your form to assign your A.objB variable and, therefore, avoid using property navigation in the th:object attribute.
It seems that to adhere to this requirement you have to set an additional model attribute in your Spring controller's handler method that is handling the initial form rendering. So given that you already have something like this in some part of your code:
model.addAttribute("A", new A());
add another line:
model.addAttribute("bForm", new B());
Keep in mind to keep the model attribute name consistent with the one you had set in #ModelAttribute annotation in your submission request handler you've posted in your question.
Maybe a late answer, but if objectB is inner object of A, then you can bind fields to B as inner object of A and process objectA in your controller.
It would look something like this, but in my case prompt is Arraylist of contained objects here:
<th:block th:each="pr, stat : ${taskExecution.prompt}">
<input type="hidden" id="hiddenName" th:field="*{prompt[__${stat.index}__].name}" th:value="${pr.name}">
<input type="hidden" id="hiddenValue" th:field="*{prompt[__${stat.index}__].value}" th:value="${pr.value}">
</th:block>

Spring: #ModelAttribute VS #RequestBody

Please correct me if I am wrong.
Both can be used for Data Binding.
The question is when to use #ModelAttribute?
#RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(#ModelAttribute Pet pet) { }
In addition, when to use #RequestBody?
#RequestMapping(value = "/user/savecontact", method = RequestMethod.POST
public String saveContact(#RequestBody Contact contact){ }
According to my understanding both serves the similar purpose.
Thanks!!
The simplest way for my understanding is, the #ModelAttribute will take a query string. so, all the data are being pass to the server through the url.
As for #RequestBody, all the data will be pass to the server through a full JSON body.
#ModelAttribute is used for binding data from request param (in key value pairs),
but #RequestBody is used for binding data from whole body of the request like POST,PUT.. request types which contains other format like json, xml.
If you want to do file upload, you have to use #ModelAttribute. With #RequestBody, it's not possible. Sample code
#RestController
#RequestMapping(ProductController.BASE_URL)
public class ProductController {
public static final String BASE_URL = "/api/v1/products";
private ProductService productService;
public ProductController(ProductService productService) {
this.productService = productService;
}
#PostMapping
#ResponseStatus(HttpStatus.CREATED)
public ProductDTO createProduct(#Valid #ModelAttribute ProductInput productInput) {
return productService.createProduct(productInput);
}
}
ProductInput class
#Data
public class ProductInput {
#NotEmpty(message = "Please provide a name")
#Size(min = 2, max = 250, message = "Product name should be minimum 2 character and maximum 250 character")
private String name;
#NotEmpty(message = "Please provide a product description")
#Size(min = 2, max = 5000, message = "Product description should be minimum 2 character and maximum 5000 character")
private String details;
#Min(value = 0, message = "Price should not be negative")
private float price;
#Size(min = 1, max = 10, message = "Product should have minimum 1 image and maximum 10 images")
private Set<MultipartFile> images;
}
I find that #RequestBody (also annotating a class as #RestController) is better for AJAX requests where you have complete control over the contents of the request being issued and the contents are sent as either XML or JSON (because of Jackson). This allows the contents to easily create a model object. Conversely, #ModelAttribute seems to be better suited to forms where there is a "command" object backing a form (which may not necessarily be a model object).
You can directly access your "pet" object in view layer, if you use ModelAttribute annotation. Also, you can instantiate this object in a method on your controller to put your model. see this.
ModelAttribute gives you a chance to use this object partial, but with RequestBody, you get all body of request.
I think #ModelAttribute and #RequestBody both are having same use, only difference
is #ModelAttribute use for normal spring MVC and #RequestBody use for REST web service. It is #PathVariable and #PathParam. But in in both the cases we can mix it. we can use #PathVariable in REST and vice versa.
With #ModelAttribute, you pass data in URL params and with #RequestBody you pass it as JSON body. If you're making a REST API then it's better to use #RequestBody. Over most youtube tutorials you might find use of #ModelAttribute - That's simply because they might be demonstrating concepts regarding Spring MVC and are using URL's to pass data.
We need to have the following jsp tag to data bind your entity to the jsp form fields:
The form is from the spring tag library:
The following is the not the full html, but I hope you can relate your self:
<%#taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<form:form action="save" method="post" modelAttribute="patient">
<table>
<tr>
<td>Name</td>
<td>
<form:input path="patient.patient_name" /> <br />
</td>
</tr>
<tr>
<td>Phone</td>
<td>
<form:input path="patient.phone_number" /> <br />
</td>
</tr>
<tr>
<td colspan="2"><button type="submit">Submit</button></td>
</tr>
</table>
</form:form>
The form has to be processed twice , once before rendering the form, during which we need to give the appropriate bean instantiation for the property value modelAttribute="patient".
For this the controller class(at the class defintion level) you need to have #RequestMapping annotation.
You need to have the handler method parameters as follows
#GetMapping("logincreate")
public String handleLoginCreate(#ModelAttribute("login") Login login, Model model)
{
System.out.println(" Inside handleLoginCreate ");
model.addAttribute("login",login);
return "logincreate";
}
Spring will scan all handler methods #ModelAttribute and instantiate it with default constructor of Login class, and call all of its getters and setters (for the jsp binding from form to the "login"). In case of missing any of the following the jsp will not be shown, various exceptions are thrown
getters/setters
default constructor
model.addAttribute("login",login);
class level #RequestMapping
method parameter level #ModelAttribute
Also, the handler method of action in the jsp, the in the above form action="save", also the handler method might look like this:
#PostMapping("save")
public String saveLoginDetails(#ModelAttribute("login") Login login, Model model) {
//write codee to insert record into DB
System.out.println(" Inside save login details ");
System.out.println("The login object is " + login.toString());
System.out.println("The model object contains the login attribute"+ model.getAttribute("login"));
loginService.saveLogin(login);
return "welcome";
}
Important learning is:
Before form is launched, spring should have appropriate annotation to indicate the backing bean of the form, in the above example the "backing bean" or "binding object" is Login login with appropriate handler method's parameter annotation #ModelAttribute("login") Login login

Data binding without using spring taglib

My html is built without using the spring taglib and now I'd like to bind the parameters of the form to a object in my controller.
Currently my form looks like this
<form>
<input type="text" name="frAccUserMgmt.userName"/>
<input type="password" name="frAccUserMgmt.userPwd"/>
</form>
The relevant part of my object is
Class FrAccUserMgmt {
private String userName;
private Strint userPwd;
// getter and setter
}
My controller is
#RequestMapping("login")
Public ModelAndView doLogin(FrAccUserMgmt frAccUserMgmt) {
//code
}
How do I go about binding it. Currently the binding doesn't happen. I just get an empty object in my code.
You could try including the BindingResult class in the method signature and then see if there are any binding errors:
#RequestMapping("login")
Public ModelAndView doLogin(FrAccUserMgmt frAccUserMgmt, BindingResult result) {
if (result.hasErrors()) {
logger.warn("BindingResult errors: " + result.toString());
}
//code
}
Remove the frAccUserMgmt part from the form field names. Spring will automatically find the command object to bind the request parameters based on the getters and setters defined in the command object.
This can also be done by adding the #ModelAttribute for the parameter bean that should be populated with the request parameters.
As per spring docs
http://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/mvc.html#mvc-ann-modelattrib-method-args
(16.3.3.8 Using #ModelAttribute on a method argument)
An #ModelAttribute on a method argument indicates the argument should be retrieved from the model. If not present in the model, the argument should be instantiated first and then added to the model. Once present in the model, the argument's fields should be populated from all request parameters that have matching names. This is known as data binding in Spring MVC, a very useful mechanism that saves you from having to parse each form field individually.

Spring MVC Data Binding Error

I'm getting the following error when I try to retrieve the form results in controller method:
org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'search' on field 'clients': rejected value [14]; codes [typeMismatch.search.clients,typeMismatch.clients,typeMismatch.java.util.List,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [search.clients,clients]; arguments []; default message [clients]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.List' for property 'clients'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [com.Client] for property 'clients[0]': no matching editors or conversion strategy found]
Model Object looks like this:
public class SearchForm {
private String name;
private List<Client> clients;
//getters and setters
}
public class Client {
private String name;
private Int id;
}
form:
<form:form method="POST", name="searchresults.html" modelattibute="search">
<form:input path="name"/>
<form:checkboxes path="clients" items="{clientsList}" itemsValue="id" itemsLabel="name"/>
</form:form>
this form displays the values correctly on the html page but when I click the submit button I get the above error
The browser will only send a list of client IDs when the form is submitted. How could Spring know how to convert each ID into a Client instance. You either have to tell it how to do, or add a List<String> clientIds property to your bean, and make the form:checkboxes tag use this property instead of clients as its path.

How do I set the selected value in a Spring MVC form:select from the controller?

In my controller:
#Controller
public class UserController {
#RequestMapping(value="/admin/user/id/{id}/update", method=RequestMethod.GET)
public ModelAndView updateUserHandler(#ModelAttribute("userForm") UserForm userForm, #PathVariable String id) {
Map<String, Object> model = new HashMap<String, Object>();
userForm.setCompanyName("The Selected Company");
model.put("userForm", userForm);
List<String> companyNames = new ArrayList<String>();
companyNames.add("First Company Name");
companyNames.add("The Selected Company");
companyNames.add("Last Company Name");
model.put("companyNames", companyNames);
Map<String, Map<String, Object>> modelForView = new HashMap<String, Map<String, Object>>();
modelForView.put("vars", model);
return new ModelAndView("/admin/user/update", modelForView);
}
}
The select form field in my view:
<form:form method="post" action="/admin/user/update.html" modelAttribute="userForm">
<form:select path="companyName" id="companyName" items="${vars.companyNames}" itemValue="id" itemLabel="companyName" />
</form:form>
It was my understanding that the form backing bean would be mapped based upon the modelAttribute attribute in the form. I'm obviously missing something here.
It appears the issue was not related to my setup. The problem was that the itemValue was set to the company id property, while the comparison was being done to the company name property on my form backing bean. So the two were not equal, and therefore, no item was set to selected.
The above code works just fine, and setting the value in the userForm for a particular property will set that value as selected in select form fields so long as the value of one of the items in the items collection is equal to the form value. I changed my form field to look like this, which pulls the companyName instead of the id.
<form:form method="post" action="/admin/user/update.html" modelAttribute="userForm">
<form:select path="companyName" id="companyName" items="${vars.companyNames}" itemValue="companyName" itemLabel="companyName" />
</form:form>
The easiest solution is to override the toString() method in the model class.
In this case just change the class UserForm by overriding toString() like below:
#Override
public String toString() {
return this.getCompanyName();
}
Spring then will automatically select the correct value in form:option
I was struggling some time on the same issue.
This is the select field I had
<form:select path="origin" items="${origins}" itemValue="idOrigin" itemLabel="name" />
Since I had a PropertyEditor in place for my entity I couldn't write something like
<form:select path="origin.idOrigin" items="${origins}" itemValue="idOrigin" itemLabel="name" />
that worked fine, but was not parsed by the PropertyEditor.
So, thinking about the Spring's need to determine equality between entities, I came out implementing equals and hashcode in my Origin entity using only the idOrigin property, and it worked!
You can also try like this
<form:select id="selectCategoryId" path="categoryId"
class="selectList adminInput admin-align-input" multiple="">
<option value="0">-- Select --</option>
<c:forEach items="${categories}" var="category">
<option <c:if test="${category.key eq workflowDTO.categoryId}">selected="selected"</c:if> value="${category.key}">${category.value} </option>
</c:forEach>
</form:select>
it is not so complicated. You need 2 beans: a form backing bean and a select model in your domain model.
Here is my model, a list of strings, for :
/* in controller: my select model is a list of strings. However, it can be more complicated, then you had to use PropertyEditors for String <-> Bean conversions */
List<String> mySelectValues = new ArrayList<String>();
mySelectValues.add("M");
mySelectValues.add("F");
modelMap.addAttribute("mySelectValues", mySelectValues);
Here is your form, basically :
<form:form command="user">
<form:select path="gender">
<form:options items="${mySelectValues}"></form:options>
</form:select>
</form:form>
und here is my backing object:
public class User {
private String gender;
/* accessors */
}
Spring framework selects automaticaly using value of "gender" field.

Resources