Spring 3.2 form errors long restful URLs - java

Think this is an easy one but don't know how to handle it.
I have a form like this.
<c:url value="edit" var="editprofileUrl" />
<form:form class="form" id="signup" action="${editprofileUrl}" method="post" modelAttribute="editProfileForm">
<div class="formInfo">
<s:bind path="*">
<c:choose>
<c:when test="${status.error}">
<div class="text-danger">Unable to change profile. Please fix the errors below and resubmit.</div>
</c:when>
</c:choose>
</s:bind>
</div>
<div class="form-group">
<label for="firstName">First name</label>
<form:errors path="firstName" cssClass="text-danger" />
<form:input class="form-control" path="firstName" />
</div>
<div class="form-group">
<label for="lastName">Last name</label>
<form:errors path="lastName" cssClass="text-danger" />
<form:input class="form-control" id="last-name" path="lastName" />
</div>
<div class="form-group">
<button type="submit" class="btn btn-default">Save profile</button>
</div>
</form:form>
And the java form like this:
public class EditProfileForm {
#NotEmpty
private String firstName;
#NotEmpty
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
And a controller like this:
#Controller
#Transactional
public class EditProfileController {
#PersistenceContext
private EntityManager entityManager;
#RequestMapping(value = "/users/{username}/edit", method = RequestMethod.POST)
public String editProfile(#PathVariable String username, Principal currentUser, #Valid EditProfileForm form, BindingResult formBinding) {
if (formBinding.hasErrors()) {
return null;
}
Account user = entityManager.find(Account.class, currentUser.getName());
user.setFirstName(form.getFirstName());
user.setLastName(form.getLastName());
entityManager.persist(user);
return "home";
}
#RequestMapping(value = "/users/{username}/edit", method = RequestMethod.GET)
public ModelAndView editProfileForm(#PathVariable String username, Principal currentUser, WebRequest request, Model model) {
Account account = entityManager.find(Account.class, username);
if (account != null) {
model.addAttribute("account", account);
}
EditProfileForm form = new EditProfileForm();
form.setFirstName(account.getFirstName());
form.setLastName(account.getLastName());
return new ModelAndView("editprofile", "editProfileForm", form);
}
}
Everything works good besides when I don't fill in anything in one of the fields and it should give an error.
Something breaks in this code snippet:
if (formBinding.hasErrors()) {
return null;
}
Instead of returning the same page again with the errors it looks for a view that does not exist:
HTTP Status 404 - /project/WEB-INF/views/users/nilsi/edit.jsp
How do I return the same view again with the errors? Usually it works when i have a shorter #RequestMapping like /signup.
Thanks for any help on this!

In the event of binding errors, return the name of the form view again so that the user can correct the errors.
if (formBinding.hasErrors()) {
return "editprofile";
}
If you return null, Spring tries to guess a view name by looking at the url. Since your url ends with ".../edit", it tries to load edit.jsp.

Related

Spring Validator validate method never called

I have two close to each other controllers - one for products and one for users.
I'd like to add Spring Validation to both controllers.
So I added ProductValidator and UserValidator classes which implement Validator.
ProductValidator works fine.
The problem is UserValidator is never called.
InitBinder method is being called, but validation doesn't work.
At the same time everything works fine with products in ProductController.
Here's the code of user logic.
UserController
#Controller
#RequestMapping(path = "/users")
public class UserController {
private final UserService userService;
private final RoleService roleService;
private PasswordEncoder passwordEncoder;
#Autowired
public UserController(UserService userService, RoleService roleService, PasswordEncoder passwordEncoder) {
this.userService = userService;
this.roleService = roleService;
this.passwordEncoder = passwordEncoder;
}
#Autowired
#Qualifier("userValidator")
private UserValidator userValidator;
#InitBinder("user")
private void initBinder(WebDataBinder binder) {
binder.setValidator(userValidator);
binder.registerCustomEditor(Role.class, "roles", new RoleEditor(roleService));
}
...
#RequestMapping(path = "/save", method = RequestMethod.POST)
public ModelAndView submit(#ModelAttribute("user") #Valid User user,
BindingResult result) {
...
}
...
}
UserValidator
#Component("userValidator")
public class UserValidator implements Validator {
private final UserRepository userRepository;
private final RoleRepository roleRepository;
#Autowired
public UserValidator(UserRepository userRepository, RoleRepository roleRepository) {
this.userRepository = userRepository;
this.roleRepository = roleRepository;
}
#Override
public boolean supports(Class<?> paramClass) {
return User.class.equals(paramClass);
}
#Override
public void validate(Object obj, Errors errors) {
User user = (User) obj;
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "username", "username.required", "Enter username");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "firstName.required", "Enter user first name");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "lastName.required", "Enter user last name");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "password.required", "Enter user password");
User userToFind = userRepository.findByUsername(user.getUsername()).orElse(new User());
if (Objects.isNull(user.getId()) && Objects.nonNull(userToFind.getId())) {
errors.rejectValue("username", "username.already.exist", "user with this username already exists");
}
if (Objects.isNull(user.getRoles())) {
errors.rejectValue("roles", "role.required", "role must be assigned");
}
Role adminRole = roleRepository.getAdminRole();
if (Objects.nonNull(user.getId())
&& Objects.nonNull(user.getRoles())
&& !user.getRoles().contains(adminRole)
&& userRepository.findUsersWithAdministratorRole().size() == 1) {
errors.rejectValue("roles", "admin.required", "at least one admin role must exist");
}
}
public ErrorMessage validateUserToDelete(UUID id) {
ErrorMessage errorMessage = new ErrorMessage();
List<String> errors = new ArrayList<>();
Set<User> admins = userRepository.findUsersWithAdministratorRole();
if (admins.size() == 1) {
errors.add(String.format("User with email %s is the one with Admin role. Impossible to delete last Admin user."
, userRepository.findById(id).get().getUsername()));
}
errorMessage.setErrors(errors);
return errorMessage;
}
}
User
#Entity
#Table(name = "users")
public class User {
private UUID id;
private String username;
private String password;
private String firstName;
private String lastName;
private BigDecimal money;
private Set<Role> roles;
private Set<Product> products;
#Id
#Type(type="org.hibernate.type.PostgresUUIDType")
#Column(name = "id", columnDefinition = "uuid")
#GeneratedValue(strategy = GenerationType.AUTO)
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
#Column(name = "username")
#NotEmpty
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
#Column(name = "password")
#NotEmpty
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#Column(name = "first_name")
#NotEmpty
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
#Column(name = "last_name")
#NotEmpty
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
#Column(name = "money")
#DecimalMin(value = "0.0", inclusive = false)
#Digits(integer = 10, fraction = 2)
public BigDecimal getMoney() {
return money;
}
public void setMoney(BigDecimal money) {
this.money = money;
}
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.REFRESH})
#JoinTable(
name = "users_roles",
joinColumns = { #JoinColumn(name = "user_id") },
inverseJoinColumns = { #JoinColumn(name = "role_id") }
)
#NotNull
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.REFRESH})
#JoinTable(
name = "checkout",
joinColumns = { #JoinColumn(name = "user_id") },
inverseJoinColumns = { #JoinColumn(name = "product_id") }
)
public Set<Product> getProducts() {
return products;
}
public void setProducts(Set<Product> products) {
this.products = products;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return id.equals(user.id) && username.equals(user.username) && password.equals(user.password) && firstName.equals(user.firstName) && lastName.equals(user.lastName);
}
#Override
public int hashCode() {
return Objects.hash(id, username, password, firstName, lastName);
}
}
user.jsp
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%# taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%# taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
<%# page contentType="text/html;charset=UTF-8" language="java" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}"/>
<!DOCTYPE html>
<html>
<head>
<c:import url="${contextPath}/WEB-INF/jsp/header.jsp"/>
</head>
<body>
<c:import url="${contextPath}/WEB-INF/jsp/navibar.jsp"/>
<div class="container">
<div class="row">
<security:authorize access="hasRole('ROLE_ADMIN')">
<div class="btn-toolbar" role="toolbar" aria-label="Toolbar with button groups">
<div class="btn-group me-2" role="group" aria-label="Second group">
Back to users
</div>
</div>
</security:authorize>
</div><br>
<form:form action="/users/save" method="post" modelAttribute="user">
<div class="form-group">
<div class="row">
<form:label path="id">User ID:</form:label><br>
<form:input path="id" type="UUID" readonly="true" class="form-control" id="id" placeholder="User ID" name="id" value="${user.id}"/>
<br>
<form:label path="username">username (email address):</form:label><br>
<form:input path="username" type="text" class="form-control" id="username" placeholder="Enter username" name="username" value="${user.username}"/>
<form:errors path="username" cssClass="error"/>
<br>
<form:label path="password">Password:</form:label><br>
<form:input path="password" type="text" class="form-control" id="password" placeholder="Enter password" name="password" value="${user.password}"/>
<form:errors path="password" cssClass="error"/>
<br>
<form:label path="firstName">First name:</form:label><br>
<form:input path="firstName" type="text" class="form-control" id="firstName" placeholder="Enter first name" name="firstName" value="${user.firstName}"/>
<form:errors path="firstName" cssClass="error"/>
<br>
<form:label path="lastName">First name:</form:label><br>
<form:input path="lastName" type="text" class="form-control" id="lastName" placeholder="Enter last name" name="lastName" value="${user.lastName}"/>
<form:errors path="lastName" cssClass="error"/>
<br>
<form:label path="money">Money:</form:label><br>
<form:input path="money" type="number" class="form-control" id="money" placeholder="Enter money" name="money" value="${user.money}"/>
<form:errors path="money" cssClass="error"/>
<br>
<security:authorize access="hasRole('ROLE_ADMIN')">
<form:label path="roles">Roles:</form:label><br>
<c:forEach items="${roles}" var="role">
<form:checkbox path="roles" id="${roles}" label="${role.name}" value="${role}"/></td>
</c:forEach>
<br>
<form:errors path="roles" cssClass="error"/><br><br>
</security:authorize>
</div>
<div class="row">
<div class="btn-toolbar" role="toolbar" aria-label="Toolbar with button groups">
<div class="btn-group me-2" role="group" aria-label="Second group">
<form:button type="submit" value="Submit" class="btn btn-primary">Save</form:button>
</div>
</div>
</div>
</div>
</form:form>
</div>
</body>
</html>
If you look at the classname from your debugger com.intellias.testmarketplace.controller.UserController$$EnhancerBySpringCGLIB$$309171b7 you see the $$EnhancerBySpringCGLIB. That part indicates that a proxy is being generated by Spring. A proxy can be generated for various reasons but the most common one for controllers is security, the #PreAuthorize annotation comes to mind. Another option is the #Timed annotation to do monitoring on the controller.
As your initBinder method is private this will be invoked directly on the proxy and not on the actual bean instance. This is due to a class proxy being created, which works by extending your class. A method is being generated with the same signature and the interceptor is invoked and then the call is passed on to the actual method. However this will only work for public and protected methods not for private or final methods. Those will be directly invoked on the empty proxy (the fields are null as that doesn't need the dependencies).
As your method is private it is invoked on the proxy. The proxy doesn't have the fields set, so the userValidator is null. So effectivly there is no Validator being set on the WebDataBinder and thus no validation will and can be done.
To fix, make the method public (or protected) so that it will properly be called on the actual bean and not the proxy.
For the future and as a rule of thumb you probably want your methods with annotations in a controller to be public anyway instead of `private.
The issue was in private access modifier for initBinder method combined with #PreAuthorize annotation on some UserController methods.
Many thanks to #M.Deinum for patience and help.

Error while trying to include an object's property in <form:hidden/> having object list in modelAttribute

I am working on a spring mvc project. I have a view where I list all objects of type Person. The list is successfully displayed with all attributes. However, as I now need to pass the individual object back to the controller upon selection by the user, I have enclosed my list in form tag so that I could bind it with modelAttribute and send the object back to controller. When I try to include a form:hidden field to bind an attribute, I get the following error:
ERROR o.s.w.s.tags.form.HiddenInputTag - Invalid property 'uid' of bean class [java.util.ArrayList]: Bean property 'uid' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
org.springframework.beans.NotReadablePropertyException: Invalid property 'uid' of bean class [java.util.ArrayList]: Bean property 'uid' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
All setters and getters exist for the Person object and the attributes successfully get displayed if I don't include the form:hidden
Model:
public class Person {
Integer id;
String uid;
String fullname;
String firstname;
String lastname;
String fathername;
String email;
String status;
List<String> environments;
List<String> institutions;
public boolean isNew() {
return (this.id == null);
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public String getFullname() {
return fullname;
}
public void setFullname(String fullname) {
this.fullname = fullname;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getFathername() {
return fathername;
}
public void setFathername(String fathername) {
this.fathername = fathername;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public List<String> getEnvironments() {
return environments;
}
public void setEnvironments(List<String> environments) {
this.environments = environments;
}
public List<String> getInstitutions() {
return institutions;
}
public void setInstitutions(List<String> institutions) {
this.institutions = institutions;
}
}
Controller:
#RequestMapping(value = "/persons/search", method = RequestMethod.POST)
public String listPersons(#RequestParam("searchString") String searchStr, Model model) {
logger.debug("listPersons()");
userService.initialize();
myPersons = userService.searchPersons(searchStr);
model.addAttribute("persons", myPersons);
return "users/listPersons";
}
View (listPersons.jsp)
<%# page session="false"%>
<%# taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%# taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html lang="en">
<jsp:include page="../fragments/header.jsp" />
<body>
<div class="container">
<c:if test="${not empty msg}">
<div class="alert alert-${css} alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<strong>${msg}</strong>
</div>
</c:if>
<h1>All Persons</h1>
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>UID</th>
<th>Full Name</th>
<th>Email</th>
<th>Institutions</th>
<th>Environments</th>
<th>Action</th>
</tr>
</thead>
<spring:url value="/persons/cards" var="showPersonCardsUrl" />
<form:form method="post" action="${showPersonCardsUrl}" modelAttribute="persons">
<c:forEach var="person" items="${persons}" varStatus="status">
<tr>
<td>
${person.id}
</td>
<td>${person.uid}</td>
<form:hidden path="uid" />
<td>${person.fullname}</td>
<td>${person.email}</td>
<td><c:forEach var="institution" items="${person.institutions}" varStatus="loop">
${institution}
<c:if test="${not loop.last}">,</c:if>
</c:forEach>
</td>
<td><c:forEach var="environment" items="${person.environments}" varStatus="loop">
${environment}
<c:if test="${not loop.last}">,</c:if>
</c:forEach>
</td>
<td>
<spring:url value="/persons/${person.uid}" var="showPersonUrl" />
<spring:url value="/persons/${person.uid}/delete" var="deletePersonUrl" />
<spring:url value="/persons/${person.uid}/update" var="updatePersonUrl" />
<spring:url value="/persons/${person.uid}/cards" var="showPersonCardsUrl" />
<button class="btn btn-info" onclick="location.href='${showPersonUrl}'">Query</button>
<button class="btn btn-primary" onclick="location.href='${updatePersonUrl}'">Update</button>
<button class="btn btn-danger" onclick="this.disabled=true;post('${deletePersonUrl}')">Delete</button>
<button class="btn btn-info" type="submit">Cards</button>
</td>
</tr>
</c:forEach>
</form:form>
</table>
</div>
<jsp:include page="../fragments/footer.jsp" />
</body>
</html>
Instead of
<form:hidden path="uid" />
I have also tried
<form:hidden path="persons.person.uid"/>
and also this
<form:hidden path="users[${status.index}].uid"/>
but nothing works. I need to pass the person object back to the controller with its uid so that I can do further processing. What could I be doing wrong in this?
The hidden form tag will try to resolve the path from withing the modelAttribute in the from tag it's inside, for example, your modelAttribute is persons, so the hidden form will try to access persons.uid
and if you use persons.person.uid it will resolve to persons.persons.person.uid
and so on.
In your case, I suggest using a regular input:
<input type="hidden" name="persons[${status.index}]" value="${person.uid}"/>
With this, when the form is submitted it will send an array named persons with uid values inside each.
If you still insist on using <form:hidden> then you need to revisit how you build your form.

Property or field 'email' cannot be found on null

I'm trying to POST from a form within a Boostrap Modal.
Here's my form:
<form role="form" id="emailForm" action="#" th:action="#{/emailSubmission}" th:object="${university}" method="post">
<div class="form-group">
<label for="emailID"><span class="glyphicon glyphicon-user"></span> Username</label>
<input type="text" class="form-control" id="emailID" th:value="*{email}" placeholder="Enter email"></input>
</div>
<button type="submit" value="Submit" id="submitButton" class="btn btn-default btn-success btn-block" ><span class="glyphicon glyphicon-check"></span> Register</button>
</form>
Here's my controller:
#Controller
public class RegistrationController {
#RequestMapping(value = "/emailSubmission", method = RequestMethod.POST)
public String registerEmail(#ModelAttribute("university") University uni, BindingResult result, Model model)
{
System.out.println(uni.getEmail());
return "index";
}
}
And my University class:
public class University {
private String email;
public University(){
}
public String getEmail(){
return email;
}
public void setEmail(String email){
this.email = email;
}
}
I'm new to Spring and can't figure out what's going wrong and why I'm receiving the error mentioned in the title.
Changing:
th:value="*{email}"
to:
th:field="*{email}"
gives me 'Neither BindingResult nor plain target object for bean name 'university' available as request attribute' error.
You have to add the university object as an attribute to the model in your controller:
#GetMapping(value = "/index")
public String login(Model model) {
model.addAttribute("university", new University());
return "index";
}
Here is what I have for your, hope it helps. If you don't understand just tell me, have a good night.
target.request(MediaType.APPLICATION_JSON)
.post(Entity.json(Json.createObjectBuilder()
.add("attribute1", "value1")
.add("attribute2", "value2")
.build()));
or if you prefer:
MultivaluedMap<String, String> map = new MultivaluedHashMap<>();
map.add("attribute1", "value1");
map.add("attribute2", "value2");
target.request(MediaType.APPLICATION_FORM_URLENCODED)
.post(Entity.form(map));
Now what I should do with it?
#POST
public Response post(JsonObject json) {
return Response.ok(service.persist(MyFactory.create(json.getString("attribute1"), json.getString("attribute2"))).build();
}
Or of course if you prefer:
#POST
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response post(#FormParam("attribute1") String attribute1, #FormParam("attribute2") String attribute2) {
return Response.ok(service.persist(MyFactory.create(attribute1, attribute2))).build();
}

How to get querystring passed value from the url into spring controller?

I want to get the querystrting passing value from the given url, like: http://localhost:8080/app?contentid=10 I want to get the value of contentid is: 10 from the above url in my spring controller, how can I get this ?
Please note that if I pass any value there(like 10, 50,100, 200, ...etc - it can be any number), so whatever I am passing to contentid, that value should get into my controller. I want to get this contentid value from that url only, not from my html page or I don't want to pass from my html page. Currently I am getting null from the below code in controller, I am not getting the passing value(like 10 from the url). How can I achieve this ? Thanks in advance for your help !
app.html:
<form action="#" th:action="#{/app}" th:object="${TestData}" method="post">
<div class="form-group">
<label for="firstname">First Name: </label> <input type="text"
class="form-control" id="firstname" name="firstname" ></textarea>
</div>
<div class="form-group">
<label for="secondname">Second Name:</label> <input type="text"
class="form-control" id="secondname" name="secondname" />
</div>
<button type="submit" value="Submit">Submit</button>
</form>
TestData.java:
public class TestData implements Serializable{
private String firstname;
private String secondname;
private int contentid;
//setters and getters
public TestData() {}
public TestData(String firstname, String secondname, int contentid, ){
this.firstname = firstname;
this.secondname = secondname;
this.contentid = contentid;
}
#Override
public String toString() {
return String.format(
"TestData[firstname=%s, secondname=%s, contentid=%d]",
firstname, secondname, contentid);
}
}
Controller:
public class TestDataController {
#Autowired
TestService testDataService;
#RequestMapping(value="/app", method=RequestMethod.POST)
public String testDataSubmit(#RequestParam(required=false) Integer contentid, #ModelAttribute TestData testData, Model model, HttpServletRequest request) {
String id = request.getQueryString();
System.out.println("My Id: "+id);//null
System.out.println("URL Parameter: "+testData.getContentid());//0
System.out.println("URL Parameter passed objectid: "+contentid); //null
testDataService.saveTestDataDetails(testData);
return "app";
}
}
Service:
#Service
public class TestService {
#PersistenceContext
private EntityManager entityManager;
public void saveTestDataDetails(TestData testData) {
StoredProcedureQuery sp = entityManager.createStoredProcedureQuery("APP.TESTDATA");
sp.registerStoredProcedureParameter("firstname", String.class, ParameterMode.IN);
sp.registerStoredProcedureParameter("secondname", Integer.class, ParameterMode.IN);
sp.registerStoredProcedureParameter("contentid", Integer.class, ParameterMode.IN);
sp.setParameter("firstname", testData.getFirstname());
sp.setParameter("secondname", testData.getSecondname());
sp.setParameter("contentid", testData.getContentid());
sp.execute();
}
}
After discussion per chat I will provide you a working solution where you can specifiy any stuff by your own needs.
app.html:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<title></title>
</head>
<body>
<th:block th:if="${contentid != null}">
<div th:text="${'contentId: ' + contentid}"></div>
</th:block>
<form action="#" th:action="#{/app(contentid=${contentid})}" th:object="${TestData}"
method="post">
<div class="form-group">
<label for="firstname">First Name: </label>
<input type="text" class="form-control" id="firstname"
name="firstname" />
</div>
<div class="form-group">
<label for="secondname">Second Name:</label>
<input type="text" class="form-control" id="secondname"
name="secondname" />
</div>
<button type="submit" value="Submit">Submit</button>
</form>
</body>
</html>
Controller:
#Controller
public class MyController {
#GetMapping("/app")
public String getApp(Model model) {
return "app";
}
#PostMapping("/app")
public String postApp(#RequestParam(required=false) Integer contentid, #ModelAttribute TestData testData, Model model, HttpServletRequest request) {
System.out.println(contentid);
if(contentid == null) {
contentid = ThreadLocalRandom.current().nextInt(0, 100 + 1);
}
model.addAttribute("contentid",contentid);
return "app";
}
}
First change your html code th:action="#{/app?contentid=10} and then add setter and getter method for the instance variable in the class TestData.

Spring MVC view & update

I'm trying to update data from Spring MVC's view. I could update data using tester object but failed when trying update from Spring MVC's view. POST request from view has no problem key/value were posting to server. It updated my table data but with null value (unless for id field).
Please help and thank you.
My Table (tx_surveyrequest)
CREATE TABLE tx_surveyrequest
(
id uuid NOT NULL,
id_person uuid,
id_surveyrequeststatus uuid,
id_validationstatus uuid,
id_employee_assistantgis uuid,
formcode character varying(64),
b_north character varying(32),
b_east character varying(32),
b_south character varying(32),
b_west character varying(32),
hectarage numeric(10,2),
is_priority boolean DEFAULT false,
requestdate date,
surveyplandate date,
surveyplantime time without time zone,
requestedby character varying(32),
requestedprice numeric(14,2),
CONSTRAINT pk_tx_surveyrequest PRIMARY KEY (id)
)
My domain object (SurveyRequest)
#Entity
#Table(name = "tx_surveyrequest")
public class SurveyRequest implements Serializable {
private UUID id;
private Person person;
private String formCode;
private String borderNorth;
private String borderEast;
private String borderSouth;
private String borderWest;
private Double hectarage;
private Boolean priority;
private DateTime requestDate;
private DateTime surveyPlanDate;
private LocalTime surveyPlanTime;
private String requestedBy;
private Double requestedPrice;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id")
#Type(type="pg-uuid")
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
#Column(name = "hectarage")
public Double getHectarage(){
return hectarage;
}
public void setHectarage(Double hectarage){
this.hectarage = hectarage;
}
#Column(name = "formcode")
public String getFormCode() {
return formCode;
}
public void setFormCode(String formCode) {
this.formCode = formCode;
}
#Column(name="is_priority")
public Boolean getPriority() {
return priority;
}
public void setPriority(Boolean priority) {
this.priority = priority;
}
#Column(name="b_north")
public String getBorderNorth() {
return borderNorth;
}
public void setBorderNorth(String border) {
this.borderNorth = border;
}
#Column(name="b_east")
public String getBorderEast() {
return borderEast;
}
public void setBorderEast(String border) {
this.borderEast = border;
}
#Column(name="b_south")
public String getBorderSouth() {
return borderSouth;
}
public void setBorderSouth(String border) {
this.borderSouth = border;
}
#Column(name="b_west")
public String getBorderWest() {
return borderWest;
}
public void setBorderWest(String border) {
this.borderWest = border;
}
// TODO replace pattern with i18n
#Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime")
#DateTimeFormat(pattern = "dd/MM/yyyy")
#Column(name = "requestdate")
public DateTime getRequestDate() {
return requestDate;
}
public void setRequestDate(DateTime requestDate) {
this.requestDate = requestDate;
}
// TODO replace pattern with i18n
#Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime")
#DateTimeFormat(pattern = "dd/MM/yyyy")
#Column(name = "surveyplandate")
public DateTime getSurveyPlanDate() {
return surveyPlanDate;
}
public void setSurveyPlanDate(DateTime surveyPlanDate) {
this.surveyPlanDate = surveyPlanDate;
}
// TODO replace pattern with i18n
#Type(type = "org.joda.time.contrib.hibernate.PersistentLocalTimeAsTime")
#DateTimeFormat(pattern = "HH:mm")
#Column(name = "surveyplantime")
public LocalTime getSurveyPlanTime() {
return surveyPlanTime;
}
public void setSurveyPlanTime(LocalTime surveyPlanTime) {
this.surveyPlanTime = surveyPlanTime;
}
Update success using SurveyRequestUpdate
public class SurveyRequestUpdate {
public static void main(String[] args) {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("classpath:spring-data-app-context.xml");
ctx.load("classpath:datasource.xml");
ctx.refresh();
SurveyRequestService surveyRequestService = ctx.getBean(
"springJpaSurveyRequestService",SurveyRequestService.class);
UUID uid = UUID.fromString("0cd02976-1864-447b-b540-39d6e7ee3703");
SurveyRequest request = surveyRequestService.findById(uid);
System.out.println("Find by ID");
System.out.println(request);
request.setSurveyPlanDate (new DateTime(2014,7,1,0,0));
request.setSurveyPlanTime (new LocalTime(22,0));
surveyRequestService.save(request);
System.out.println(request);
}
Update Form View
<jsp:directive.page contentType="text/html;charset=UTF-8" />
<jsp:output omit-xml-declaration="yes" />
<spring:message code="l_surveyrequest_list" var="labelSurveyRequestList" />
<spring:message code="l_surveyrequest_title" var="labelSurveyRequestTitle" />
<spring:message code="l_surveyrequest_code" var="labelSurveyRequestCode" />
<spring:message code="l_surveyrequest_requestdate" var="labelSurveyRequestRequestDate" />
<spring:message code="l_surveyrequest_surveydate" var="labelSurveyRequestSurveyDate" />
<spring:message code="l_surveyrequest_surveytime" var="labelSurveyRequestSurveyTime" />
<spring:message code="l_surveyrequest_priority" var="labelSurveyRequestPriority" />
<spring:message code="l_surveyrequest_complete" var="labelSurveyRequestComplete" />
<spring:message code="l_surveyrequest_hectarage" var="labelSurveyRequestHectarage" />
<spring:message code="l_surveyrequest_north" var="labelSurveyRequestNorth" />
<spring:message code="l_surveyrequest_east" var="labelSurveyRequestEast" />
<spring:message code="l_surveyrequest_south" var="labelSurveyRequestSouth" />
<spring:message code="l_surveyrequest_west" var="labelSurveyRequestWest" />
<spring:message code="form_new" var="formNew" />
<spring:message code="form_edit" var="formEdit" />
<spring:message code="btn_save" var="buttonSave" />
<spring:message code="btn_cancel" var="buttonCancel" />
<spring:message code="long_date_format_pattern" var="formatDate" />
<spring:message code="time_format_pattern" var="formatDateTime" />
<spring:message code="short_time_format_pattern" var="formatShortTime" />
<spring:url value="/surveyrequest" var="showSurveyRequestUrl" />
<spring:eval expression="surveyRequest.id == null ? formNew:formEdit" var="formTitle"/>
<div class="row">
<div class="col-md-12">
<h1>${formTitle} ${labelSurveyRequestTitle}</h1>
</div>
</div>
<!-- /top action bar -->
<div class="row">
<div class="col-md-12">
<c:if test="${not empty surveyRequest}">
<form:form class="form-horizontal"
modelAttribute="surveyRequest" commandName="surveyRequest" method="post" enctype="multipart/form-data">
<div class="row">
<div class="col-md-12">
<button type="submit" class="btn btn-primary">${buttonSave}</button>
<button type="reset" class="btn btn-primary">${buttonCancel}</button>
</div>
</div>
<c:if test="${not empty message}">
<div id="message" class="${message.type}">${message.message}</div>
</c:if>
<div class="form-group">
<label class="col-md-3 control-label">${labelSurveyRequestCode}</label>
<div class="col-md-3">
<form:input type="text" class="form-control" path="formCode" />
</div>
</div>
<div class="form-group">
<label class="col-md-3 control-label">${labelSurveyRequestRequestDate}</label>
<div class='col-md-3 input-group date datepicker'>
<form:input type='text' class="form-control" path="requestDate" />
<span class="input-group-addon"><span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
<div class="form-group">
<label class="col-md-3 control-label">${labelSurveyRequestSurveyDate}</label>
<div class='col-md-3 input-group date datepicker'>
<form:input type='text' class="form-control" path="surveyPlanDate" />
<span class="input-group-addon"><span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
<div class="form-group">
<form:label class="col-md-3 control-label"
path="surveyPlanTime">${labelSurveyRequestSurveyTime}</form:label>
<div class='col-md-3 input-group date timepicker'>
<form:input type='text' class="form-control" path="surveyPlanTime" />
<span class="input-group-addon"><span class="glyphicon glyphicon-time"></span>
</span>
</div>
</div>
<div class="form-group">
<label class="col-md-3 control-label">${labelSurveyRequestHectarage}</label>
<div class="col-md-3">
<form:input type="text" class="form-control" path="hectarage" />
</div>
</div>
<div class="form-group">
<p class="col-md-3"></p>
<div class="col-md-9">
<c:choose>
<c:when test="${surveyRequest.priority == true}">
${labelSurveyRequestPriority}
</c:when>
</c:choose>
</div>
</div>
<div class="form-group">
<p class="col-md-3"></p>
<div class="col-md-9">
<table class="table">
<thead>
<tr>
<th>${labelSurveyRequestNorth}</th>
<th>${labelSurveyRequestEast}</th>
<th>${labelSurveyRequestSouth}</th>
<th>${labelSurveyRequestWest}</th>
</tr>
</thead>
<tbody>
<tr>
<td>${surveyRequest.borderNorth}</td>
<td>${surveyRequest.borderEast}</td>
<td>${surveyRequest.borderSouth}</td>
<td>${surveyRequest.borderWest}</td>
</tr>
</tbody>
</table>
</div>
</div>
</form:form>
</c:if>
</div>
</div>
<script type="text/javascript">
$(function () {
$('.datepicker').datetimepicker({
pickTime: false,
language: 'id',
});
$('.timepicker').datetimepicker({
pickDate: false,
minuteStepping:15,
useSeconds: false,
language: 'id'
});
});
</script>
</div>
My Controller
#RequestMapping("/surveyrequest")
#Controller
public class SurveyRequestController {
private static final int PAGE_SIZE = 10;
private static final Logger logger = LoggerFactory
.getLogger(SurveyRequestController.class);
#Autowired
MessageSource messageSource;
#Autowired
private SurveyRequestService surveyRequestService;
// Others code omitted
#RequestMapping(value="/{id}", method = RequestMethod.GET)
public String show(#PathVariable("id") UUID id, Model model) {
SurveyRequest request = surveyRequestService.findById(id);
model.addAttribute("surveyRequest", request);
return "surveyrequest/show";
}
/*
* Edit a survey request
*/
#RequestMapping(value="/{id}", params="form", method = RequestMethod.GET)
public String updateForm(#PathVariable("id") UUID id, Model model) {
SurveyRequest request = surveyRequestService.findById(id);
model.addAttribute("surveyRequest", request);
return "surveyrequest/edit";
}
/*
* Update a survey request
*/
#RequestMapping(value = "/{id}", params = "form", method = RequestMethod.POST)
public String update(SurveyRequest surveyRequest, BindingResult bindingResult, Model uiModel,
HttpServletRequest httpServletRequest, RedirectAttributes redirectAttributes, Locale locale) {
if (bindingResult.hasErrors()) {
uiModel.addAttribute("message", new Message("error", messageSource.getMessage("surveyrequest_save_fail", new Object[]{}, locale)));
uiModel.addAttribute("surveyRequest", surveyRequest);
return "surveyrequest/update";
}
uiModel.asMap().clear();
redirectAttributes.addFlashAttribute("message", new Message("success", messageSource.getMessage("surveyrequest_save_success", new Object[]{}, locale)));
logger.info("Before paste: " + surveyRequest);
surveyRequestService.save(surveyRequest);
return "redirect:/surveyrequest/" + UrlUtil.encodeUrlPathSegment(surveyRequest.getId().toString(), httpServletRequest);
}
Updated:
I could resolve the problem with removing enctype="multipart/form-data" at edit.jspx. But it raised another issue, how if I'm going to upload file?
Add #ModelAttribute annotation to your model object parameter in the controller method that you're calling, so that Spring knows that's the object that you want to bind the form data to.
/*
* Update a survey request
*/
#RequestMapping(value = "/{id}", params = "form", method = RequestMethod.POST)
public String update(#ModelAttribute("surveyRequest") SurveyRequest surveyRequest,
BindingResult bindingResult, Model uiModel,
HttpServletRequest httpServletRequest,
RedirectAttributes redirectAttributes, Locale locale) {
[...]
}

Categories