Einführung in die Validierungs-API von Vavr

Einführung in die Validierungs-API von Vavr

1. Überblick

Die Validierung ist eine häufig vorkommende Aufgabe in Java-Anwendungen, und daher wurde viel Aufwand in die Entwicklung von Validierungsbibliotheken gesteckt.

Vavr (früher bekannt als Javaslang) liefert ein vollwertigesvalidation API. Es ermöglicht uns die einfache Validierung von Daten mithilfe eines objektfunktionalen Programmierstils. Wenn Sie einen Blick auf das Angebot dieser Bibliothek werfen möchten, können Siethis article überprüfen.

In diesem Tutorial werfen wir einen detaillierten Blick auf die Validierungs-API der Bibliothek und lernen, wie die wichtigsten Methoden verwendet werden.

2. DieValidation-Schnittstelle

Die Validierungsschnittstelle von Vavr basiert auf einem funktionalen Programmierkonzept, das alsapplicative functor bezeichnet wird. Es führt eine Folge von Funktionen aus, während die Ergebnisse akkumuliert werden, selbst wenn einige oder alle dieser Funktionen während der Ausführungskette fehlschlagen.

Der anwendungsbezogene Funktor der Bibliothek basiert auf den Implementierern derValidation-Schnittstelle. Diese Schnittstelle bietet Methoden zum Sammeln von Validierungsfehlern und validierten Daten, sodass beide als Stapel verarbeitet werden können.

3. Benutzereingaben überprüfen

Das Validieren von Benutzereingaben (z. B. von Daten, die von einer Webebene gesammelt wurden) ist unter Verwendung der Validierungs-API reibungslos, da eine benutzerdefinierte Validierungsklasse erstellt wird, die die Daten validiert und gegebenenfalls die daraus resultierenden Fehler ansammelt.

Überprüfen Sie den Namen und die E-Mail-Adresse eines Benutzers, die über ein Anmeldeformular übermittelt wurden. Zuerst müssen wirVavr’s Maven artifact in diepom.xml-Datei aufnehmen:


    io.vavr
    vavr
    0.9.0

Als Nächstes erstellen wir eine Domänenklasse, die Benutzerobjekte modelliert:

public class User {
    private String name;
    private String email;

    // standard constructors, setters and getters, toString
}

Definieren wir abschließend unseren benutzerdefinierten Validator:

public class UserValidator {
    private static final String NAME_PATTERN = ...
    private static final String NAME_ERROR = ...
    private static final String EMAIL_PATTERN = ...
    private static final String EMAIL_ERROR = ...

    public Validation, User> validateUser(
      String name, String email) {
        return Validation
          .combine(
            validateField(name, NAME_PATTERN, NAME_ERROR),
            validateField(email, EMAIL_PATTERN, EMAIL_ERROR))
          .ap(User::new);
    }

    private Validation validateField
      (String field, String pattern, String error) {

        return CharSeq.of(field)
          .replaceAll(pattern, "")
          .transform(seq -> seq.isEmpty()
            ? Validation.valid(field)
            : Validation.invalid(error));
    }
}

Die KlasseUserValidatorüberprüft den angegebenen Namen und die E-Mail-Adresse einzeln mit der MethodevalidateField(). In diesem Fall führt diese Methode einen typischen auf regulären Ausdrücken basierenden Mustervergleich durch.

Das Wesentliche in diesem Beispiel ist die Verwendung der Methodenvalid(),invalid() undcombine().

4. Die Methodenvalid(),invalid() undcombine()

Wenn der angegebene Name und die angegebene E-Mail-Adresse mit den angegebenen regulären Ausdrücken übereinstimmen, ruft die MethodevalidateField()valid() auf. Diese Methode gibt eine Instanz vonValidation.Valid zurück. Wenn umgekehrt die Werte ungültig sind, gibt die Methodeinvalid()des Gegenstücks eine Instanz vonValidation.Invalid zurück.

Dieser einfache Mechanismus, der auf der Erstellung unterschiedlicherValidation-Instanzen in Abhängigkeit von den Validierungsergebnissen basiert, sollte uns zumindest eine grundlegende Vorstellung davon geben, wie die Ergebnisse verarbeitet werden sollen (mehr dazu in Abschnitt 5).

Die relevanteste Facette des Validierungsprozesses ist diecombine()-Methode. Intern verwendet diese Methode die KlasseValidation.Builder, mit der bis zu 8 verschiedeneValidation-Instanzen kombiniert werden können, die mit verschiedenen Methoden berechnet werden können:

static  Builder combine(
  Validation validation1, Validation validation2) {
    Objects.requireNonNull(validation1, "validation1 is null");
    Objects.requireNonNull(validation2, "validation2 is null");
    return new Builder<>(validation1, validation2);
}

Die einfachsteValidation.Builder-Klasse benötigt zwei Validierungsinstanzen:

final class Builder {

    private Validation v1;
    private Validation v2;

    // standard constructors

    public  Validation, R> ap(Function2 f) {
        return v2.ap(v1.ap(Validation.valid(f.curried())));
    }

    public  Builder3 combine(
      Validation v3) {
        return new Builder3<>(v1, v2, v3);
    }
}

Validation.Builder, gibt zusammen mit derap(Function)-Methode ein einzelnes Ergebnis mit den Validierungsergebnissen zurück. Wenn alle Ergebnisse gültig sind, ordnet die Methodeap(Function)die Ergebnisse einem einzelnen Wert zu. Dieser Wert wird in einerValid-Instanz mithilfe der in der Signatur angegebenen Funktion gespeichert.

