Themeleaf and submit form POST - spring

Mavens,
I am struggling to invoke a controller from Themeleaf.
My themeleaf code looks like:
<form action="#" th:action="#{/order}" modelAttribute="order" th:object="${order}" method="POST">
<div class="div">
<h5>Amount</h5>
<input type="text" class="input" th:field="*{amountValue}">
</div>
<input type="submit" class="btn" value="Process Payment">
</form>
My Controller Code is:
#RequestMapping(value = "/order", method = RequestMethod.POST)
public ModelAndView processOrder(#ModelAttribute Order order) {
ModelAndView modelAndView = new ModelAndView();
String accessToken = token();
String paymentURL = null;
if (accessToken != null) {
paymentURL = placeOrder(accessToken, order);
if (paymentURL != null) {
modelAndView.addObject("orderReferenceNumber", paymentURL.substring(paymentURL.indexOf("=") + 1));
modelAndView.addObject("paymentURL", paymentURL + "&slim=true");
modelAndView.setViewName("paymentProcess");
return modelAndView;
}
}
return modelAndView;
}
My Get method is
#RequestMapping(value = "/index", method = RequestMethod.POST)
public ModelAndView doLogin(#RequestParam(value = "username", required = true) String username,
#RequestParam(value = "password", required = true) String password) {
ModelAndView modelAndView = new ModelAndView();
if (username != null && password != null) {
if (username.equalsIgnoreCase("one") && password.equalsIgnoreCase("one")) {
modelAndView.addObject("order", new Order());
modelAndView.setViewName("index");
return modelAndView;
}
}
modelAndView.setViewName("welcome");
return modelAndView;
}
Error on click of the button
Error resolving template [order], template might not exist or might not be accessible by any of the configured Template Resolvers
org.thymeleaf.exceptions.TemplateInputException: Error resolving template [order], template might not exist or might not be accessible by any of the configured Template Resolvers
What am i doing wrong ?

The issue comes from how you populate your ModelAndView instance. Only when your two if clauses are matched, you set the name of the view with modelAndView.setViewName("paymentProcess");. That means for some executions (not matching your both conditionals), you don't set the name of the view at all and Spring MVC doesn't know which view to render and return to the user.
To fix this, make sure you always set a default/fallabck view to return in case the if conditions are both not true. Your code might override this view name but you have at least for each case a view to fallback on:
#RequestMapping(value = "/order", method = RequestMethod.POST)
public ModelAndView processOrder(#ModelAttribute Order order) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("yourDefaultViewName"); // this is the important line
String accessToken = token();
String paymentURL = null;
if (accessToken != null) {
paymentURL = placeOrder(accessToken, order);
if (paymentURL != null) {
modelAndView.addObject("orderReferenceNumber", paymentURL.substring(paymentURL.indexOf("=") + 1));
modelAndView.addObject("paymentURL", paymentURL + "&slim=true");
modelAndView.setViewName("paymentProcess");
return modelAndView;
}
}
return modelAndView;
}

Related

The input tag not showing in the form spring-mvc-form

So i'm trying to create a from for adding products but when i go to the url the form doesn't show any input tag only the labels
I followed this documentation : https://www.baeldung.com/spring-mvc-form-tutorial
this is the GET method
#RequestMapping(value = "/product", method = RequestMethod.GET)
public ModelAndView showForm() {
return new ModelAndView("products/CreateProduct", "produit", new Produit());
}
and this is the POST method
#RequestMapping(value = "/create-product", method = RequestMethod.POST)
public String submitproduct(#Valid #ModelAttribute("produit")Produit produit,
BindingResult result, ModelMap model) {
if (result.hasErrors()) {
return "error";
}
model.addAttribute("productID", produit.getId_produit());
model.addAttribute("productName", produit.getNom_produit());
model.addAttribute("productPrice", produit.getPrix_produit());
model.addAttribute("productDescription", produit.getDescription_produit());
model.addAttribute("productBrand", produit.getMarque_produit());
model.addAttribute("productBarCode", produit.getCodeBarre_produit());
return "products/CreateProduct";
}

SpringBoot RedirectAttributes does not show in thymeleaf

controller code:
#Controller
#RequestMapping("/admin")
#AllArgsConstructor
public class AdminController {
private AdminUserService adminUserService;
#PostMapping("/login")
public String login(#RequestParam String username, #RequestParam String password, #RequestParam String kaptcha, RedirectAttributes attributes, HttpSession session) {
String errorMsg;
if (StringUtils.isEmpty(kaptcha)) {
errorMsg = "kaptcha can't be empty";
attributes.addFlashAttribute("errorMsg", errorMsg);
return "redirect:admin/login";
}
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
errorMsg = "username or password can't be empty";
attributes.addFlashAttribute("errorMsg", errorMsg);
return "redirect:admin/login";
}
String code = (String) session.getAttribute(Const.kapchaCode);
if (StringUtils.isEmpty(code) || !kaptcha.equals(code)) {
errorMsg = "invalid kaptcha code";
attributes.addFlashAttribute("errorMsg", errorMsg);
return "redirect:admin/login";
}
AdminUser login = adminUserService.login(username, password);
if (login == null) {
errorMsg = "invalid username password combination";
attributes.addFlashAttribute("errorMsg", errorMsg);
return "redirect:admin/login";
}
session.setAttribute("loginUser", login.getAlias());
session.setAttribute("loginUserId", login.getAdminUserId());
return "redirect:/admin/index";
}
template:
<div class="form-group">
<div th:if="${errorMsg}" class="alert alert-danger" th:text="${errorMsg}"></div>
</div>
errorMsg wouldn't show up when there is an error.
I have checked that errorMsg indeed get into RedirectAttributes, but it wouldn't display on the page;
Please set the value in redirected controller admin/login. Example
#RequestMapping(value = "admin/login", method = RequestMethod.GET)
public String OtherController(#ModelAttribute("errorMsg") String errorMsg, Model model) {
model.addAttribute("errorMsg", errorMsg);
return "login";//template name
}

