Spring WebFlow: view-state is not calling validation method before <evaluate> tag - spring

This problem is driving me crazy.
I have following view-state:
<on-start>
<evaluate expression="new com.zxxztech.zecure.services.webflow.FormularioConfirmacionCorreo()"
result="flowScope.ccForm" />
</on-start>
<view-state id="activacionManual" model="ccForm" >
<transition on="enviar" to="resultado" bind="true">
<evaluate expression="usersManager.activarUsuario(ccForm.correo, ccForm.codigo)"
result="flowScope.resultado" />
</transition>
<transition on="cancelar" to="cancelar" validate="false" bind="false" />
</view-state>
And this is this the Validation class:
#Component
public class FormularioConfirmacionCorreoValidator {
#Autowired
private UsersManager usersManager;
public void validateActivacionManual(FormularioConfirmacionCorreo ccForm, ValidationContext validContext) {
...
[Validation logic]
}
public UsersManager getUsersManager() {
return usersManager;
}
public void setUsersManager(UsersManager usersManager) {
this.usersManager = usersManager;
}
}
When form is submited, webflow execute <evaluate> tag directly, without calling validation method.
I don't know what could I doing wrong.
Edit:
This is the activacionManual.jsp file:
...
<form:form cssClass="ym-form" modelAttribute="ccf" method="post" action="${flowExecutionUrl}">
<form:errors cssClass="ym-error" element="div" path="*"/>
<div class="ym-box">
<div class="ym-fbox">
<label for="correo"><spring:message
code="activacion.form.correo.label"
text="activacion.form.correo.label" /></label>
<form:input path="correo" />
</div>
<div class="ym-fbox">
<label for="codigo"><spring:message
code="activacion.form.codigo.label"
text="activacion.form.codigo.label" /></label>
<form:input path="codigo" />
</div>
<div class="ym-fbox-footer ym-fbox-button">
<input class="ym-button ym-gr" type="submit"
value="<spring:message code="formulario.button.cancelar" text="formulario.button.cancelar" />"
name="_eventId_cancelar">
<input class="ym-button ym-primary ym-gr" type="submit"
value="<spring:message code="formulario.button.enviar" text="formulario.button.enviar" />"
name="_eventId_enviar">
</div>
</div>
</form:form>
...

The second way is to define a separate object, called a Validator, which validates your model object. To do this, first create a class whose name has the pattern ${model}Validator, where ${model} is the capitialized form of the model expression, such as booking. Then define a public method with the name validate${state}, where ${state} is the id of your view-state, such as enterBookingDetails.
Thus, since your model attribute is ccForm, the validator Class must be named CcFormValidator. (Or rename your model attribute.)
(Also, I think your JSP is going to have problems using modelAttribute="ccf" instead of "ccForm". The model name needs to match across flow.xml, JSPs, and validators.)

Related

Controller Not receiving value from span in HTML using Spring boot and Thymeleaf

I have the following content in my HTML which is using Thymeleaf
<form action="#" th:action="#{/shutDown}" th:object="${ddata}" method="post">
<span>Domain</span>
<span th:text="${domain}" th:field="*{domain}">domain</span>
<input type="Submit" value="close" />
</form>
And I have the following in my Controller which is using Sprint Boot
#RequestMapping(value = "/shutDown", method = RequestMethod.POST)
public ModelAndView shutDownPage(ModelAndView modelAndView, Authentication authentication,
#ModelAttribute("ddata") DInputBean dInputBean) {
String domain = dInputBean.getdomain();
return modelAndView;
}
I'm hoping I'd get value of domain from the HTML in the Controller but it's always null. DInputBean has getters and setters for "domain" field.
The th:field attribute can be used on <input>, <select>, or, <textarea>.
A solution you could possibly replacing you second <span> with a hidden input element.
<form action="#" th:action="#{/shutDown}" th:object="${ddata}" method="post">
<span>Domain</span>
<input type="hidden" th:field="*{domain}" th:value="${domain}" />
<input type="Submit" value="close" />
</form>
If you wanted to keep the second div, just place the <input type="hidden"> inside the second <span> and remove the th:field attribute from the second <span>.
Edit:
If you wanted to add the value of domain in a span.
<form action="#" th:action="#{/shutDown}" th:object="${ddata}" method="post">
<span>Domain</span>
<span th:text="${domain}">domain<span>
<input type="hidden" th:field="*{domain}" th:value="${domain}" />
<input type="Submit" value="close" />
</form>
http://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html#inputs
An option is to use a read-only input field:
<input type="text" th:field="*{domain}" th:value="${domain}" readonly="readonly"/>
This both displays the value and sends it on submit.
The key is to add the value of the domain variable to the form:
#GetMapping("/shutDownPage")
public String shutDownPage(Model model) {
model.addAttribute("ddata" new Ddata()); //or however you create your bean
String username = ... //however you get your username
String domain = myRepositoryService.findDomainByUsername(username);
model.addAttribute("domain", domain);
return "shutDownPage";
}
Include an HTML page in the action so that when you open the HTML page in a browser without a server/container, the button will still appear to work:
<form action="confirmationPage.html" th:action="#{/shutDown}" th:object="${ddata}" method="post">
<!-- You can benefit from using a conditional expression -->
<span th:text="${domain != null ? domain : 'No domain supplied'}">[domain]</span>
<input type="hidden" th:field="*{domain}" th:value="${domain}"/>
<input type="Submit" value="close"/>
</form>
And your post method:
#PostMapping("/shutDown") //use shorthand
public String shutDownPagePost(#ModelAttribute("ddata") DInputBean dInputBean {
String domain = dInputBean.getDomain();
//do whatever with it
return "confirmationPage";
}

