# Validation über ManagedBeans



## Llois41 (29. Mai 2018)

Hallo,

ich möchte aus einem Formular zum "Profil bearbeiten" die eingegebene Mail überprüfen. Standardmäßig steht die des angemeldeten Nutzers bereits im entsprechenden InputField. Beim Speichern soll u.a. dieses Feld auf 3 Sachen geprüft werden:

1. Ist die Mail noch die gleiche wie in der DB hinterlegt (keine Änderung)?
2. Ist die geänderte Mail noch nicht vergeben (Das Attribut ist kein Primärschlüssel, sollte trotzdem unique sein)
3. Sollte die Mail bereits vergeben sein, soll eine message ausgegeben werden, welche den Nutzer informiert.

So sieht das Feld im Formular aus:


```
<b:column medium-screen="4">
                    <b:inputText id="mail" required="true"
                                 validatorMessage="Deine E-Mail-Adresse ist nicht gültig. Bitte gib Deine E-Mail-Adresse erneut ein!"
                                 requiredMessage="Deine E-Mail-Adresse ist nicht gültig. Bitte gib Deine E-Mail-Adresse erneut ein!"
                                 label="E-Mail" placeholder="name@example.com" value="#{profilManagedBean.mail}">
                        <f:validateRegex pattern="[\w\.-]*[a-zA-Z0-9_]@[\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]"/>
                        <f:ajax event="blur" execute="mail" render="m_mail"/>
                    </b:inputText>
                    <b:messages id="m_mail" for="mail"/>
                </b:column>
```

So die Bean-Methode, welche über den Speichern-Button in dem "action"-Attribut übergeben wird:


```
public String update() {
        if (!nutzer.getPasswort().equals(tempPassword) && tempPassword != null && !tempPassword.isEmpty()) {
            nutzer.setPasswort(tempPassword);
        }
        if (mail.equals(nutzer.getMail())) {
            nutzer.addBezirk(dao.findBezirkByID(bezirkID));
            updateSprachen();
            updateFreizeitaktivitaeten();
            dao.merge(nutzer);
            refreshNutzer();
            return "home";

        } else if(validateMail(mail)){
            nutzer.addBezirk(dao.findBezirkByID(bezirkID));
            updateSprachen();
            updateFreizeitaktivitaeten();
            nutzer.setMail(mail);
            dao.merge(nutzer);
            refreshNutzer();
            return "home";
        }
            else {
            FacesContext.getCurrentInstance().addMessage("formProfil:mail", new FacesMessage(
                    "Diese Mail wird bereits verwendet!"));
            return "profil";
        }
    }
```


Das Problem ist in meinen Augen, dass ich bei der "update"-Methode immer einen String, also eine .xhtml-Seite zurückgeben muss und daher die Seite wahrscheinlich sogar nach Ausgabe der richtigen Fehlermeldung neu geladen wird. Diesen benötige ich aber, um nach erfolgreichem Speichern automatisch auf die Homepage zu gelangen.

Wie kann ich das umgehen? 

Eine Alternative wäre keine Seite zurückzugeben, und damit auf die automatische Weiterleitung zu verzichten, oder?


----------



## Flown (29. Mai 2018)

Du könntest `null` zurückgeben. Das bewirkt, dass du auf der aktuellen Seite bleibst.


----------



## Llois41 (29. Mai 2018)

Funktioniert leider nicht, die Seite wird trotzdem aktualisiert und die Nachricht somit nicht angezeigt


----------



## Flown (29. Mai 2018)

Hilft dir vielleicht das hier weiter? https://stackoverflow.com/questions/9721547/ajax-onsubmit-validation-in-jsf-2-0


----------



## Llois41 (30. Mai 2018)

Flown hat gesagt.:


> Hilft dir vielleicht das hier weiter? https://stackoverflow.com/questions/9721547/ajax-onsubmit-validation-in-jsf-2-0



Ich habe jetzt versucht, das so umzusetzen, leider funktioniert auch das nicht.

Hier ist der Aufruf in der .xhtml:


```
<b:inputText id="mail" required="true"
                                 requiredMessage="Bitte geben Sie Ihre E-Mail-Adresse an!"
                                 label="E-Mail" placeholder="name@example.com" value="#{registrierenManagedBean.nutzer.mail}">
                        <f:validator validatorId="mailValidatorRegistrieren"/>
                        <b:messages for="mail"/>
                    </b:inputText>
```

So sieht der Validator aus:


```
@FacesValidator("mailValidatorRegistrieren")
public class MailValidatorRegistrieren implements Validator {

    @EJB
    private DAO dao;
    private String mail;
    private static final Pattern EMAIL_PATTERN =
            Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE);

    @Override
    public void validate(FacesContext facesContext, UIComponent uiComponent, Object o) throws ValidatorException {
        mail = (String)o;
        boolean matchesPattern = EMAIL_PATTERN.matcher(mail).find();

        if(!matchesPattern)
        {
            throw new ValidatorException((new FacesMessage("Ungültige E-Mail-Adresse","Bitte erneut eingeben!")));
        }

        if(mail.isEmpty()) {
            return;
        } else if(validateNutzer(mail)){
            throw new ValidatorException(new FacesMessage("Die E-Mail-Adresse ist bereits vergeben!"));
        } else{
            return;
        }

    }

    private boolean validateNutzer(String mail) {
        try {
            Nutzer n = dao.findNutzerByMail(mail);
            return n.getMail().equals(mail);
        } catch (NullPointerException e) {
            return false;
        }
    }
}
```

Und so die "findNutzerByMail"Methode in der DAO:


```
public Nutzer findNutzerByMail(String mail) {
        try {
            return em.createNamedQuery("findNutzerByMail", Nutzer.class)
                    .setParameter("mail", mail)
                    .getSingleResult();
        } catch (NoResultException e) {
            return null;
        }
    }
```

Das Problem ist, dass er nun trotz bereits existierender Mail in der DB einen Eintrag mit der gleichen Mail hinzufügt, dann aber direkt eine NonUniqueResultException wirft, da er ja nun 2 Einträge findet. Ich frage mich, wie das sein kann, denn eigentlich kommt die Validierung im JSF-Lebenszyklus doch vor der Invoke-Application-Phase und er müsste somit nach der Validierung abbrechen.

Sieht jemand den Fehler?


----------



## Llois41 (30. Mai 2018)

Gleiches Problem, habe es nur erst mal auf die Registrieren-Seite verschoben. Allerdings funktioniert das nicht wie ich es mir vorstelle und ich weiß absolut nicht wieso..


----------



## stg (30. Mai 2018)

Deine private `validateNutzer` Methode sieht falsch aus. Zum einen ist bei der Benennung völlig unklar, was denn der boolsche Rückgabewert überhaupt aussagen soll. Eigentlich willst du doch nur prüfen, ob es einen solchen Benutzer mit der übergebenen Mail-Adresse gibt?! Dann nenn die Methode doch auch so ... also etwa `doesUserExistsWithMail(String mail)` Darin braucht es nur eine Überprüfung à la `return dao.findNutzerByMail(mail) != null`
Die NullPointerException zu fangen und in diesem Fall einfach `false` zurückzugeben ist gefährlich. Bist du dir sicher, dass die EJB Injection deines DAO überhaupt funktioniert? Wenn nein, dann gibt die Methode ebenfalls false zurück. (Und keine Ahnung, ob das mit den neuesten Versionen mittlerweile geht, aber noch vor einiger Zeit war EJB Injection in JSF Validatoren technsich nicht möglich.)
Wenn du damit noch nicht weiterkommst, dann versuch doch bitte ein MCVE zu erstellen, die geposteten Code-Schnipsel zeigen das Problem möglicherweise gar nicht. Außerdem ist es mitunter wichtig zu wissen welche exakte Plattform du verwendest (welche JSF Version und welche Implementierung, welche EJB Version usw...)


----------



## stg (30. Mai 2018)

stg hat gesagt.:


> Bist du dir sicher, dass die EJB Injection deines DAO überhaupt funktioniert? Wenn nein, dann gibt die Methode ebenfalls false zurück.



...und das würde nebenbei bemerkt auch das beschriebene Verhalten erklären. Aber nur geraten, ich hab nix getestet.


----------

