„Unmöglich können wir das Betragen anderer mit Strenge prüfen, wenn wir nicht selbst zuerst unsere Pflicht erfüllen.“
Demosthenes
Im letzten Beitrag wurde für die Klasse InternationalTelephoneNumber
aus dem Projekt Telephone ein Serialisierer und ein Deserialisierer für Jackson erstellt. Damit ist es möglich diese Klasse direkt in REST Request einzusetzen. Zusätzlich wäre eine Validierung der Telefonnummern wünschenswert. Auf diese Weise könnten Telefonnummern mit speziellen Vorwahlen oder Durchwahlen abgelehnt werden.
public class MemberDonation { @NotBlank private String firstname; @NotBlank private String lastname; @Past private LocalDate dayOfBirth; @GermanCountryCode private InternationalPhoneNumber phoneNumber; @Positive private Money donation; }
In dem hier dargestellten Beispiel, sollen durch die Annotation @GermanCountryCode
, nur Telefonnummern mit dem Country Code 49 zugelassen werden.
Ähnlich wie die Klasse InternationalPhoneNumber
haben auch die Klassen LocalDate
und Money
eine String Darstellung in der JSON Anfrage und werden durch spezielle Jackson Module (jackson-modules-java8
, jackson-datatype-money
) umgewandelt.
Um den Country Code per Annotation zu validieren, müssen zunächst die entsprechenden Annotation erstellt werden. Dies sind die folgenden Annotationen @CountryCode
und @GermanCountryCode
.
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint(validatedBy = {}) public @interface CountryCode { String message() default "{de.schegge.validator.CountryCode.message}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; String[] value(); boolean allowed() default true; }
Die allgemeinere Annotation @CountryCode
definiert eine Liste von Country Codes im Attribut value
und ein boolean
Flag allowed
. Ist das Flag positiv, dann sind die angegebenen Country Codes erlaubt, ansonsten sind sie verboten.
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint(validatedBy = {}) @CountryCode("49") public @interface GermanCountryCode { @OverridesAttribute(constraint = CountryCode.class) String message() default "{de.schegge.validator.GermanCountryCode.message}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; @OverridesAttribute(constraint = CountryCode.class) boolean allowed() default true; }
Die Annotation @GermanCountryCode
ist eine Spezialisierung der Annotation @CountryCode
und legt den Wert von value
auf "49"
fest.
Die Implementierung des CountryCodeValidator
für die Klasse InternationalPhoneNumber
ist recht einfach gehalten. Der Country Code des aktuellen Wertes wird in der vorgegebenen Liste gesucht. Das Ergebnis wird mit dem Flag verglichen. Ist das Flag positiv und wurde der Country Code gefunden ist die Validierung erfolgreich. Wurde der Country Code nicht gefunden und das Flag ist negativ, dann ist die Validierung auch erfolgreich.
public class CountryCodeValidator implements ConstraintValidator<CountryCode, InternationalPhoneNumber> { private List<String> countryCodes; private boolean allowed; @Override public void initialize(CountryCode constraintAnnotation) { countryCodes = Arrays.asList(constraintAnnotation.value()); allowed = constraintAnnotation.allowed(); } @Override public boolean isValid(InternationalPhoneNumber phoneNumber, ConstraintValidatorContext constraintValidatorContext) { if (phoneNumber == null) { return true; } return allowed == countryCodes.contains(phoneNumber.getCountryCode()); } }
Als Ergänzung zum CountryCodeValidator
gibt es noch den StringCountryCodeValidator
. Dieser Validator prüft den Country Code in Strings, die nach DIN 5008 formatierte Telefonnummern enthalten.
public class StringCountryCodeValidator implements ConstraintValidator<CountryCode, String> { private CountryCodeValidator wrapped; @Override public void initialize(CountryCode constraintAnnotation) { wrapped.initialize(constraintAnnotation); } @Override public boolean isValid(String phoneNumber, ConstraintValidatorContext constraintValidatorContext) { if (phoneNumber == null) { return true; } return wrapped.isValid(InternationalPhoneNumber.parse(phoneNumber), constraintValidatorContext); } }
Der StringCountryCodeValidator
verwendet den CountryCodeValidator
und delegiert das Validierung, wenn der String eine passende Telefonnummer enthält.
Die Validatoren für die nationale Vorwahl sind ganz ähnlich aufgebaut und bieten die Annotationen @NationalDestinationCode
zum validieren an. Zusätzlich gibt es die Annotation @GermanMobilNumber
, die @NationalDestinationCode
und @GermanCountryCode
kombiniert.
Wer die Validierung ausprobieren möchte, kann die Implementierungen auf Maven Central finden.
<dependency> <groupId>de.schegge</groupId> <artifactId>telephone</artifactId> <version>0.2.1</version> </dependency>