How to validate just one field for register a user for spring project with hibernate validator?

<input name="id" placeholder="input your id"/>
<button id="id_check">check</button>
<input name="password" placeholder="input your id"/>
<input type="submit" value="register" />
Currently, I uses a controller for user.
Controller code following...
#Controller
public class UserController {
#GetMapping("...")
public String registerForm(...) { ... }
#PostMapping("...")
public String register(#ModelAttribute #Validated ...) { ... }
.....
}
I want to check id using ajax call.
For id duplication check, can I use hibernate validator? (for one field. not submit action) Also it should be validate duplication of id.(using dao).
For example, one field for validation should be checked duplication and validated by hibernate validator(#NotEmpty, #Email...)

Spring webflow partial validation not working

I am trying to implement partial validation in Spring Webflow 2.4.
I have looked at Spring webflow partial validation and the solution given there - but it does not work for me. Validation does not kick in at all when I add the groups (but works OK with no groups).
I have code and config as follows:
flow config
<view-state id="page1" view="apply/page1" model="myModel">
<binder>
<binding property="foo" />
</binder>
<transition on="next" to="page2" validate="true" validation-hints="'group1'"></transition>
</view-state>
<view-state id="page2" view="apply/page2" model="myModel">
<binder>
<binding property="baz" />
</binder>
<transition on="next" to="page3" validate="true" validation-hints="'group2'"></transition>
</view-state>
<view-state id="page3" view="apply/page3" model="myModel">
<on-render>
<set name="requestScope.foo" value="myModel.foo" />
<set name="requestScope.baz" value="myModel.baz" />
</on-render>
</view-state>
model class
public class MyModel implements Serializable {
#NotBlank(message = "Bad foo!", groups = {Group1.class})
private String foo;
#NotBlank(message = "Bad baz!", groups = {Group2.class})
private String baz;
...//getters and setters
public interface Group1 {
}
public interface Group2 {
}
}
html views like:
<form th:object="${myModel}" th:action="${flowExecutionUrl}" method="post">
<div class="error" th:if="${#fields.hasErrors('*')}">
<span th:each="err : ${#fields.errors('*')}">
<span th:text="${err}">Input is incorrect</span>
</span>
</div>
<input type="text" th:field="*{foo}"/>
<button type="submit" name="_eventId_next">Next</button>
</form>
and
<form th:object="${myModel}" th:action="${flowExecutionUrl}" method="post">
<div class="error" th:if="${#fields.hasErrors('*')}">
<span th:each="err : ${#fields.errors('*')}">
<span th:text="${err}">Input is incorrect</span>
</span>
</div>
<input type="text" th:field="*{baz}"/>
<button type="submit" name="_eventId_next">Next</button>
</form>
My intention is to use a single model for the backing form across a number of views that make up a wizard flow - doing partial validation as I go along. But when I introduce the groups needed to achieve this, the validation does not trigger at any point in the flow.
If I put typos in the validation hint names, I get errors - eg:
<transition on="next" to="page2"
validate="true" validation-hints="'group1zzzz'"></transition>
results in
Failed to resolve validation hint [group1zzzz]
so the groups are being picked up. But validation is not happening, and I can walk through the data-entry without triggering the errors.
I am using spring-webflow 2.4.2.RELEASE
Any ideas if I am missing something, or if this just does not work?
Update
Iv'e done some digging into the code for org.hibernate.validator.internal.engine.ValidatorImpl - where it decides if validation is required:
private boolean isValidationRequired(ValidationContext<?> validationContext,
ValueContext<?, ?> valueContext,
MetaConstraint<?> metaConstraint) {
if ( validationContext.hasMetaConstraintBeenProcessed(
valueContext.getCurrentBean(),
valueContext.getPropertyPath(),
metaConstraint
) ) {
return false;
}
if ( !metaConstraint.getGroupList().contains( valueContext.getCurrentGroup() ) ) {
return false;
}
return isReachable(
validationContext,
valueContext.getCurrentBean(),
valueContext.getPropertyPath(),
metaConstraint.getElementType()
);
}
at:
if ( !metaConstraint.getGroupList().contains( valueContext.getCurrentGroup() ) ) {
return false;
}
I see my instances of my group - eg 'MyModel$Group2' in both collections, but they appear not to be the same object instance, therefore the 'Collection.contains()' fails, and isValidationRequired() always returns false. See screenshots.
[![enter image description here][2]][2]
Agggggggggghhhhhhh - found what appears to be the cause of the issue
With spring-boot-devtools in the mix, there appears to be another classloader getting involved in loading other instances of the group marker interfaces. That is making the 'Collection.contains()' above fail every time.
If I take out spring-boot-devtools from my build stack, it works. The question now becomes:
"how to get validation groups working with spring-boot-devtools in the mix?"
Well, after all that, it appears that if you have spring-boot-devtools in the mix, that webflow causes some problems with the classloaders. Fix appears to be to create file:
/WEB-INF/spring-devtools.properties
and add webflow jar to the include list:
restart.include.webflow=/spring-webflow-2.4.2.RELEASE.jar
That seems to make webflow use the same devetools classloader, and validation now kicks-in OK when looking for the validation-hints interfaces.

Spring MVC validation errors are not displayed in Freemarker template

I'm trying to display validation errors in a 'user registration' page built with freemarker template if a controller returns binding errors.
My controller's code is as follows:
#Controller
#RequestMapping("/")
public class UserController {
#Autowired
private UserService userService;
#Autowired
private SecurityService securityService;
#Autowired
private UserValidator userValidator;
#RequestMapping(value = "/registration", method = RequestMethod.GET)
public String registration(Model model) {
model.addAttribute("userForm", new User());
return "registration";
}
#RequestMapping(value = "/registration", method = RequestMethod.POST)
public String registration(#ModelAttribute("useraccount") User userForm, BindingResult bindingResult, Model model) {
userValidator.validate(userForm, bindingResult);
if (bindingResult.hasErrors()) {
return "registration";
}
userService.save(userForm);
securityService.autologin(userForm.getUsername(), userForm.getPasswordConfirm());
return "redirect:/explore";
}
while this is the registration.ftl freemarker template I am trying to build :
<div>
<fieldset>
<h1>Create your Account</h1>
<form id="regForm" class="idealform" action="registration" method="post" name='useraccount'>
Username: <input type="text" name="username" /> <errors path="username" cssClass="error"/><br/>
Password: <input type="text" name="password" /><errors path="password" cssClass="error"/><br/>
<label class="main-label" style="width: 91px;"> </label>
<input type="submit" value="submit">
</form>
</fieldset>
I tried also the solution recommended here:
Displaying Spring MVC validation errors in Freemarker templates
and the registration.ftl becomes:
<#assign form=JspTaglibs["http://www.springframework.org/tags/form"] />
<#macro formErrors>
<#assign formErrors><#form.errors path="*" /></#assign>
<#if formErrors?has_content>
<div id="errors">
<#spring.message "admin.error.globalMessage" />
</div>
</#if>
</#macro>
<div>
<fieldset>
<h1>Create your Account</h1>
<#form.form id="regForm" class="idealform" action="registration" method="post" name='useraccount'>
Username: <input type="text" name="username" path="username" /> <br/>
Password: <input type="text" name="password" path="password" /><br/>
<#formErrors />
<label class="main-label" style="width: 91px;"> </label>
<input type="submit" value="submit">
</#form.form>
</fieldset>
</div>
but still the validation messages are not displayed.
Could you share your thoughts with me on this issue?
Thank you very much.
I rewrote your controller code to something like this:
#Controller
#RequestMapping("/")
public class UserController {
#Autowired
private UserService userService;
#Autowired
private SecurityService securityService;
#Autowired
private UserValidator userValidator;
#RequestMapping(value = "/registration", method = RequestMethod.GET)
public String registration(#ModelAttribute(name = "userForm") User user) {
return "registration";
}
#RequestMapping(value = "/registration", method = RequestMethod.POST)
public String registration(#ModelAttribute(name = "userForm") User user, BindingResult result) {
userValidator.validate(user, result);
if (result.hasErrors()) {
return "registration";
}
userService.save(user);
securityService.autologin(user.getUsername(), user.getPasswordConfirm());
return "redirect:/explore";
}
}
1) There is no need to use model.addAttribute("modelName", model) if you use a constructor without arguments, instead you can use a #ModelAttribute annotation specifying the name attribute (by default the name goes from the class name). You only have to be sure that this model is in a consistent state. Also you need to pass the name exactly the same as you use in your view (freemarker template).
Now "registration.ftl"
...
<#import "/spring.ftl" as spring/>
...
<fieldset>
<h1>Create your Account</h1>
<form id="regForm" class="idealform" action="<#spring.url '/registration'/>" method="post">
<#spring.bind 'userForm.username'/>
Username: <input type="text" name="${spring.status.expression}" value="${spring.status.value?html}"/>
<#list spring.status.errorMessages as error>
<span class="error">${error}</span>
<br>
</#list>
<#spring.bind 'userForm.password'/>
Password: <input type="password" name="${spring.status.expression}" value="${spring.status.value?html}"/>
<#list spring.status.errorMessages as error>
<span class="error">${error}</span>
<br>
</#list>
<label class="main-label" style="width: 91px;"> </label>
<input type="submit" value="submit">
</form>
</fieldset>
...
1)You need <#import "/spring.ftl" as spring/> in order to add spring's user-defined directives that are pretty useful.
2)Use <#spring.bind 'userForm.username'> directive to bind following input to your model. Here "userForm" is your model and "username" is a field that you want to bind. This directive also declares new variable "spring.status" that contains an "expression" variable - for a path, a "value" - to populate the form in case it's returned with errors, and "errorMessages" from BindingResult.
3)If you use a message source to support different languages you should change <span class="error">${error}</span> to something like <#spring.message '${error}'/> otherwise you'll get just message codes.
Hope it helps.

