Formularvalidierung mit AngularJS und Spring MVC

Formularvalidierung mit AngularJS und Spring MVC

1. Überblick

Die Validierung ist nie so einfach wie erwartet. Die Validierung der von einem Benutzer in eine Anwendung eingegebenen Werte ist natürlich sehr wichtig, um die Integrität unserer Daten zu gewährleisten.

Im Kontext einer Webanwendung erfolgt die Dateneingabe normalerweise mithilfe von HTML-Formularen und erfordert sowohl eine clientseitige als auch eine serverseitige Validierung.

In diesem Tutorial sehen wir unsimplementing client-side validation of form input using AngularJS and server-side validation using the Spring MVC framework an.

2. Maven-Abhängigkeiten

Fügen Sie zunächst die folgenden Abhängigkeiten hinzu:


    org.springframework
    spring-webmvc
    4.3.7.RELEASE


    org.hibernate
    hibernate-validator
    5.4.0.Final


    com.fasterxml.jackson.core
    jackson-databind
    2.8.7

Die neuesten Versionen vonspring-webmvc,hibernate-validator undjackson-databind können von Maven Central heruntergeladen werden.

3. Validierung mit Spring MVC

Eine Anwendung sollte sich niemals ausschließlich auf die clientseitige Validierung verlassen, da dies leicht umgangen werden kann. Um zu verhindern, dass falsche oder böswillige Werte gespeichert werden oder die Anwendungslogik nicht ordnungsgemäß ausgeführt wird, ist es wichtig, die Eingabewerte auch auf der Serverseite zu überprüfen.

Spring MVC bietet Unterstützung für die serverseitige Validierung mithilfe der Spezifikationsanmerkungen vonJSR 349 Bean Validation. In diesem Beispiel verwenden wir die Referenzimplementierung der Spezifikation, nämlichhibernate-validator.

3.1. Das Datenmodell

Erstellen wir eineUser-Klasse mit Eigenschaften, die mit entsprechenden Validierungsanmerkungen versehen sind:

public class User {

    @NotNull
    @Email
    private String email;

    @NotNull
    @Size(min = 4, max = 15)
    private String password;

    @NotBlank
    private String name;

    @Min(18)
    @Digits(integer = 2, fraction = 0)
    private int age;

    // standard constructor, getters, setters
}

Die oben verwendeten Anmerkungen gehören zur SpezifikationJSR 349, mit Ausnahme von@Email und@NotBlank, die spezifisch für die Bibliothekhibernate-validator sind.

3.2. Spring MVC Controller

Erstellen wir eine Controller-Klasse, die den Endpunkt eines/userdefiniert, mit dem ein neuesUser-Objekt in einemList gespeichert wird.

Um die Validierung desUser-Objekts zu ermöglichen, das über Anforderungsparameter empfangen wurde, muss der Deklaration die Annotation@Validvorangestellt werden, und die Validierungsfehler werden in einerBindingResult-Instanz gespeichert.

Um festzustellen, ob das Objekt ungültige Werte enthält, können wir diehasErrors()-Methode vonBindingResult verwenden.

WennhasErrors()true zurückgibt, können wirJSON array zurückgeben, die die Fehlermeldungen enthalten, die den nicht bestandenen Validierungen zugeordnet sind. Andernfalls fügen wir das Objekt der Liste hinzu:

@PostMapping(value = "/user")
@ResponseBody
public ResponseEntity saveUser(@Valid User user,
  BindingResult result, Model model) {
    if (result.hasErrors()) {
        List errors = result.getAllErrors().stream()
          .map(DefaultMessageSourceResolvable::getDefaultMessage)
          .collect(Collectors.toList());
        return new ResponseEntity<>(errors, HttpStatus.OK);
    } else {
        if (users.stream().anyMatch(it -> user.getEmail().equals(it.getEmail()))) {
            return new ResponseEntity<>(
              Collections.singletonList("Email already exists!"),
              HttpStatus.CONFLICT);
        } else {
            users.add(user);
            return new ResponseEntity<>(HttpStatus.CREATED);
        }
    }
}


Wie Sie sehen können,server-side validation adds the advantage of having the ability to perform additional checks that are not possible on the client side.

In unserem Fall können wir überprüfen, ob bereits ein Benutzer mit derselben E-Mail-Adresse vorhanden ist, und in diesem Fall den Status 409 CONFLICT zurückgeben.

Wir müssen auch unsere Benutzerliste definieren und mit ein paar Werten initialisieren:

private List users = Arrays.asList(
  new User("[email protected]", "pass", "Ana", 20),
  new User("[email protected]", "pass", "Bob", 30),
  new User("[email protected]", "pass", "John", 40),
  new User("[email protected]", "pass", "Mary", 30));

Fügen Sie außerdem eine Zuordnung zum Abrufen der Benutzerliste als JSON-Objekt hinzu:

@GetMapping(value = "/users")
@ResponseBody
public List getUsers() {
    return users;
}

Das letzte Element, das wir in unserem Spring MVC-Controller benötigen, ist eine Zuordnung, um die Hauptseite unserer Anwendung zurückzugeben:

@GetMapping("/userPage")
public String getUserProfilePage() {
    return "user";
}

Wir werden uns die Seiteuser.htmlim Abschnitt AngularJS genauer ansehen.

3.3. Spring MVC-Konfiguration

Fügen wir unserer Anwendung eine grundlegende MVC-Konfiguration hinzu:

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.example.springmvcforms")
class ApplicationConfiguration implements WebMvcConfigurer {

    @Override
    public void configureDefaultServletHandling(
      DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    @Bean
    public InternalResourceViewResolver htmlViewResolver() {
        InternalResourceViewResolver bean = new InternalResourceViewResolver();
        bean.setPrefix("/WEB-INF/html/");
        bean.setSuffix(".html");
        return bean;
    }
}

3.4. Initialisierung der Anwendung

Erstellen wir eine Klasse, die die Schnittstelle vonWebApplicationInitializerimplementiert, um unsere Anwendung auszuführen:

public class WebInitializer implements WebApplicationInitializer {

    public void onStartup(ServletContext container) throws ServletException {

        AnnotationConfigWebApplicationContext ctx
          = new AnnotationConfigWebApplicationContext();
        ctx.register(ApplicationConfiguration.class);
        ctx.setServletContext(container);
        container.addListener(new ContextLoaderListener(ctx));

        ServletRegistration.Dynamic servlet
          = container.addServlet("dispatcher", new DispatcherServlet(ctx));
        servlet.setLoadOnStartup(1);
        servlet.addMapping("/");
    }
}

3.5. Testen der Spring Mvc-Validierung mit Curl

Bevor wir den AngularJS-Client-Abschnitt implementieren, können wir unsere API mit cURL mit dem folgenden Befehl testen:

curl -i -X POST -H "Accept:application/json"
  "localhost:8080/spring-mvc-forms/user?email=aaa&password=12&age=12"

Die Antwort ist ein Array mit den Standardfehlermeldungen:

[
    "not a well-formed email address",
    "size must be between 4 and 15",
    "may not be empty",
    "must be greater than or equal to 18"
]

4. AngularJS-Validierung

Die clientseitige Validierung ist hilfreich, um eine bessere Benutzererfahrung zu erzielen, da der Benutzer Informationen zum erfolgreichen Übermitteln gültiger Daten erhält und weiterhin mit der Anwendung interagieren kann.

Die AngularJS-Bibliothek bietet eine hervorragende Unterstützung für das Hinzufügen von Überprüfungsanforderungen für Formularfelder, das Behandeln von Fehlermeldungen und das Stylen gültiger und ungültiger Formulare.

Zunächst erstellen wir ein AngularJS-Modul, das dasngMessages-Modul einfügt, das für Validierungsnachrichten verwendet wird:

var app = angular.module('app', ['ngMessages']);

Als Nächstes erstellen wir einen AngularJS-Dienst und -Controller, der die im vorherigen Abschnitt erstellte API verwendet.

4.1. Der AngularJS-Dienst

Unser Service verfügt über zwei Methoden, die die MVC-Controller-Methoden aufrufen - eine zum Speichern eines Benutzers und eine zum Abrufen der Benutzerliste:

app.service('UserService',['$http', function ($http) {

    this.saveUser = function saveUser(user){
        return $http({
          method: 'POST',
          url: 'user',
          params: {email:user.email, password:user.password,
            name:user.name, age:user.age},
          headers: 'Accept:application/json'
        });
    }

    this.getUsers = function getUsers(){
        return $http({
          method: 'GET',
          url: 'users',
          headers:'Accept:application/json'
        }).then( function(response){
            return response.data;
        } );
    }

}]);

4.2. Der AngularJS-Controller

DerUserCtrl-Controller injiziert dieUserService, ruft die Dienstmethoden auf und verarbeitet die Antwort- und Fehlermeldungen:

app.controller('UserCtrl', ['$scope','UserService', function ($scope,UserService) {

    $scope.submitted = false;

    $scope.getUsers = function() {
           UserService.getUsers().then(function(data) {
               $scope.users = data;
           });
       }

    $scope.saveUser = function() {
        $scope.submitted = true;
          if ($scope.userForm.$valid) {
            UserService.saveUser($scope.user)
              .then (function success(response) {
                  $scope.message = 'User added!';
                  $scope.errorMessage = '';
                  $scope.getUsers();
                  $scope.user = null;
                  $scope.submitted = false;
              },
              function error(response) {
                  if (response.status == 409) {
                    $scope.errorMessage = response.data.message;
                  }
                  else {
                    $scope.errorMessage = 'Error adding user!';
                  }
                  $scope.message = '';
            });
          }
    }

   $scope.getUsers();
}]);

Im obigen Beispiel können wir sehen, dass die Servicemethode nur aufgerufen wird, wenn die Eigenschaft$valid vonuserForm wahr ist. In diesem Fall gibt es jedoch die zusätzliche Prüfung auf doppelte E-Mails, die nur auf dem Server durchgeführt werden kann und in der Funktionerror() separat behandelt wird.

Beachten Sie außerdem, dass eine Variablesubmitteddefiniert ist, die angibt, ob das Formular gesendet wurde oder nicht.

Anfangs ist diese Variablefalse, und beim Aufrufen der MethodesaveUser() wird sie zutrue. Wenn wir nicht möchten, dass Validierungsnachrichten angezeigt werden, bevor der Benutzer das Formular sendet, können wir die Variablesubmittedverwenden, um dies zu verhindern.

4.3. Formular mit AngularJS-Validierung

Um die AngularJS-Bibliothek und unser AngularJS-Modul nutzen zu können, müssen wir die Skripte zu unsereruser.html-Seite hinzufügen:



Dann können wir unser Modul und unseren Controller verwenden, indem wir die Eigenschaftenng-app undng-controller einstellen:

Erstellen wir unser HTML-Formular:

...

Beachten Sie, dass wir das Attributnovalidate im Formular festlegen müssen, um die Standard-HTML5-Validierung zu verhindern und durch unser eigenes zu ersetzen.

Das Attributng-class fügt die CSS-Klasseform-error dynamisch zum Formular hinzu, wenn die Variablesubmitted den Werttrue hat.

Das Attributng-submit definiert die AngularJS-Controller-Funktion, die aufgerufen wird, wenn das Formular gesendet wird. Die Verwendung vonng-submit anstelle vonng-click hat den Vorteil, dass auch auf das Senden des Formulars mit der EINGABETASTE reagiert wird.

Fügen wir nun die vier Eingabefelder für die Benutzerattribute hinzu:











Jedes Eingabefeld ist über das Attributng-modelan eine Eigenschaft der Variablenusergebunden.

For setting validation rules verwenden wir das HTML5required-Attribut und mehrere AngularJS-spezifische Attribute:ng-minglength, ng-maxlength, ng-min, undng-trim.

Für das Feldemail verwenden wir auch das Attributtype mit dem Wertemail für die clientseitige E-Mail-Validierung.

In order to add error messages corresponding to each field, AngularJS bietet die Direktiveng-messages an, die das$errors-Objekt einer Eingabe durchläuft und Nachrichten basierend auf jeder Validierungsregel anzeigt.

Fügen wir die Direktive für das Feldemaildirekt nach der Eingabedefinition hinzu:

Invalid email!

Email is required!

Ähnliche Fehlermeldungen können für die anderen Eingabefelder hinzugefügt werden.

We can control when the directive is displayed für das Feldemail unter Verwendung der Eigenschaftng-show mit einem booleschen Ausdruck. In unserem Beispiel zeigen wir die Direktive an, wenn das Feld einen ungültigen Wert hat, was bedeutet, dass die Eigenschaft$invalidtrue ist und die Variablesubmitted ebenfallstrue ist.

Für ein Feld wird jeweils nur eine Fehlermeldung angezeigt.

Wir können nach dem Eingabefeld auch ein Häkchen (dargestellt durch das HEX-Codezeichen ✓) einfügen, falls das Feld gültig ist, abhängig von der Eigenschaft$valid:

Die AngularJS-Validierung bietet auch Unterstützung für das Styling mithilfe von CSS-Klassen wieng-valid undng-invalid oder spezifischeren wieng-invalid-required undng-invalid-minlength.

Fügen wir die CSS-Eigenschaftborder-color:red für ungültige Eingaben in dieform-error-Klasse des Formulars ein:

.form-error input.ng-invalid {
    border-color:red;
}

Wir können die Fehlermeldungen auch mit einer CSS-Klasse in Rot anzeigen:

.error-messages {
    color:red;
}

Nachdem Sie alles zusammengestellt haben, sehen wir uns ein Beispiel an, wie unsere clientseitige Formularvalidierung aussehen wird, wenn sie mit einer Mischung aus gültigen und ungültigen Werten ausgefüllt wird:

AngularJS form validation example

5. Fazit

In diesem Tutorial haben wir gezeigt, wie wir die clientseitige und serverseitige Validierung mit AngularJS und Spring MVC kombinieren können.

Wie immer finden Sie den vollständigen Quellcode für die Beispiele inover on GitHub.

Um die Anwendung anzuzeigen, greifen Sie nach dem Ausführen auf die URL von/userPagezu.