I'm working on validating a form and showing inline errors for fields that don't pass validation using Thymeleaf. I'm using Spring Boot 1.5.8.
I have a User
object that will be bound to the form. It has fields like: username
and email
. Here's the form declaration,
<form role="form" class="form" method="post"
th:action="@{'/admin/user/new'}" th:object="${user}">
I set up an error controller like this because I wanted a catch all error page. I also created a template at src/main/resources/templates/error.html
. This was because the security context was lost on the error page according to this github issue, although it indicates the issue should be fixed in my version. Without this controller the default error template is shown, but I have no access to the security context. With this controller in place and the error.html
template any random error will be handled.
@Controller
public class ErrorController extends BasicErrorController {
/**
* This Bean declaration provides the Spring Security Context to 4xx responses. This is reported as
* fixed in Spring Boot 2.0.0:
*
* https://github.com/spring-projects/spring-boot/issues/1048
*
* @param springSecurityFilterChain
* @return
*/
@Bean
public FilterRegistrationBean getSpringSecurityFilterChainBindedToError(
@Qualifier("springSecurityFilterChain") Filter springSecurityFilterChain) {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(springSecurityFilterChain);
registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
return registration;
}
@Autowired
public ErrorController(ErrorAttributes errorAttributes) {
super(errorAttributes, new ErrorProperties());
}
@RequestMapping(value = "/error")
public String error(Model model) {
return "/error";
}
@Override
public String getErrorPath() {
return "/__dummyErrorPath";
}
}
For this example I'm just trying to validate the email
field and to show a message if validation fails next to this field. The class field looks like this on User
,
@NotNull
@Email
private String email;
The Thymeleaf field looks like this,
<div th:fragment="email" class="form-group">
<label>Email*</label>
<input class="form-control" type="text" th:field="*{email}" th:required="required" />
<span class="error" th:if="${#fields.hasErrors('email')}" th:errors="*{email}">...</span>
</div>
The new user form controller method looks like this,
@GetMapping("admin/user/new")
public String newUser(Model model, HttpServletRequest request) {
model.addAttribute("user", new User());
return "admin/user/new";
}
And the corresponding POST method looks like this,
@PostMapping("admin/user/new")
public String newUser(@Valid @ModelAttribute User user, final ModelMap model, BindingResult bindingResult) {
Assert.notNull(user, "User must not be null");
if (bindingResult.hasErrors()) {
return "admin/user/new";
}
userService.save(user);
model.clear();
return "redirect:/admin/user/list";
}
In this POST method I'm checking to see if the BindingResult
has errors. If it does I'm expecting the admin/user/new
template to be rendered with the inline error message shown. However my catch all error template is being rendered. The URL still shows /admin/user/new
and refreshing the page still shows the error template.
My main question is how can I use the catch all error page and still show inline errors when I need to if possible?