Error Handling in Thymeleaf and Spring boot - spring-boot

In my spring boot thymeleaf application, i am struggling to figure out how to display the error message returned from the service layer onto the UI.
My UI Code (index.html) is
<div class="u-expanded-width-xs u-form u-form-1">
<form class="u-clearfix u-form-horizontal u-form-spacing-10 u-inner-form"
method="POST"
modelAttribute="indexFormBean" name="form"
style="padding: 0;"
th:action="#{/home}" th:object="${indexFormBean}">
<div class="u-form-group u-form-name u-form-group-1">
<label for="email-dbf3"
class="u-form-control-hidden u-label">Email</label>
<input type="email" placeholder="Enter a valid email address"
id="email-dbf3" name="email"
class="u-border-1 u-border-grey-30 u-input u-input-rectangle u-white"
required="true" autofocus="autofocus">
</div>
<div class="u-form-email u-form-group u-form-group-2">
<label for="name-dbf3"
class="u-form-control-hidden u-label">Name</label>
<input type="text" placeholder="Enter a valid password" id="name-dbf3"
name="password"
class="u-border-1 u-border-grey-30 u-input u-input-rectangle u-white"
required="true">
</div>
<div class="u-align-left u-form-group u-form-submit u-form-group-3">
<button type="submit" name="submit" class="btn btn-primary btn-lg">Login
</button>
</div>
<p th:if="${#fields.hasErrors('email')}" th:errors="*{email}">Description errors</p>
</form>
</div>
The controller method invoked when the form is submitted is like this
#PostMapping("/home")
public String authenticate(#ModelAttribute IndexFormBean indexFormBean, Model model){
String loginResponse = userService.login(indexFormBean.getEmail(),
indexFormBean.getPassword());
if(StringUtils.isEmpty(loginResponse)){
//Some error that is returned from the service layer
return "index";
}
return "home";
}
Now if the authenticate method needs to show some error on the UI (index.html) how can I do so ?

There are two ways you can handle error messages in MVC.
Use an error.html template. Spring boot will automatically use this template if it encounters any uncaught exceptions within controller method.
Catch the exception in your controller method and add appropriate error message to your model. You can then use the error message somewhere in your index.html or home.html
You can do something like,
if(StringUtils.isEmpty(loginResponse)){
model.addAttribute("errorMessage","Login failed");
return "index";
}
And in your index.html, add something like,
<span th:if="${errorMessage}" th:text="${errorMessage}">Error</span>

Related

Spring Boot - Edit data

I did the get, post and delete method. But now I'm suffering to do the put method.
My controller:
#RequestMapping("/teste/equipe/editar/{id}")
public String update(#RequestBody Team newTeam ,#PathVariable("id") Long id)
{
Team team = teamService.findById(id);
team.setName(newTeam.getName());
team.setName(newTeam.getRole());
teamService.save(team);
return "redirect:/teste/equipe";
}
HTML:
<form action="/teste/equipe/editar/{id}" method="PUT">
<input type="text" th:field="${team.name}" placeholder="Nome">
<br>
<input type="text" th:field="${team.role}" placeholder="Função">
<button>
Enviar
</button>
</form>
Error:
Not certain this helps but the error on the whitelabel error page states:
Number Format Exception for input {id}
Seems like you're expecting {id} in your form:
<form action="/teste/equipe/editar/****{id}****" method="PUT">
to be interpolated with an actual ID. Should it instead be:
<form action="/teste/equipe/editar/${id}" method="PUT">

Thymeleaf + Spring. Using an object method instead another block in the form

I have a typical updateUser form built with Thymeleaf + Spring. I can list the user roles but I am trying to put these into a selectbox component. I am struggling with this line th:selected="${user.hasRole(role.role)}". Now, I know this component works and that is only a matter of access a boolean function to enable it. I am trying to reference a function of the form object using the select th:object. My syntax doesn't work. I have also try to access that function just like previous input tags would (only using the name without the object itself .ie username or in this case hasRole(). That doesn't work either.
<form th:object="${user}"
th:action="#{'/admin/usermanagement/adduser'} " method="post">
<div class="row">
<div class="col-md-3 form-group">
<label>Username:</label> <input type="text" class="form-control"
th:field="*{username}" />
</div>
</div>
<div class="col-md-3">
<h5>Roles :</h5>
</div>
<select class="js-example-basic-multiple" style="width: 75%"
name="froles" id="froles" multiple="multiple">
<option th:each="role : ${roles}" th:value="${role.role}"
th:selected="${user.hasRole(role.role)}"
th:text="${role.role}"></option>
</select>
<button type="submit" class="btn btn-primary">Submit</button>
In fact, my problem is types. th:selected="${user.hasRole(role.role)}" is a string and should be th:selected="${user.hasRole(role)}". I have both method in the domain object so that is fine.
th:value="${role.role}" is wrong for persistence. I want to return role but I get the following error message when I use role.role
codes [user.roles,roles]; arguments []; default message [roles]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.List' for property
and Java null exception when I use "${role}"

Can pass models to Spring, but Thymeleaf still indicates an error

My problem is that I can pass models between Thymeleaf and Spring, but Thymeleaf still indicates an error.
Spring code:
#GetMapping("{id}/edit")
String getEdit(#PathVariable Long id, Model model) {
postRepository.findById(id).ifPresent(o -> model.addAttribute("post", o));
return "edit";
}
#PostMapping("{id}/edit")
String postEdit(#ModelAttribute Post post) {
postRepository.save(post);
return "redirect:/";
}
Thymeleaf code:
<form th:action="|/${post.id}/edit|" th:method="POST" th:object="${post}">
<input type="text" th:value="*{title}" name="title">
<input type="text" th:value="*{content}" name="content">
<input type="submit" value="Edit">
</form>
Thymeleaf indicates that it can't resolve ${post.id}, *{title} and *{content}. I have stopped and rerun the application more times so I suppose something is amiss in my code, even if it works.
What should I do to solve this issue?
First of all I think you don't need path variable in post mapping. You can use post mapping without path variable. So try modifying you controller like
#PostMapping("/edit")
String postEdit(#ModelAttribute Post post) {
postRepository.save(post);
return "redirect:/";
}
If you write controller like this it will be easy defining path in thymeleaf.
And second error can't resolve *{title} and *{content} is because of invalid keyword. Please try modifying your thymeleaf like
<form th:action="#{/edit}" th:method="POST" th:object="${post}">
<input type="text" th:field="*{title}" name="title">
<input type="text" th:field="*{content}" name="content">
<input type="submit" value="Edit">
</form>
I think this will work as you are expecting.

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";
}

