I got some trouble to get spring validation based on annotation form works.
i added this to my spring-servlet.xml
<context:component-scan base-package="com.it.controller" />
(package containing all my controller)
and
<context:component-scan base-package="com.it.form" />
(package containing all my form classes)
class email in package com.it.form :
public class email {
#NotEmpty
#Email
private String email;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public email() {
email = "";
// TODO Auto-generated constructor stub
}
}
Form :
<body>
<form:form method="post" action="" commandName='email'>
<div class="requestEmail">
<form:label path="email">Entrez votre email:</form:label>
<form:input path="email" />
<form:errors path="email" />
</div>
<div>
<input type="submit" value="VALIDER" />
</div>
</form:form>
Controller :
#Controller
#SessionAttributes
public class passwordController {
/*
* ##########################################################
*
* Print & valid form Email
*
* ##########################################################
*/
#RequestMapping(value = "/passwordChange.mvc", method = RequestMethod.GET)
public String get(final ModelMap model) {
email email= new email();
model.addAttribute("email", email);
return "passwordChangeRequestEmail"; // jsp form
}
#RequestMapping(value = "/passwordChange.mvc", method = RequestMethod.POST)
public String post(final ModelMap model,
#ModelAttribute("email") #Valid final email email,
final BindingResult result) {
if (result.hasErrors()) {
return "error";
}
return "success";
}
}
it seems when i submit my form i am always redirect to /success page, even if i leave email input blank...
Dunno if i missed something
Thanks in advance :)
You have to add
<mvc:annotation-driven />
to your servlet context file and to import a validator like Hibernate Validator in your classpath.
Related
I have a thymeleaf form that is throwing the following exception after it's submitted
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute
I've read a couple of answers which suggests putting a BindingResult directly after the model attribute in the controller method but this doesn't seem to have solved it. Here's what I have
<form action="#" th:action="#{/capturedetails}" th:object="${command}" method="post">
<div class="form-group" th:if="${mobilePhone} == null">
<label for="mobilePhone">Mobile Phone</label> <input
type="tel" class="form-control"
id="mobilePhone"
placeholder="Mobile Phone no." th:field="*{mobilePhone}"></input>
</div>
<div class="form-group" th:if="${secondEmail} == null">
<label for="secondEmail">Secondary Email</label>
<input type="email" class="form-control"
id="secondEmail" placeholder="Secondary Email" th:field="*{secondEmail}"></input>
</div>
<button type="submit">Submit</button>
</form>
The controller method
#PostMapping(value = "/capturedetails")
public String updateProfile(#ModelAttribute("command") CaptureDetailsFormCommand command, BindingResult bindingResult, Model model) {
model.addAttribute("command", command);
return "redirect: someWhere";
}
And the command object
public class CaptureDetailsFormCommand {
private String mobilePhone;
private String secondEmail;
public String getMobilePhone() {
return mobilePhone;
}
public void setMobilePhone(String mobilePhone) {
this.mobilePhone = mobilePhone;
}
public String getSecondEmail() {
return secondEmail;
}
public void setSecondEmail(String secondEmail) {
this.secondEmail = secondEmail;
}
}
OK in my usual style, solved it myself. The problem was actually in the Get Mapping not the post mapping eg. I needed
#GetMapping(value = "/capturedetails")
public ModelAndView captureDetails() {
ModelAndView mav = new ModelAndView("capturedetails");
mav.addObject("command", new CaptureDetailsFormCommand());
return mav;
}
Add the name of the model attribute to your form like so:
<form action="#" modelAttribute="command" ...
And check the bindingResult has no errors like so:
#PostMapping(value = "/capturedetails")
public String updateProfile(#ModelAttribute("command") CaptureDetailsFormCommand command, BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors()) {
return "error"; //This should return some kind of error
}
....
When i run app in console, result is correct. Parent is updating, child not delete.
app.java
public class app {
public static void main(String[] args) {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("classpath:app-context-annotation.xml");
ctx.refresh();
ClientService clientService = ctx.getBean("jpaClientService", ClientService.class);
UserService userService = ctx.getBean("jpaUserService", UserService.class);
CustomerService customerService = ctx.getBean("jpaCustomerService", CustomerService.class);
TroubleService troubleService = ctx.getBean("jpaTroubleService", TroubleService.class);
Client newClient = clientService.findOne(8l);
String newClientName = "IP 8";
newClient.setClient(newClientName);
clientService.saveClient(newClient);
clientList("Find all:", clientService.findAll());
System.out.println(client.getUserList());*/
}
private static void clientList (String message, List<Client> clientList) {
System.out.println(message);
for (Client client : clientList) {
System.out.println(client);
}
}
private static void userList (String message, List<User> userList) {
System.out.println(message);
for (User user : userList) {
System.out.println(user);
}
}
private static void troubleList (String message, List<Trouble> troubleList) {
System.out.println(message);
for (Trouble trouble : troubleList) {
System.out.println(trouble);
}
}
}
But, when i using MVC, when the parent is updating all childs was delete from all tables.
edit.jspx
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:spring="http://www.springframework.org/tags"
xmlns:form="http://www.springframework.org/tags/form"
xmlns="http://www.w3.org/1999/xhtml" version="2.0">
<jsp:directive.page contentType="text/html;charset=UTF-8"/>
<jsp:output omit-xml-declaration="yes"/>
<spring:message code="label_client_info" var="labelClientInfo"/>
<spring:message code="label_client_client" var="labelClient"/>
<spring:message code="label_client_id" var="labelId"/>
<spring:message code="label_client_inn" var="labelInn"/>
<spring:message code="label_client_specific" var="labelClientSpecific"/>
<spring:message code="label_client_address" var="labelClientAddress"/>
<spring:message code="label_client_phone" var="labelClientPhone"/>
<spring:message code="label_client_update" var="labelClientUpdate"/>
<spring:message code="label_client_new" var="labelClientNew"/>
<spring:eval expression="client.id == null ? labelClientNew:labelClientUpdate" var="formTitle"/>
<html>
<head><title>${formTitle}</title></head>
<body>
<h1>${formTitle}</h1>
<div id="contactUpdate">
<form:form modelAttribute="client" id="clientUpdateForm" method="post">
<c:if test="${not empty message}">
<div id="message" class="${message.type}">"${message.message}"</div>
</c:if>
<form:label path="client">
${labelClient}
</form:label>
<form:input path="client"/>
<div>
<form:errors path="client" cssClass="error"/>
</div>
<form:label path="inn">
${labelInn}
</form:label>
<form:input path="inn"/>
<div>
<form:errors path="inn" cssClass="error"/>
</div>
<form:label path="address">
${labelClientAddress}
</form:label>
<form:input path="address"/>
<div>
<form:errors path="address" cssClass="error"/>
</div>
<form:label path="phone">
${labelClientPhone}
</form:label>
<form:input path="phone"/>
<div>
<form:errors path="phone" cssClass="error"/>
</div>
<form:label path="clientSpecific">
${labelClientSpecific}
</form:label>
<form:select path="clientSpecific">
<form:options items="${clientSpecific}"/>
</form:select>
<form:hidden path="version"/>
<br/>
<button type="submit">Save</button>
<button type="reset">Reset</button>
</form:form>
</div>
</body>
</html>
</jsp:root>
ClientController
#RequestMapping("/clients")
#Controller
public class ClientController {
private Log log = LogFactory.getLog(ClientController.class);
private ClientService clientService;
private MessageSource messageSource;
#RequestMapping(method = RequestMethod.GET)
public String list (Model uiModel) {
log.info("Listing clients");
List<Client> clients = clientService.findAll();
uiModel.addAttribute("clients", clients);
log.info("No. of Clients: " + clients.size());
for (Client client : clients) {
System.out.println(client);
}
return "clients/list";
}
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String show(#PathVariable("id") Long id, Model uiModel){
Client client = clientService.findOne(id);
uiModel.addAttribute("client", client);
uiModel.addAttribute("users", client.getUserList());
return "clients/show";
}
#RequestMapping(value = "/{id}", params = "form", method = RequestMethod.POST)
public String update(Client client, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest, RedirectAttributes redirectAttributes, Locale locale){
log.info("Updating client:");
if(bindingResult.hasErrors()){
uiModel.addAttribute("message", new Message("error", messageSource.getMessage("client_save_fail", new Object[]{}, locale)));
uiModel.addAttribute("client", client);
return "clients/update";
}
uiModel.asMap().clear();
redirectAttributes.addFlashAttribute("message", new Message("success", messageSource.getMessage("client_save_success", new Object[]{}, locale)));
clientService.saveClient(client);
return "redirect:/clients/"+ UrlUtil.encodeUrlPathSegment(client.getId().toString(), httpServletRequest);
}
#RequestMapping(value = "/{id}", params = "form", method = RequestMethod.GET)
public String updateForm(#PathVariable("id") Long id, Model uiModel){
uiModel.addAttribute("client", clientService.findOne(id));
return "clients/update";
}
#RequestMapping(params = "form", method = RequestMethod.POST)
public String create(Client client, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest, RedirectAttributes redirectAttributes, Locale locale){
log.info("Creating Client:");
if(bindingResult.hasErrors()){
uiModel.addAttribute("message", new Message("error", messageSource.getMessage("client_create_fail", new Object[]{}, locale)));
uiModel.addAttribute("client", client);
}
uiModel.asMap().clear();
redirectAttributes.addFlashAttribute("message", new Message("success", messageSource.getMessage("client_create_success", new Object[]{}, locale)));
clientService.saveClient(client);
log.info("Client ID: " + client.getId() + "was created success");
return "redirect:/clients/" + UrlUtil.encodeUrlPathSegment(client.getId().toString(), httpServletRequest);
}
#RequestMapping(params = "form", method = RequestMethod.GET)
public String createForm(Model uiModel){
Client client = new Client();
uiModel.addAttribute(client);
return "clients/create";
}
#Autowired
public void setClientService(#Qualifier("jpaClientService") ClientService clientService){
this.clientService = clientService;
}
#Autowired
public void setMessageSource(MessageSource messageSource){
this.messageSource = messageSource;
}
}
What am i doing wrong?
app-context == webapp-context
Solved. Deleted "orphanremoval=true" from entities.
I am creating an application with Spring Roo for generate documents. First of all the user create a document:
public class Document {
#NotNull
private String titleDocument;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "relatedDocumentToThisDateField")
private Set<DateField> dateFields = new HashSet<DateField>();
#OneToMany(cascade = CascadeType.ALL, mappedBy = "relatedDocumentToThisRadioButton")
private Set<RadioButtonField> radioButtonFields = new HashSet<RadioButtonField>();
#OneToMany(cascade = CascadeType.ALL, mappedBy = "relatedDocumentToThisStringField")
private Set<StringField> stringFields = new HashSet<StringField>();
}
There are 3 types of fields, for simplify, this is StringField (the others are almost the same):
public class StringField extends Field {
#NotNull
private String valueString;
#NotNull
private Boolean isEditable;
#ManyToOne
private Document relatedDocumentToThisStringField;
#NotNull
private String nameStringField;
}
When the document with fields is created, another user has to fill it. Since I dont know how much fields the document will have I need to create a HashMap Form (I am following this tutorial)
My HashMap is:
public class DynamicForm {
private Map<String, String> dynamicMap=new HashMap<String, String>();
public Map<String, String> getDynamicMap() {
return dynamicMap;
}
public void setDynamicMap(Map<String, String> dynamicMap) {
this.dynamicMap = dynamicMap;
}
}
In my FillDocumentController I have:
#RequestMapping(value = "/{id}", params = "form", produces = "text/html")
public String updateForm(#PathVariable("id") Long id, Model uiModel) {
...
// Create DynamicForm Map
DynamicForm dynamicForm = new DynamicForm();
for (Field field : allFields) {
// Is StringField?
if (field.getClass().equals(StringField.class) == true) {
StringField stringField = (StringField) field;
if( stringField.getIsEditable() == true ) {
dynamicForm.getDynamicMap().put(stringField.getNameStringField(), stringField.getValueString() );
}
}
}
uiModel.addAttribute("dynamicForm", dynamicForm);
return "filldocuments/update";
}
This is the view:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:field="urn:jsptagdir:/WEB-INF/tags/form/fields"
xmlns:form="http://www.springframework.org/tags/form"
xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0">
<jsp:directive.page contentType="text/html;charset=UTF-8" />
<jsp:output omit-xml-declaration="yes" />
<form:form action="/DocumentGenerator/filldocuments/add" modelAttribute="dynamicForm" method="post">
<c:forEach items="${dynamicForm.dynamicMap}" var="element">
<input name="element['${element.key}']" value="${element.value}"/>
</c:forEach>
<input type="submit" value="Save" />
</form:form>
</div>
And this is the method that catch the modelAttribute:
#RequestMapping(value="/add", method = RequestMethod.POST, produces = "text/html")
public String update(#ModelAttribute(value="dynamicForm") DynamicForm dynamicForm, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
for( String key : dynamicForm.getDynamicMap().keySet() ) {
System.out.println("key="+key);
}
return "redirect:/filldocuments";
}
My problem is that dynamicForm is empty. There is another DynamicForm inside of uiModel and is empty too (I was in debug mode). Where is the data that the user fill?? I dont know what is wrong!!
My fault, the view has to be like that:
<c:forEach items="${dynamicForm.dynamicMap}" var="dynamicMap">
<input name="dynamicMap['${dynamicMap.key}']" value="${dynamicMap.value}"/>
</c:forEach>
So the error was use the variable element
I'm trying to manually add an individual email error message to my model but nothing is displaying in the view.
I think it may be how I'm creating or attached the ObjectError to the BindingResult.
I'm adding the error inside the catch.
Here is the contents of result.errors when I leave email field empty and JSR-303 annotations kick in (error displays in view):
[Field error in object 'user' on field 'email': rejected value []; codes [NotEmpty.user.email,NotEmpty.email,NotEmpty.java.lang.String,NotEmpty]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.email,email]; arguments []; default message [email]]; default message [may not be empty]]
Here is the contents of of result.errors after I manually add the ErrorObject(email error does not display in view):
[Error in object 'email': codes []; arguments []; default message [An account already exists for this email.]]
Controller:
#RequestMapping(value = "/registration", method = RequestMethod.POST)
public ModelAndView post(#Valid User user, BindingResult result)
{
if (result.hasErrors())
{
ModelAndView modelAndView = new ModelAndView(
Consts.MODEL_RESISTER_PAGE);
modelAndView.addObject("user", user);
return modelAndView;
}
else
{
try
{
userService.addUser(user);
ModelAndView modelAndView = new ModelAndView(
Consts.MODEL_CARD_REPORTS_HOME_PAGE);
modelAndView.addObject("userRegisteredSuccess", Boolean.TRUE);
return modelAndView;
}
catch (DataIntegrityViolationException ex)
{
ObjectError error = new ObjectError("email","An account already exists for this email.");
result.addError(error);
ModelAndView modelAndView = new ModelAndView(
Consts.MODEL_RESISTER_PAGE);
modelAndView.addAllObjects(result.getModel());
modelAndView.addObject("user", user);
return modelAndView;
}
}
}
My Model:
#Entity
public class User implements Serializable
{
/**
*
*/
private static final long serialVersionUID = -5232533507244034448L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotEmpty
#Size(min=2, max=15)
private String firstname;
#NotEmpty
#Size(min=2, max=15)
private String surname;
#NotEmpty
#Email
private String email;
#NotEmpty
#Size(min=6, max=10)
private String password;
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public String getFirstname()
{
return firstname;
}
public void setFirstname(String firstname)
{
this.firstname = firstname;
}
public String getSurname()
{
return surname;
}
public void setSurname(String surname)
{
this.surname = surname;
}
public String getEmail()
{
return email;
}
public void setEmail(String email)
{
this.email = email;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
}
File add.html
<form id="registrationForm" action="#" th:action="#{/registration}" th:object="${user}" method="post" class="clearfix">
<legend>Registration</legend>
<div class="control-group input" th:class="${#fields.hasErrors('firstname')}? 'control-group input error'">
<input type="text" th:field="*{firstname}" placeholder="Firstname" />
<span class="help-block" th:if="${#fields.hasErrors('firstname')}" th:errors="*{firstname}"></span>
</div>
<div class="control-group input" th:class="${#fields.hasErrors('surname')}? 'control-group input error'">
<input type="text" th:field="*{surname}" placeholder="Surname" />
<span class="help-block" th:if="${#fields.hasErrors('surname')}" th:errors="*{surname}"></span>
</div>
<div class="control-group input" th:class="${#fields.hasErrors('email')}? 'control-group input error'">
<input type="text" th:field="*{email}" placeholder="Email" />
<span class="help-block" th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></span>
</div>
<div class="control-group input" th:class="${#fields.hasErrors('password')}? 'control-group input error'">
<input type="password" th:field="*{password}" placeholder="Password" />
<span class="help-block" th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></span>
</div>
<div class="clearfix">
<input type="submit" class="btn btn-success btn-large" value="Register" />
</div>
</form>
I usually call result.rejectValue("property", "error.object"); to add errors to BindingResult. If you want to add a global object error, you could use result.reject("error.object");.
So, in your code, instead of:
ObjectError error = new ObjectError("email","An account already exists for this email.");
result.addError(error);
try with:
result.rejectValue("email", "error.user", "An account already exists for this email.");
Check the reference here.
Hope this helps.
I've been trying to integrate reCaptcha with my application built on Spring framework, but I am getting this error:
org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'recaptcha_challenge_field' is not present
Could someone help me understand that why am I getting this error. I've got both recaptcha_challenge_field and recaptcha_response_field parameters bound to the User domain object.
Could anybody help me understand what am I missing?
Thanks
Here is the code of the controller I am using, all I am trying to do is register a user with reCaptcha functionality but what I am getting is a http status 400 with the error org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'recaptcha_challenge_field' is not present:
UserManagementController.java
#Controller
public class UserManagementController {
private static final String RECAPTCHA_HTML = "reCaptchaHtml";
#Autowired
private UserService userService;
#Autowired
private ReCaptcha reCaptcha;
#RequestMapping(method=RequestMethod.GET, value="/addNewUser.do")
public ModelAndView addNewUser() {
User user = new User();
String html = reCaptcha.createRecaptchaHtml(null, null);
ModelMap modelMap = new ModelMap();
modelMap.put("user", user);
modelMap.put(RECAPTCHA_HTML, html);
return new ModelAndView("/addNewUser", modelMap);
}
#RequestMapping(method=RequestMethod.POST, value="/addNewUser.do")
public String addNewUser(#Valid User user, BindingResult result,
#RequestParam("recaptcha_challenge_field") String challenge,
#RequestParam("recaptcha_response_field") String response,
HttpServletRequest request,
Model model) {
verifyBinding(result);
String remoteAddr = request.getRemoteAddr();
ReCaptchaResponse reCaptchaResponse = reCaptcha.checkAnswer(remoteAddr, challenge, response);
if (!reCaptchaResponse.isValid()) {
result.rejectValue("captcha", "errors.badCaptcha");
}
model.addAttribute("user", user);
if (result.hasErrors()) {
result.reject("form.problems");
return "addNewUser";
}
return "redirect:showContent.do";
}
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.setAllowedFields(new String[] {
"firstName", "lastName", "email",
"username", "password", "recaptcha_challenge_field", "recaptcha_response_field"
});
}
private void verifyBinding(BindingResult result) {
String[] suppressedFields = result.getSuppressedFields();
if (suppressedFields.length > 0) {
throw new RuntimeException("You've attempted to bind fields that haven't been allowed in initBinder(): "
+ StringUtils.join(suppressedFields, ", "));
}
}
}
Here is the addNewUser.jsp element on the form page for the above controller:
<tr>
<td>Please prove you're a person</td>
<td>${reCaptchaHtml}</td>
<td><form:errors path="captcha" cssStyle="color:red"></form:errors></td>
</tr>
Could you help me understand what am I missing here?
Thanks for reply.
What is the implementation of:
String html = reCaptcha.createRecaptchaHtml(null, null); ?
The reCaptcha html must have the name attribute as "recaptcha_challenge_field"
...
<textarea name="recaptcha_challenge_field" ... />
<input type="hidden" name="recaptcha_response_field" value="manual_challenge" />
...
Captcha is dynamic loaded script on the page. It is better to read captcha parameters from request object as shown in below example:
#RequestMapping(value="/submitCaptcha.web",method = RequestMethod.POST)
public String submitCaptcha(#ModelAttribute("recaptchaBean") RecaptchaBean recaptchaBean,BindingResult result, ModelMap model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
String captchaChallenge = request.getParameter("recaptcha_challenge_field");
String captchaText = request.getParameter("recaptcha_response_field"); }