How to validate a form with thymeleaf and spring?

I simply want to validate a form data and display an error message. Validating email format, password size and password matching. What would be the best way to do that?

Here's what I've tried

My controller

@PostMapping("/{customerId}/createUser")
public String signIn(@PathVariable(value = "customerId", required = false) Long customerId,
        @ModelAttribute(name = "user") @Valid Users user, RedirectAttributes redirectAttributes,
        BindingResult bindingResult) {

    if (bindingResult.hasErrors()) {
        return "customerNewUser";
    }

    // Encrypt password
    user.setPassword(encoder.encode(user.getPassword()));
    user.setCustomerId(customerId);
    user.setEventDescription("User Admin creation");

    try {
        Users returnedUser = userService.save(user);

        List<Authorities> authorities = new ArrayList<Authorities>();
        Authorities auth = new Authorities(new AuthoritiesPK(returnedUser.getId(), "ROLE_CLI_ADM"), returnedUser,
                "ROLE_CLI_ADM");
        authorities.add(auth);
        returnedUser.setAuthorities(authorities);
        returnedUser.setEventDescription("Add Admin role");

        for (int i = 0; i < returnedUser.getAuthorities().size(); i++) {
            authorityService.save(returnedUser.getAuthorities().get(i));
        }
        userService.save(returnedUser);
    } catch (WebExchangeBindException webe) {
        StringBuilder errorBuilder = new StringBuilder();
        for (ObjectError error : webe.getAllErrors()) {
            errorBuilder.append(messageSource.getMessage(error.getCode(), null, Locale.getDefault())).append("\n");
        }
        redirectAttributes.addFlashAttribute("signInError", errorBuilder.toString());
    }

    return "redirect:/customers/?id=" + customerId;
}

My form in my customerNewUser html file

<form
                        th:action="@{${user?.id} ? '/customers/' + ${customer.id} + '/createUser/' + ${user.id} : '/customers/' + ${customer.id} + '/createUser/'}"
                        th:object="${user}" action="#" method="post">

<div class="alert alert-danger" th:if="${#fields.hasAnyErrors()}">
                            <div th:each="detailedError : ${#fields.detailedErrors()}">
                                <span th:text="${detailedError.message}"></span>
                            </div>
                        </div>

                        <div class="form-group">
                            <div class="form-row">
                                <div class="col-md-5">
                                    <label class="text-right label-control"
                                        th:text="#{firstname} + ':'">Firstname:</label>
                                </div>
                                <div class="col-md-4">
                                    <input type="text" id="firstname" name="firstname"
                                        th:value="${user?.firstname} ? ${user?.firstname} : ''"
                                        class="form-control" th:placeholder="#{firstname}"
                                        required="required" autofocus="autofocus" th:field="*{firstname}"/>
                                        <label th:style="'color: red;'" class="text-right label-control" th:if="${#fields.hasErrors('firstname')}" th:errors="*{firstname}" th:text="#{passwordError}">Password Error</label>
                                </div>
                            </div>
                            </div>
                            <div class="form-group">
                                <div class="form-row">
                                    <div class="col-md-5">
                                        <label class="text-right label-control"
                                            th:text="#{surname} + ':'">Surname:</label>
                                    </div>
                                    <div class="col-md-4">
                                        <input type="text" id="surname" name="surname"
                                            th:value="${user?.surname} ? ${user?.surname} : ''"
                                            class="form-control" th:placeholder="#{surname}"
                                            autofocus="autofocus" />
                                    </div>
                                </div>
                            </div>
                            <div class="form-group">
                                <div class="form-row">
                                    <div class="col-md-5">
                                        <label class="text-right label-control"
                                            th:text="#{email} + ':'">Email:</label>
                                    </div>
                                    <div class="col-md-4">
                                        <input th:placeholder="#{email}" required="required"
                                            autofocus="autofocus" id="email" class="form-control"
                                            type="text" th:value="${user?.email} ? ${user?.email} : ''"
                                            name="email" th:field="*{email}"/>
                                            <label th:style="'color: red;'" class="text-right label-control" th:if="${#fields.hasErrors('email')}" th:errors="*{email}" th:text="#{emailError}">Email Error</label>
                                    </div>
                                </div>
                            </div>
                            <div class="form-group" th:if="!${user?.id}">
                                <div class="form-row">
                                    <div class="col-md-5">
                                        <label class="text-right label-control"
                                            th:text="#{password} + ':'">Password:</label>
                                    </div>
                                    <div class="col-md-4">
                                        <input type="password" id="password" name="password"
                                            class="form-control" th:placeholder="#{password}"
                                            required="required" autofocus="autofocus" th:field="*{password}"/>
                                            <label th:style="'color: red;'" class="text-right label-control" th:if="${#fields.hasErrors('password')}" th:errors="*{password}" th:text="#{passwordError}">Password Error</label>
                                    </div>
                                </div>
                            </div>
                            <div class="form-group" th:if="!${user?.id}">
                                <div class="form-row">
                                    <div class="col-md-5">
                                        <label class="text-right label-control"
                                            th:text="#{passwordConfirmation} + ':'">Password confirmation:</label>
                                    </div>
                                    <div class="col-md-4">
                                        <input type="password" id="matchPassword" name="matchPassword"
                                            class="form-control" th:placeholder="#{password}"
                                            required="required" autofocus="autofocus" th:field="*{matchPassword}"/>
                                            <label th:style="'color: red;'" class="text-right label-control" th:if="${#fields.hasErrors('matchPassword')}" th:errors="*{matchPassword}" th:text="#{matchPassword}">Match Password Error</label>
                                    </div>
                                </div>
                            </div>

                            <div class="form-group" th:if="${user?.id}">
                                <div class="form-row">
                                    <div class="col-md-5">
                                        <label class="text-right label-control"
                                            th:text="#{userStatus} + ':'">Status:</label>
                                    </div>
                                    <div class="col-md-3">
                                        <select class="form-control form-control" id="userStatus"
                                            name="userStatus">
                                            <option
                                                th:each="userStatus : ${T(br.com.macrosul.stetho.entity.UserStatus).values()}"
                                                th:text="${userStatus}" th:value="${userStatus}"
                                                th:selected="${user.userStatus} eq ${userStatus}"></option>
                                        </select>
                                    </div>
                                </div>
                            </div>
                            <div class="form-group">
                                <p class="error-control" th:text="${signInError}"></p>
                            </div>
                            <input type="submit" class="btn btn-md btn-block"
                                value="Sign in"
                                th:value="${user?.id} ? #{updateUser} : #{signIn}" />
                    </form>