How to check if checkbox is checked when submiting a form in Thymeleaf and Spring boot?

I want to check if the checkbox is checked when submitting a form.
I need to validate the user input at server side so I am using Spring MVC Form validator.
I am checking the form with a UserFormValidator class but I do not find how to validate the field checkbox.
The html code:
<form method="post" th:action="#{/addUser}" th:object="${userForm}">
<!-- other fields ... -->
<input type="checkbox" name="isTermsChecked" value="" th:checked="${isChecked}">
<span class="text-danger" th:text="${errorTermsChecked}"></span>
<button type="submit">Get Started</button>
</form>
That's what I did in the Controller class:
#PostMapping(value = "/addUser")
public ModelAndView addUser(#Valid #ModelAttribute("userForm") UserForm userForm, BindingResult bindingResult, String isTermsChecked) {
ModelAndView modelAndView = new ModelAndView();
boolean isChecked = false;
System.out.println("isTermsChecked: "+isTermsChecked);
//check is checkbox checked
if (isTermsChecked == null) {
modelAndView.addObject("isChecked", isChecked);
modelAndView.addObject("errorTermsChecked", "Please accept the Terms of Use.");
}else{
isChecked = true;
modelAndView.addObject("isChecked", isChecked);
modelAndView.addObject("errorTermsChecked", "");
}
if (bindingResult.hasErrors() || isTermsChecked == null) {
modelAndView.setViewName("view_addUser");
} else {
//add user ...
modelAndView.setViewName("view_addUser");
}
return modelAndView;
}
My code seems to work correctly and I do not know if it's the correct way.
I only removed th:field=*{checked} and everything is working properly and that's what I did:
<input name="checked" class="form-check-input" type="checkbox" th:checked="*{checked}" />
and in the controller:
#PostMapping(value = "/contact")
public String contactUsHome(#Valid #ModelAttribute("mailForm") final MailForm mailForm, BindingResult bindingResult)
throws MessagingException {
if (bindingResult.hasErrors()) {
return HOME_VIEW;
} else {
emailService.sendSimpleMail(mailForm);
return REDIRECT_HOME_VIEW;
}
}
and for the validation I used Spring Validation:
public class MailValidator implements Validator {
//...
#Override
public void validate(Object obj, Errors errors) {
//...
MailForm mailForm = (MailForm) obj;
validateChecked(errors, mailForm);
//...
}
private void validateChecked(Errors errors, MailForm mailForm) {
if (mailForm.isChecked() == false) {
errors.rejectValue("checked", "mailForm.checked");
}
}
}

Spring MVC variable resets for no reason

For some reason when I execute a GET request to a certain URI the variable that I need to access in that method loses its memory or points to null.
I have a form where a user can update his personal information. But when he enters a duplicate, it redirects him to a page that lets him know
I have : private static volatile User currentUser;
This field is set when a user logs in and the server performs a GET request to a REST API, which I programmed myself, and returns the User containing his info. This works as expected and the user info is displayed on his home screen.
Code for the above:
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(#ModelAttribute Credentials credentials,
RedirectAttributes redirect) {
RestTemplate restTemplate = new RestTemplate();
RoleInfo roleInfo = restTemplate.postForObject(
"http://localhost:9090/users/login", credentials,
RoleInfo.class);
if (roleInfo != null) {
if (roleInfo.isAdmin()) {
redirect.addFlashAttribute("credentials", credentials);
return "redirect:/adminHome";
} else {
redirect.addFlashAttribute("credentials", credentials);
return "redirect:/getBasicUser";
}
} else {
return "login_fail";
}
}
#RequestMapping(value = "/getBasicUser", method = RequestMethod.GET)
public <T> String getBasicUser(#ModelAttribute Credentials credentials,
Model model, RedirectAttributes redirect) {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:9090/users/getBasicUser?username="
+ credentials.getUsername();
ResponseEntity<User> responseEntity = restTemplate.exchange(
url,
HttpMethod.GET,
new HttpEntity<T>(createHeaders(credentials.getUsername(),
credentials.getPassword())), User.class);
User user;
user = responseEntity.getBody();
currentUser = user;
System.out.println("current user: " + currentUser.getUsername());
if (user != null) {
userName = credentials.getUsername();
passWord = credentials.getPassword();
redirect.addFlashAttribute("credentials", credentials);
redirect.addFlashAttribute("user", user);
return "redirect:/basicHome";
} else {
return "register_fail";
}
}
So on "basicHome" he can view his information. Also on that page is a link to a form where he can edit the information:
#RequestMapping(value = "/edit", method = RequestMethod.GET)
public String getEditProfilePage(Model model) {
model.addAttribute("currentUser", currentUser);
System.out.println("current use firstname: " + currentUser.getFirstname());
model.addAttribute("user", new User());
return "edit_profile";
}
If an edit is successful he is returned back to his home page with the updated information.
The problem comes when he enters invalid info. He should be redirected back to the "/edit" URI and the currentUserfield should still hold his information but is actually null.
Here is the "/edit" PUT function:
#RequestMapping(value = "/edit", method = RequestMethod.PUT)
public <T> String editProfile(#ModelAttribute("user") User user,
#ModelAttribute("credentials") Credentials credentials,
RedirectAttributes redirect) {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:9090/users/update?username=" + userName;
HttpHeaders headers = createHeaders(userName,
passWord);
#SuppressWarnings({ "unchecked", "rawtypes" })
HttpEntity<T> entity = new HttpEntity(user, headers);
ResponseEntity<User> responseEntity = restTemplate.exchange(url,
HttpMethod.PUT, entity, User.class);
User returnedUser = responseEntity.getBody();
currentUser = returnedUser;
if (returnedUser != null) {
redirect.addFlashAttribute("user", returnedUser);
redirect.addFlashAttribute("credentials", credentials);
return "redirect:/basicHome";
} else {
return "redirect:/editFail";
}
}
I figured out what I had to do. I basically made "user" a session object in: #SessionAttributes("user")

Spring 4.0 missing validation errors

Not sure what am I doing wrong - but after I submit the person form without any value
I am unable to see any validation error in the html output.
When I add a breakpoint in the controller I am able to see the "errors"
So it is going to result.hasErrors() tried to add * form:errors path="*" - still nothing
but still errors are not on the form.
Get method:
Person class is a POJO with no annotations.
#RequestMapping(value="/person/add" , method = RequestMethod.GET)
public ModelAndView personAdd() {
ModelAndView modelAndView = new ModelAndView("personAdd");
Person person = new Person();
person.setCreationDate(new Date());
modelAndView.addObject(person);
return modelAndView;
}
post method to save the new person
#RequestMapping(value="/person/add" , method = RequestMethod.POST)
public ModelAndView processSubmit(#ModelAttribute("person") Person person,BindingResult result) {
personValidator.validate(person, result);
if (result.hasErrors()) {
ModelAndView modelAndView = new ModelAndView("personAdd");
modelAndView.addObject(person);
return modelAndView;
} else {
ModelAndView modelAndView = new ModelAndView("refreshParent");
dao.persist(person);
return modelAndView;
}
}
The personValidator:
#Override
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "personName","required.personName", "Name is required.");
The Person form (for simplicity only name is there)
<form:form method="POST" modelAttribute="person" action="${pageContext.request.contextPath}/person/add">
<form:errors path="*" cssClass="errorblock" element="div"/>
<form:errors path="*" />
<div class="form-group">
<label class="control-label" for="inputError">Person Name:</label>
<form:input path="personName" class="form-control" placeholder="personName"/>
<form:errors path="personName" cssClass="error" />
</div>
<form:form>
ModelAndView modelAndView = new ModelAndView("personAdd");
This line constructs a a new ModelAndView and by doing that you dismiss the current model. You add only the object in the line following this, but you have effectivly destroyed the binding results already available. If you use this construct pass in the model from the BindingResults. And you don't have to add the model object anymore as that is already included.
ModelAndView modelAndView = new ModelAndView("personAdd", result.getModel());
However with the annotation driven #Controller you don't have to return a ModelAndView in this case a simple String would suffice.
#RequestMapping(value="/person/add" , method = RequestMethod.POST)
public ModelAndView processSubmit(#ModelAttribute("person") Person person,BindingResult result) {
personValidator.validate(person, result);
if (result.hasErrors()) {
return "personAdd";
} else {
dao.persist(person);
return "refreshParent";
}
}
This will render the correct view and leave the current model (which contains the errors) intact.
You could even apply automatic validation by adding #Valid to your model attribute argument and include a #InitBinder annotated method.
#RequestMapping(value="/person/add" , method = RequestMethod.POST)
public ModelAndView processSubmit(#Valid #ModelAttribute("person") Person person,BindingResult result) {
if (result.hasErrors()) {
return "personAdd";
} else {
dao.persist(person);
return "refreshParent";
}
}
#InitBinder
public void initBinder(WebDataBinder dataBinder) {
dataBinder.setValidator(personValidator);
}
Try to put this configuration:
#RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(#Valid #ModelAttribute("pet") Pet pet, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}
And put this in your person class
#NotNull
String personName;
And do not forget to place in your Spring XML file or the same but in your java configuration
<mvc:annotation-driven/>
All this will do the validation

Resources