Hibernate Validator retrieve error messages without using Spring Framework

I am currently using hibernate-distribution-3.6.4.Final. New to Hibernate Validator.
Problem:
How should I retrieve error messages I got from adding annotations to databean/formbean? I know in Spring that everyone seems to use messages.properties file from the classpath?
But how about pure hibernate 3, is there any file like that or what should i do instead? (Didn't find good answers online...)
Hope this will help. Source is : Mastering Spring MVC
You will need to add a few more things for validation to work. First, the controller needs to say that it wants a valid model on form submission. Adding the javax.validation.Valid as wel as
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;
annotation to the parameter representing the form does just that:
#RequestMapping(value = "/profile", method = RequestMethod.POST)
public String saveProfile(#Valid ProfileForm profileForm,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "profile/profilePage";
}
System.out.println("save ok" + profileForm);
return "redirect:/profile";
}
Note that you do not redirect the user if the form contains any errors. This will allow you to display them on the same web page. Speaking of which, you need to add a place on the web page where those errors will be displayed.
Add these lines in profilePage.html:
<form th:action="#{/profile}" th:object="${profileForm}"
....">
<div class="row">
<div class="input-field col s6">
<input th:field="${profileForm.twitterHandle}"
id="twitterHandle" type="text" th:errorclass="invalid"/>
<label for="twitterHandle">Twitter handle</label>
<div th:errors="*{twitterHandle}" class="redtext">
Error</div>
</div>
<div class="input-field col s6">
<input th:field="${profileForm.email}" id="email"
type="text" th:errorclass="invalid"/>
<label for="email">Email</label>
<div th:errors="*{email}" class="red-text">Error</div>
</div>
</div>
<div class="row">
<div class="input-field col s6">
<input th:field="${profileForm.birthDate}"
id="birthDate" type="text" th:errorclass="invalid" th:placeholder="${
dateFormat}"/>
<label for="birthDate">Birth Date</label>
<div th:errors="*{birthDate}" class="red-text">Error</
div>
</div>
</div>
<div class="row s12">
<button class="btn indigo waves-effect waves-light"
type="submit" name="save">Submit
<i class="mdi-content-send right"></i>
</button>
</div>
</form>
Yes Indeed, Spring Boot takes care of creating a message source bean for us.
The default location for this message source is in
src/main/resources/messages.
properties.
Create such a bundle, and add the following text:
Size.profileForm.twitterHandle=Please type in your twitter user name
Email.profileForm.email=Please specify a valid email address
NotEmpty.profileForm.email=Please specify your email address
PastLocalDate.profileForm.birthDate=Please specify a real birth date
NotNull.profileForm.birthDate=Please specify your birth date
typeMismatch.birthDate = Invalid birth date format
.

Resources