And here is the stacktrace when I try to force a password size error ->

 @Size(min=6)
@Column(nullable = false)
private String password;

Field error in object 'user' on field 'password': rejected value [12345]; codes [Size.user.password,Size.password,Size.java.lang.String,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.password,password]; arguments []; default message [password],2147483647,6]; default message [tamanho deve estar entre 6 e 2147483647]

I want a simple validation. If there's a simpler way to indicate the user the data are invalid I would appreciate for any help or advice!

Upvotes: 0

Views: 2102

Answers (3)

Ant&#237;a Roig
Ant&#237;a Roig

Reputation: 11

It is a little bit late, but you need to put your BindingResult next to your @valid attribute so instead of:

@ModelAttribute(name = "user") @Valid Users user, RedirectAttributes redirectAttributes, BindingResult bindingResult

you should write:

@ModelAttribute(name = "user") @Valid Users user, BindingResult bindingResult, RedirectAttributes redirectAttributes

Upvotes: 1

Ant&#237;a Roig
Ant&#237;a Roig

Reputation: 11

You can put a div in the html that only exists if there are errors on the validation. Then you show each error in a loop:

<div th:if="${#fields.hasErrors('*')}">
  <p th:each="err : ${#fields.errors('*')}"> 
      <span th:text="${err}"></span>
  </p>
</div>

This way, when you return to this view there will be errors on your fields so this code will be shown.

Upvotes: 0

Ajay Kumar
Ajay Kumar

Reputation: 3282

You need a validator like below. Its not the exact code what you need but sufficient enough and pretty close to what you need to get you going.

@Override
    public void validate(Object o, Errors errors) {
     ValidationUtils.rejectIfEmptyOrWhitespace(errors, "useremail", "NotEmpty");
     ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userfirstname", "NotEmpty");
     ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userlastname", "NotEmpty");
     ValidationUtils.rejectIfEmptyOrWhitespace(errors, "useraddress", "NotEmpty");

     ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "NotEmpty");
        if (user.getPassword().length() < 8 || user.getPassword().length() > 32) {
        errors.rejectValue("password", "Size.userForm.password");
        }

        if (!user.getPasswordConfirm().equals(user.getPassword())) {
        errors.rejectValue("passwordConfirm", "Diff.userForm.passwordConfirm");
        }
    }

For more details you can take a look at here, here and here.

Upvotes: 0

Related Questions