Note
|
This repository contains the guide documentation source. To view the guide in published form, view it on the Open Liberty website. |
Explore the use of bean validation to validate user input data for microservices.
You will learn the basics of writing and testing a microservice that uses bean validation and the new functionality of Bean Validation 2.0. The service uses bean validation to validate that the supplied JavaBeans meet the defined constraints.
Bean Validation is a Java specification that simplifies data validation and error checking. Bean validation uses a standard way to validate data stored in JavaBeans. Validation can be performed manually or with integration with other specifications and frameworks, such as Contexts and Dependency Injection (CDI), Java Persistence API (JPA), or JavaServer Faces (JSF). To set rules on data, apply constraints by using annotations or XML configuration files. Bean validation provides both built-in constraints and the ability to create custom constraints. Bean validation allows for validation of both JavaBean fields and methods. For method-level validation, both the input parameters and return value can be validated.
Several additional built-in constraints are included in Bean Validation 2.0,
which reduces the need for custom validation in common validation scenarios.
Some of the new built-in constraints include @Email
, @NotBlank
, @Positive
,
and @Negative
. Also in Bean Validation 2.0, you can now specify constraints
on type parameters.
The example microservice uses both field-level and method-level validation as well as several of the built-in constraints and a custom constraint.
Point your browser to the http://localhost:9080/openapi/ui
URL. You see the OpenAPI
user interface documenting the REST endpoints used in this guide. If you are interested
in learning more about OpenAPI, read Documenting RESTful APIs.
Expand the /beanvalidation/validatespacecraft POST request to validate your
spacecraft bean
section and click Try it out. Copy the following example input
into the text box:
{
"astronaut": {
"name": "Libby",
"age": 25,
"emailAddress": "libbybot@openliberty.io"
},
"destinations": {
"Mars": 500
},
"serialNumber": "Liberty0001"
}
Click Execute and you receive the response No Constraint Violations
because the
values specified pass the constraints you will create in this guide. Now try copying
the following value into the box:
{
"astronaut": {
"name": "Libby",
"age": 12,
"emailAddress": "libbybot@openliberty.io"
},
"destinations": {
"Mars": 500
},
"serialNumber": "Liberty0001"
}
This time you receive Constraint Violation Found: must be greater than or equal
to 18
as a response because the age specified was under the minimum age of 18. Try
other combinations of values to get a feel for the constraints that will be defined
in this guide.
Navigate to the start
directory to begin.
First, create the JavaBeans to be constrained. Create the Astronaut
bean class
in the src/main/java/io/openliberty/guides/beanvalidation/Astronaut.java
file:
link:finish/src/main/java/io/openliberty/guides/beanvalidation/Astronaut.java[role=include]
The bean stores the attributes of an astronaut, name
, age
, and emailAddress
, and
provides getters and setters to access and set the values.
The Astronaut
class has the following constraints applied:
-
The astronaut needs to have a name. Bean Validation 2.0 provides a built-in
@NotBlank
constraint, which ensures the value is not null and contains one non-whitespace character. The annotation constrains thename
field. -
The email supplied needs to be a valid email address. Another built-in constraint in Bean Validation 2.0 is
@Email
, which can validate that theAstronaut
bean includes a correctly formatted email address. The annotation constrains theemail
field. -
The astronaut needs to be between 18 and 100 years old. Bean validation allows you to specify multiple constraints on a single field. The
@Min
and@Max
built-in constraints applied to theage
field check that the astronaut is between the ages of 18 and 100.
In this example, the annotation is on the field value itself. You can also place the annotation on the getter method, which has the same effect.
Create another class to represent a spacecraft in the
src/main/java/io/openliberty/guides/beanvalidation/Spacecraft.java
file:
link:finish/src/main/java/io/openliberty/guides/beanvalidation/Spacecraft.java[role=include]
The Spacecraft
bean contains 3 fields, astronaut
, serialNumber
, and
destination
. The JavaBean needs to be a CDI managed bean to allow for
method-level validation, which uses CDI interceptions. Because the
spacecraft
bean is a CDI managed bean, a scope is necessary. A request
scope is used in this example. To learn more about CDI, see
Injecting dependencies into microservices.
The Spacecraft
class has the following constraints applied:
-
Every destination that is specified needs a name and a positive distance. In Bean Validation 2.0, you can specify constraints on type parameters. The
@NotBlank
and@Positive
annotations constrain thedestination
map so that the destination name is not blank, and the distance is positive. The@Positive
constraint ensures that numeric value fields are greater than 0. -
A correctly formatted serial number is required. In addition to specifying the built-in constraints, you can create custom constraints to allow user-defined validation rules. The
@SerialNumber
annotation that constrains theserialNumber
field is a custom constraint, which will be created later.
Because you already specified constraints on the Astronaut
bean, the constraints do
not need to be respecified in the Spacecraft
bean. Instead, because of the @Valid
annotation on the field, all the nested constraints on the Astronaut
bean are
validated.
You can also use bean validation with CDI to provide method-level validation. The
launchSpacecraft
method on the Spacecraft
bean accepts a launchCode
parameter,
and if the launchCode
parameter is OpenLiberty
, the method returns true
that the
spacecraft is launched. Otherwise, the method returns false
. The launchSpacecraft
method uses both parameter and return value validation. The @NotNull
constraint
eliminates the need to manually check within the method that the parameter is not null.
Additionally, the method has the @AssertTrue
return-level constraint to enforce that
the method must return the true
boolean.
To create the custom constraint for @SerialNumber
, begin by creating an annotation in
the src/main/java/io/openliberty/guides/beanvalidation/SerialNumber.java
file:
link:finish/src/main/java/io/openliberty/guides/beanvalidation/SerialNumber.java[role=include]
The @Target
annotation indicates the element types to which you can apply the custom
constraint. Because the SerialNumber
constraint is used only on a field, only the
FIELD
target is specified.
When you define a constraint annotation, the specification requires the RUNTIME
retention policy.
The @Constraint
annotation specifies the class that contains the validation logic for
the custom constraint.
In the SerialNumber
body, the message()
method provides the message that is output
when a validation constraint is violated. The groups()
and payload()
methods
associate this constraint only with certain groups or payloads. The defaults are used
in the example.
Now, create the class that provides the validation for the @SerialNumber
constraint
in the src/main/java/io/openliberty/guides/beanvalidation/SerialNumberValidator.java
file:
link:finish/src/main/java/io/openliberty/guides/beanvalidation/SerialNumberValidator.java[role=include]
The SerialNumberValidator
class has one method, isValid()
, which contains the
custom validation logic. In this case, the serial number must start with Liberty
followed by 4 numbers, such as Liberty0001
. If the supplied serial number matches the
constraint, isValid
returns true
. If the serial number does not match, it returns
false
.
Finally, create a service that can be used to programmatically validate the constraints
on the Spacecraft
and Astronaut
JavaBeans in the
src/main/java/io/openliberty/guides/beanvalidation/BeanValidationEndpoint.java
file:
link:finish/src/main/java/io/openliberty/guides/beanvalidation/BeanValidationEndpoint.java[role=include]
Two resources, a validator and an instance of the Spacecraft
JavaBean, are injected
into the class. The default validator is used and is obtained through CDI
injection. However, you can also obtain the default validator with resource injection
or a JNDI lookup. The Spacecraft
JavaBean is injected so that the method-level
constraints can be validated.
The programmatic validation takes place in the validateSpacecraft
method. To validate
the data, the validate
method is called on the Spacecraft
bean. Because the
Spacecraft
bean contains the @Valid
constraint on the Astronaut
bean, both
JavaBeans are validated. Any constraint violations found during the call to the
validate
method are returned as a set of ConstraintViolation
objects.
The method level validation occurs in the launchSpacecraft
method. A call is then
made to the launchSpacecraft
method on the Spacecraft
bean, which throws a
ConstraintViolationException
exception if either of the method-level constraints is
violated.
Navigate to the http://localhost:9080/openapi/ui
URL. Expand the
/beanvalidation/validatespacecraft POST request to validate your spacecraft bean
section and click Try it out. Copy the following example input into the text box:
{
"astronaut": {
"name": "Libby",
"age": 25,
"emailAddress": "libbybot@openliberty.io"
},
"destinations": {
"Mars": 500
},
"serialNumber": "Liberty0001"
}
Click Execute and you receive the response No Constraint Violations
because
the values specified pass previously defined constraints.
Next, modify the following values, all of which break the previously defined constraints:
Age = 10
Email = libbybot
SerialNumber = Liberty1
After you click Execute, the response contains the following constraint violations:
Constraint Violation Found: serial number is not valid.
Constraint Violation Found: must be greater than or equal to 18
Constraint Violation Found: must be a well-formed email address
To try the method-level validation expand the /beanvalidation/launchspacecraft
POST request to specify a launch code
section. Enter OpenLiberty
in the text box
to note that launched
is returned because the launch code passes the defined
constraints. Replace OpenLiberty with anything else to note that a constraint
violation is returned.
Now, write automated tests to drive the previously created service. Create the
test class in the
src/test/java/it/io/openliberty/guides/beanvalidation/BeanValidationTest.java
file:
link:finish/src/test/java/it/io/openliberty/guides/beanvalidation/BeanValidationTest.java[role=include]
The @Before
annotation causes the setup
method to execute before the test cases.
The setup
method retrieves the port number for the Open Liberty server and creates
a Client
that is used throughout the tests, which are described as follows:
-
The
testNoFieldLevelConstraintViolations
test case verifies that constraint violations do not occur when valid data is supplied to theAstronaut
andSpacecraft
bean attributes. -
The
testFieldLevelConstraintViolation
test case verifies that the appropriate constraint violations occur when data that is supplied to theAstronaut
andSpacecraft
attributes violates the defined constraints. Because 3 constraint violations are defined, 3ConstraintViolation
objects are returned as a set from thevalidate
call. The 3 expected messages aremust be greater than 0
for the negative distance specified in the destination map,must be a well-formed email address
for the incorrect email address, and the customserial number is not valid
message for the serial number. -
The
testNoMethodLevelConstraintViolations
test case verifies that the method-level constraints that are specified on thelaunchSpacecraft
method of theSpacecraft
bean are validated when the method is called with no violations. In this test, the call to thelaunchSpacecraft
method is made with theOpenLiberty
argument. A value oftrue
is returned, which passes the specified constraints. -
The
testMethodLevelConstraintViolation
test case verifies that aConstraintViolationException
exception is thrown when one of the method-level constraints is violated. A call with an incorrect parameter,incorrectCode
, is made to thelaunchSpacecraft
method of theSpacecraft
bean. The method returnsfalse
, which violates the defined constraint, and aConstraintViolationException
exception is thrown. The exception includes the constraint violation message, which ismust be true
in this example.
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running it.io.openliberty.guides.beanvalidation.BeanValidationTest
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.493 sec - in
it.io.openliberty.guides.beanvalidation.BeanValidationTest
Results :
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
You developed and tested a Java microservice by using bean validation and Open Liberty.