In unserem Beispiel wird ein neuesUser-Objekt erstellt, wenn der angegebene Name und die angegebene E-Mail-Adresse gültig sind. Natürlich ist es möglich, etwas völlig anderes mit einem gültigen Ergebnis zu tun, d. H. Es in einer Datenbank zu speichern, es per E-Mail zu senden und so weiter.

5. Validierungsergebnisse verarbeiten

Es ist ziemlich einfach, verschiedene Mechanismen zur Verarbeitung von Validierungsergebnissen zu implementieren. Aber wie validieren wir Daten überhaupt? Insofern verwenden wir die KlasseUserValidator:

UserValidator userValidator = new UserValidator();
Validation, User> validation = userValidator
  .validateUser("John", "[email protected]");

Sobald eine Instanz vonValidation erhalten wurde, können wir die Flexibilität der Validierungs-API nutzen und die Ergebnisse auf verschiedene Arten verarbeiten.

Lassen Sie uns die am häufigsten anzutreffenden Ansätze näher erläutern.

5.1. Die InstanzenValid undInvalid

Dieser Ansatz ist bei weitem der einfachste. Es besteht aus der Überprüfung der Validierungsergebnisse mit den InstanzenValid undInvalid:

@Test
public void
  givenInvalidUserParams_whenValidated_thenInvalidInstance() {
    assertThat(
      userValidator.validateUser(" ", "no-email"),
      instanceOf(Invalid.class));
}

@Test
public void
  givenValidUserParams_whenValidated_thenValidInstance() {
    assertThat(
      userValidator.validateUser("John", "[email protected]"),
      instanceOf(Valid.class));
}

Anstatt die Gültigkeit der Ergebnisse mit den InstanzenValid undInvalidzu überprüfen, sollten wir nur einen Schritt weiter gehen und die MethodenisValid() undisInvalid()verwenden.

5.2. Die APIsisValid() undisInvalid()

Die Verwendung des TandemsisValid() /isInvalid() ist analog zum vorherigen Ansatz, mit dem Unterschied, dass diese Methoden je nach Validierungsergebnissentrue oderfalse zurückgeben:

@Test
public void
  givenInvalidUserParams_whenValidated_thenIsInvalidIsTrue() {
    assertTrue(userValidator
      .validateUser("John", "no-email")
      .isInvalid());
}

@Test
public void
  givenValidUserParams_whenValidated_thenIsValidMethodIsTrue() {
    assertTrue(userValidator
      .validateUser("John", "[email protected]")
      .isValid());
}

Die Instanz vonInvalidenthält alle Validierungsfehler. Sie können mit der MethodegetError()abgerufen werden:

@Test
public void
  givenInValidUserParams_withGetErrorMethod_thenGetErrorMessages() {
    assertEquals(
      "Name contains invalid characters, Email must be a well-formed email address",
      userValidator.validateUser("John", "no-email")
        .getError()
        .intersperse(", ")
        .fold("", String::concat));
 }

Wenn umgekehrt die Ergebnisse gültig sind, kann die Instanz vonUsermit der Methodeget()erfasst werden:

@Test
public void
  givenValidUserParams_withGetMethod_thenGetUserInstance() {
    assertThat(userValidator.validateUser("John", "[email protected]")
      .get(), instanceOf(User.class));
 }

Dieser Ansatz funktioniert wie erwartet, aber der Code sieht immer noch ziemlich ausführlich und langwierig aus. Wir können es mit dertoEither()-Methode weiter verdichten.

5.3. DietoEither() API

Die MethodetoEither() erstellt Instanzen vonLeft undRight der SchnittstelleEither. Diese ergänzende Schnittstelle verfügt über mehrere praktische Methoden, mit denen die Verarbeitung von Validierungsergebnissen verkürzt werden kann.

Wenn die Ergebnisse gültig sind, wird das Ergebnis in der Instanz vonRightgespeichert. In unserem Beispiel würde dies einem gültigenUser-Objekt entsprechen. Wenn umgekehrt die Ergebnisse ungültig sind, werden die Fehler in der Instanz vonLeftgespeichert:

@Test
public void
  givenValidUserParams_withtoEitherMethod_thenRightInstance() {
    assertThat(userValidator.validateUser("John", "[email protected]")
      .toEither(), instanceOf(Right.class));
}

Der Code sieht jetzt viel prägnanter und übersichtlicher aus. Aber wir sind noch nicht fertig. DieValidation-Schnittstelle bietet diefold()-S-Methode, die eine benutzerdefinierte Funktion anwendet, die für gültige Ergebnisse gilt, und eine andere für ungültige.

5.4. Diefold() API

Lassen Sie uns sehen, wie die Methodefold()zur Verarbeitung von Validierungsergebnissen verwendet wird:

@Test
public void
  givenValidUserParams_withFoldMethod_thenEqualstoParamsLength() {
    assertEquals(2, (int) userValidator.validateUser(" ", " ")
      .fold(Seq::length, User::hashCode));
}

Die Verwendung vonfold() reduziert die Verarbeitung von Validierungsergebnissen auf nur einen Einzeiler.

Hervorzuheben ist, dass die als Argumente an die Methode übergebenen Rückgabetypen der Funktionen identisch sein müssen. Darüber hinaus müssen die Funktionen durch die in der Validierungsklasse definierten Typparameter unterstützt werden, d. H.Seq<String> undUser.

6. Fazit

In diesem Artikel haben wir die Validierungs-API von Vavr eingehend untersucht und gelernt, wie einige der wichtigsten Methoden verwendet werden. Eine vollständige Liste finden Sie inofficial docs API.

Die Validierungskontrolle von Vavr bietet eine sehr ansprechende Alternative zu herkömmlichen Implementierungen vonJava Beans Validation, wieHibernate Validator.

Wie üblich sind alle im Artikel gezeigten Beispiele inover on GitHub verfügbar.