JSF 2.3 + Bean Validation

Bean Validation ist schon längst fester Bestandteil von JSF Projekten und sorgt dafür das Validierungs-Regeln unabhängig von JSF implementiert werden können. Eine Anforderung kommt dabei immer wieder auf: Feldübergreifende Validierung. Seit JSF 2.3 ist das nun möglich, hier ein kurzer Blick darauf und was mit „groups“ möglich ist

g4542
Fast schon ein alter Hut, die Deklaration von Pflichtfeldern und Mindestlängen für String-Eingaben:

public class DemoModel {

    @NotNull
    @Size(min = 4)
    private String firstname;

    @NotNull
    @Size(min = 4)
    private String lastname;

    private boolean reciveNewsletter;

JSF wird diese Regeln automatisch in der Validation-Phase heranziehen, diese prüfen und bei Fehlern FacesMessage an den Benutzer weiter geben. Diese Validierung findet ganz automatisch statt und stellt den Default-Fall war.

Ein spannendes Features von Bean Validation sind die so genannten „groups“. Hier können Regeln definiert werden die nur in bestimmten Fällen validiert werden sollen (z.B. auf Basis eines Status oder wenn zugehörige Daten erfasst wurden). Solche Gruppen sind im Kern nur ein Marker-Interface welches dem entsprechenden Attribut der Bean Validation Regel übergeben wird:

public interface NewsletterReciver {
    
}

public class DemoModel {
    ...

    @Min(value = 18, groups = NewsletterReciver.class)
    @NotNull(groups = NewsletterReciver.class)
    private Integer age;

= die Validierung dieser Regeln erfolgt nur bei aktivierter Gruppe „NewsletterReciver“. Gruppen können dann bei der programmatischen Prüfung mittels javax.validation.Validator angegeben werden oder aber auch in die JSF-View integriert werden:

            <h:panelGroup class="input">
                <h:outputLabel value="Newsletter empfangen:" for="reciveNewsletter" />
                <h:selectBooleanCheckbox id="reciveNewsletter" value="#{demoController.model.reciveNewsletter}">
                    <f:ajax event="change" render="age" />
                </h:selectBooleanCheckbox>
            </h:panelGroup>
            <h:panelGroup class="input">
                <h:outputLabel value="Alter:" for="age" /> 
                <h:inputText id="age" value="#{demoController.model.age}">
                    <f:validateBean validationGroups="de.gedoplan.blog.jsf.validation.groups.NewsletterReciver"  disabled="#{!demoController.model.reciveNewsletter}"/>
                </h:inputText>
            </h:panelGroup>

(in unserem Beispiel müssen alle Newsletter-Empfänger mindestens 18 Jahre alt sein)

In JSF 2.3 wurden die Möglichkeiten noch erweitert und ein neues Tag-Hinzugefügt welches es uns erlaubt eine Feld-übergreifende Validierung durch zu führen. Dazu sind folgende Schritte nötig.

1. Aktivierung WholeBeanValidation

    <context-param>
        <param-name>javax.faces.validator.ENABLE_VALIDATE_WHOLE_BEAN</param-name>
        <param-value>true</param-value>
    </context-param>

web.xml

2. Eigenen Bean Validation Validator implementieren

@Constraint(validatedBy = ValidAddressValidator.class)
@Target(value = {ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface ValidAddress {

    String message() default "Adresse unvollständig";

    Class[] groups() default {};

    Class[] payload() default {};
}

@ApplicationScoped
public class ValidAddressValidator implements ConstraintValidator {
    public boolean isValid(AddressModel value, ConstraintValidatorContext context) {
        Object[] attributes = new Object[]{value.getNumber(), value.getCity(), value.getStreet()};
        return Stream.of(attributes).allMatch(Objects::isNull) || Stream.of(attributes).noneMatch(Objects::isNull) ;
    }
}

(unser Beispiel validiert ein Adress-Objekt, es sollen entweder alle Felder gefüllt sein oder gar keins)

3. JSF View erweitern

<h:form>
    ...

    <f:validateWholeBean value="#{demoController.model.address}" />
</h:form>

Der entscheidenden Vorteil gegenüber einer „händischen“ Prüfung innerhalb der Submit Methode (s. DemoController@GitHub ) besteht darin das die fehlerhaften Werte bei der Verwendung dieser JSF-Variante noch nicht ins Model übertragen wurden. Stattdessen erstellt JSF eine Kopie des zu prüfenden Objektes und überträgt die Werte erst dann wenn die Prüfung erfolgreich war.

Github? Klar.

Werbeanzeigen

Über Dominik Mathmann
Dominik Mathmann arbeitet als Berater, Entwickler und Trainer bei der GEDOPLAN GmbH. Auf Basis seiner langjährigen Erfahrung in der Implementierung von Java-EE-Anwendungen leitet er Seminare, hält Vorträge und unterstützt Kunden bei der Konzeption und Realisierung von Webanwendungen vom Backend bis zum Frontend. Sein derzeitiger Schwerpunkt liegt dabei auf der Entwicklung von JSF-Anwendungen. Er sieht die stärker werdende Position von JavaScript-Frameworks jedoch positiv und beschäftigt sich darüber hinaus mit Webframeworks wie Angular.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden /  Ändern )

Google Foto

Du kommentierst mit Deinem Google-Konto. Abmelden /  Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden /  Ändern )

Verbinde mit %s

%d Bloggern gefällt das: