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
CreateOrderFormJavaBean so that:-
The
namemust 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, andcreamermust 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
CreateOrderFormparameter with@Validannotation. -
Add a
BindingResultparameter 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.htmltemplate 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:ifandth:errorsattributes, 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-coffeeand 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.