binding to list property of an object returns nothing mvc3

Context: ASP MVC 3/4, VB.net
I am trying to bind to an object that has a list property of complex type.
Problem statement: upon post back I get values in all the properties other than the complex type property, it is returned as nothing. I am doing something wrong because of which default model binder is not able to bind my property which is a list of complex type.
What am i doing wrong?
My View Model classes:
Public Class Customer
Public Property ID As Integer
Public Property FirstName As String
Public Property LastName As String
Public Appointments As IList(Of Appointment)
End Class
Public Class Appointment
Public Property ClientName As String
Public Property [Date] As DateTime
Public Property TermsAccepted As Boolean
End Class
My action methods in controller look like this
Function CreateCusotmerWithMultipleBookings() As ActionResult
Dim Modeldata As New Customer With {.ID = 1, .FirstName = "First Name", .LastName = "Last Name"}
Modeldata.Appointments = New List(Of Appointment) From {New Appointment, New Appointment, New Appointment}
Return View(Modeldata)
End Function
<HttpPost()> _
Function CreateCusotmerWithMultipleBookings(FormData As Customer) As ActionResult
Return View(FormData)
End Function
My view looks like this:
#ModelType Customer
#Using Html.BeginForm
#Html.EditorForModel
If Model.Appointments IsNot Nothing AndAlso Model.Appointments.Count > 0 Then
For i As Integer = 0 To Model.Appointments.Count - 1
#:<b >Appointment #i</b><br />
#Html.TextBoxFor(Function(x) x.Appointments(i).ClientName) #:<br />
#Html.TextBoxFor(Function(x) x.Appointments(i).Date) #:<br />
#Html.TextBoxFor(Function(x) x.Appointments(i).TermsAccepted)#:<br />
Next
End If
End Using
HTML that gets generated from this view is something like: (I have removed unnecessary mark up to ease reading)
<body>
<form action="/testarea/Appointment/CreateCusotmerWithMultipleBookings" method="post">
<input id="ID" name="ID" type="number" value="1" />
<input class="text-box single-line" id="FirstName" name="FirstName" type="text" value="First Name" />
<input class="text-box single-line" id="LastName" name="LastName" type="text" value="Last Name" />
<input id="Appointments_0__ClientName" name="Appointments[0].ClientName" type="text" value="" />
<input id="Appointments_0__Date" name="Appointments[0].Date" type="text" value="1/1/0001 12:00:00 AM" />
<input id="Appointments_0__TermsAccepted" name="Appointments[0].TermsAccepted" type="text" value="False" />
<input id="Appointments_1__ClientName" name="Appointments[1].ClientName" type="text" value="" />
<input id="Appointments_1__Date" name="Appointments[1].Date" type="text" value="1/1/0001 12:00:00 AM" />
<input id="Appointments_1__TermsAccepted" name="Appointments[1].TermsAccepted" type="text" value="False" />
<input id="Appointments_2__ClientName" name="Appointments[2].ClientName" type="text" value="" />
<input id="Appointments_2__Date" name="Appointments[2].Date" type="text" value="1/1/0001 12:00:00 AM" />
<input id="Appointments_2__TermsAccepted" name="Appointments[2].TermsAccepted" type="text" value="False" />
<input type="submit" value="Create customer with multiple appointments" />
</form>
Question again:
Why does the formdata.Appointments in following controller has nothing value in it?
<HttpPost()> _
Function CreateCusotmerWithMultipleBookings(FormData As Customer) As ActionResult
Return View(FormData)
End Function

Resources