Class FormField<I,O>
- Type Parameters:
I
- input value typeO
- output value type
This class is responsible for converting arbitrary data, sent to us by the web browser, into validated data structures that the server-side code can use. For example:
private enum Gender { MALE, FEMALE }
private static final FormField<String, String> NAME_FIELD = FormField.named("name")
.matches("[a-z]+")
.range(atMost(16))
.required()
.build();
private static final FormField<String, Gender> GENDER_FIELD = FormField.named("gender")
.asEnum(Gender.class)
.required()
.build();
public Person makePerson(Map<String, String> params) {
Person.Builder person = new Person.Builder();
for (String name : NAME_FIELD.extract(params).asSet()) {
person.setName(name);
}
for (Gender name : GENDER_FIELD.extract(params).asSet()) {
person.setGender(name);
}
return person.build();
}
This class provides full type-safety if and only if you statically initialize your FormField objects and write a unit test that causes the class to be loaded.
Exception Handling
When values passed to convert(I)
or extract(java.util.Map<java.lang.String, I>)
don't meet the contract, FormFieldException
will be thrown, which provides the field name and a short error message
that's safe to pass along to the client.
You can safely throw FormFieldException
from within your validator functions, and the
field name will automatically be propagated into the exception object for you.
In situations when you're validating lists or maps, you'll end up with a hierarchical field
naming structure. For example, if you were validating a list of maps, an error generated by the
bar
field of the fifth item in the foo
field would have a fully-qualified field
name of: foo[5][bar]
.
Library Definitions
You should never assign a partially constructed FormField.Builder
to a variable or
constant. Instead, you should use asBuilder()
or asBuilderNamed(String)
.
Here is an example of how you might go about defining library definitions:
final class FormFields {
private static final FormField<String, String> COUNTRY_CODE =
FormField.named("countryCode")
.range(Range.singleton(2))
.uppercased()
.in(ImmutableSet.copyOf(Locale.getISOCountries()))
.build();
}
final class Form {
private static final FormField<String, String> COUNTRY_CODE_FIELD =
FormFields.COUNTRY_CODE.asBuilder()
.required()
.build();
}
-
Nested Class Summary
-
Method Summary
Modifier and TypeMethodDescriptionReturns a builder of this object, which can be used to further restrict validation.asBuilderNamed
(String newName) Same asasBuilder()
but changes the field name.Convert and validate a raw user-supplied value.Convert and validate a raw user-supplied value from a map.extractUntyped
(Map<String, ?> jsonMap) Convert and validate a raw user-supplied value from an untyped JSON map.static FormField.Builder
<Map<String, ?>, Map<String, ?>> Returns a form field builder for validating JSON nested maps.name()
Returns the name of this field.static FormField.Builder
<String, String> Returns an optional string form field namedname
.static <T> FormField.Builder
<T, T> Returns an optional form field namedname
with a specificinputType
.
-
Method Details
-
named
Returns an optional string form field namedname
. -
named
Returns an optional form field namedname
with a specificinputType
. -
mapNamed
Returns a form field builder for validating JSON nested maps.Here's an example of how you'd use this feature:
private static final FormField<String, String> REGISTRAR_NAME_FIELD = FormField.named("name") .emptyToNull() .required() .build(); private static final FormField<Map<String, ?>, Registrar> REGISTRAR_FIELD = FormField.mapNamed("registrar") .transform(Registrar.class, new Function<Map<String, ?>, Registrar>() { @Nullable @Override public Registrar apply(@Nullable Map<String, ?> params) { Registrar.Builder builder = new Registrar.Builder(); for (String name : REGISTRAR_NAME_FIELD.extractUntyped(params).asSet()) { builder.setName(name); } return builder.build(); }}) .build();
When a
FormFieldException
is thrown, it'll be propagated to create a fully-qualified field name. For example, if the JSON input is{registrar: {name: ""}}
then thefield name
will beregistrar.name
. -
name
Returns the name of this field. -
convert
Convert and validate a raw user-supplied value.- Throws:
FormFieldException
- if value does not meet expected contracts.
-
extract
Convert and validate a raw user-supplied value from a map.This is the same as saying:
field.convert(valueMap.get(field.name())
- Throws:
FormFieldException
- if value does not meet expected contracts.
-
extractUntyped
Convert and validate a raw user-supplied value from an untyped JSON map.- Throws:
FormFieldException
- if value is wrong type or does not meet expected contracts.
-
asBuilder
Returns a builder of this object, which can be used to further restrict validation.- See Also:
-
asBuilderNamed
Same asasBuilder()
but changes the field name.
-