Lab14 order validation
Goal¶
Validate the Create Order form to tell the user if there are errors.
A. Add Validation Annotations¶
-
Add the following unit test to your project. When you run it, it should fail.
package com.welltestedlearning.cvm; import org.hibernate.validator.HibernateValidator; import org.junit.Before; import org.junit.Test; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import javax.validation.ConstraintViolation; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; public class CreateOrderFormValidationTest { private LocalValidatorFactoryBean localValidatorFactory; @Before public void setup() { localValidatorFactory = new LocalValidatorFactoryBean(); localValidatorFactory.setProviderClass(HibernateValidator.class); localValidatorFactory.afterPropertiesSet(); } @Test public void invalidCoffeeOrderFormShouldReport4Errors() { CoffeeOrderForm coffeeOrderForm = new CoffeeOrderForm(); coffeeOrderForm.setName("A"); // should not be only 1 letter coffeeOrderForm.setSize(""); // should not be blank coffeeOrderForm.setSweetener(""); // should not be blank coffeeOrderForm.setCreamer(""); // should not be blank Set<ConstraintViolation<CoffeeOrderForm>> constraintViolations = localValidatorFactory.validate(coffeeOrderForm); assertThat(constraintViolations) .hasSize(4); } @Test public void validFormShouldResultInNoErrors() throws Exception { CoffeeOrderForm coffeeOrderForm = new CoffeeOrderForm(); coffeeOrderForm.setName("Alicia"); coffeeOrderForm.setSize("LARGE"); coffeeOrderForm.setSweetener("sugar"); coffeeOrderForm.setCreamer("MILK"); Set<ConstraintViolation<CoffeeOrderForm>> constraintViolations = localValidatorFactory.validate(coffeeOrderForm); assertThat(constraintViolations) .isEmpty(); } }
-
Annotate the fields (member variables) of the
CreateOrderForm
JavaBean so that:-
The
name
must have 2 or more characters, but no more than 31 (so 31 is OK, but not 32).Class Name Conflict
Because our domain has a class
Size
, which is also the name of the validation constraint annotation, you'll have to use the fully-qualified package (i.e.,@javax.validation.constraints.Size()
) when applying the annotation. Using@Size()
by itself will cause a compilation error. -
The
size
,sweetener
, andcreamer
must not be blankValidation Annotations
Use this summary of annotations at: https://javaee.github.io/tutorial/bean-validation002.html.
-
-
Re-run the
CreateOrderFormValidationTest
, which should now pass.
B. Validate Form Submission¶
To turn on validation for the form submission, in the CoffeeOrderWebController
class,
find the method that's POST-mapped to /order-coffee
and:
-
Annotate the
CreateOrderForm
parameter with@Valid
annotation. -
Add a
BindingResult
parameter to the method (no annotations needed for this) -
Return the original form view if there are validation errors in the
BindingResult
(i.e.,result.hasErrors()
is true)
Example from Spring's PetClinic
This is an example from the PetClinic sample application from Spring:
@PostMapping("/owners/new")
public String processCreationForm(@Valid Owner owner, BindingResult result) {
if (result.hasErrors()) {
return "createOwnerForm";
}
owners.save(owner);
return "redirect:/owners/" + owner.getId();
}
C. Display Errors on Form¶
-
Update the
order-coffee.html
template and add the display of error messages next to each input field. For example, to display any errors for an accountName field, if you had this before:<p>Name: <input type="text" th:field="*{accountName}"/></p>
You would add a
<span>
tag with theth:if
andth:errors
attributes, like this:<p>Name: <input type="text" th:field="*{accountName}"/> <span th:if="${#fields.hasErrors('accountName')}" th:errors="*{accountName}">Name Error</span> </p>
-
You can also add a general error message error telling the customer that there are errors on the form like this:
<div th:if="${#fields.hasErrors('*')}" style="color: #dc3545; font-weight: 500;"> There's a problem with your order.</div>
More Validation and Error Messages
For more information and details about other ways of displaying errors can be found here: https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html#validation-and-error-messages.
For example, instead of
#fields.hasErrors('*')
you can use#fields.hasAnyErrors()
.And, instead of using an inline style (as above), you can use a CSS class if there are errors, e.g.:
th:errorclass="warning"
, which will only use the class if there are errors. -
Try it out by going to
localhost:8080/order-coffee
and seeing what errors you can cause to be displayed.
References
-
Summary of validation annotations: https://javaee.github.io/tutorial/bean-validation002.html
-
Hibernate Validator reference: https://docs.jboss.org/hibernate/validator/5.3/reference/en-US/html/index.html
-
Spring validation reference: https://docs.spring.io/spring/docs/4.3.20.RELEASE/spring-framework-reference/htmlsingle/#validation-beanvalidation
-
Thymeleaf form tutorial: http://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html#creating-a-form
-
Bean Validation specifications: http://beanvalidation.org/specification/
Once you've completed the above steps,
check in with the instructor to review your code.