Проверка формы с AngularJS и Spring MVC

Проверка формы с помощью AngularJS и Spring MVC

1. обзор

Валидация никогда не бывает такой простой, как мы ожидаем. И, конечно, проверка значений, введенных пользователем в приложение, очень важна для сохранения целостности наших данных.

В контексте веб-приложения ввод данных обычно осуществляется с использованием HTML-форм и требует как клиентской, так и серверной проверки.

В этом руководстве мы рассмотримimplementing client-side validation of form input using AngularJS and server-side validation using the Spring MVC framework.

2. Maven Зависимости

Для начала добавим следующие зависимости:


    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

Последние версииspring-webmvc,hibernate-validator иjackson-databind можно загрузить с Maven Central.

3. Проверка с использованием Spring MVC

Приложение никогда не должно полагаться исключительно на проверку на стороне клиента, поскольку это можно легко обойти. Чтобы предотвратить сохранение неправильных или вредоносных значений или неправильное выполнение логики приложения, важно также проверить входные значения на стороне сервера.

Spring MVC предлагает поддержку проверки на стороне сервера с использованием аннотаций спецификацииJSR 349 Bean Validation. В этом примере мы будем использовать эталонную реализацию спецификации, то естьhibernate-validator.

3.1. Модель данных

Давайте создадим классUser, свойства которого снабжены соответствующими аннотациями проверки:

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
}

Используемые выше аннотации относятся к спецификацииJSR 349, за исключением@Email и@NotBlank, которые относятся к библиотекеhibernate-validator.

3.2. Spring MVC Controller

Давайте создадим класс контроллера, который определяет конечную точку/user, которая будет использоваться для сохранения нового объектаUser вList.

Чтобы включить проверку объектаUser, полученного с помощью параметров запроса, объявлению должна предшествовать аннотация@Valid, а ошибки проверки будут храниться в экземпляреBindingResult.

Чтобы определить, содержит ли объект недопустимые значения, мы можем использовать методhasErrors() изBindingResult.

ЕслиhasErrors() возвращаетtrue, мы можем вернутьJSON array, содержащий сообщения об ошибках, связанных с проверками, которые не прошли. В противном случае мы добавим объект в список:

@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);
        }
    }
}


Как видите,server-side validation adds the advantage of having the ability to perform additional checks that are not possible on the client side.

В нашем случае мы можем проверить, существует ли уже пользователь с таким же адресом электронной почты, и вернуть статус 409 CONFLICT, если это так.

Нам также нужно определить наш список пользователей и инициализировать его несколькими значениями:

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));

Давайте также добавим отображение для получения списка пользователей в виде объекта JSON:

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

Последний элемент, который нам нужен в нашем Spring MVC-контроллере, - это отображение, возвращающее главную страницу нашего приложения:

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

Мы рассмотрим страницуuser.html более подробно в разделе AngularJS.

3.3. Конфигурация Spring MVC

Давайте добавим в наше приложение базовую конфигурацию MVC:

@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. Инициализация приложения

Давайте создадим класс, реализующий интерфейсWebApplicationInitializer для запуска нашего приложения:

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. Тестирование Spring Mvc Validation с помощью Curl

Перед тем, как реализовать клиентский раздел AngularJS, мы можем протестировать наш API с помощью cURL с помощью команды:

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

Ответ представляет собой массив, содержащий сообщения об ошибках по умолчанию:

[
    "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

Проверка на стороне клиента полезна для создания лучшего пользовательского опыта, так как она предоставляет пользователю информацию о том, как успешно отправить действительные данные, и позволяет им продолжать взаимодействовать с приложением.

Библиотека AngularJS имеет отличную поддержку для добавления требований проверки к полям формы, обработки сообщений об ошибках и стилизации допустимых и недействительных форм.

Во-первых, давайте создадим модуль AngularJS, который внедряет модульngMessages, который используется для сообщений проверки:

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

Затем давайте создадим службу и контроллер AngularJS, которые будут использовать API, построенный в предыдущем разделе.

4.1. Сервис AngularJS

Наш сервис будет иметь два метода, которые вызывают методы контроллера MVC - один для сохранения пользователя и один для получения списка пользователей:

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. Контроллер AngularJS

КонтроллерUserCtrl вводитUserService, вызывает методы службы и обрабатывает ответы и сообщения об ошибках:

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();
}]);

В приведенном выше примере видно, что метод службы вызывается только в том случае, если свойство$valid дляuserForm истинно. Тем не менее, в этом случае есть дополнительная проверка на наличие повторяющихся писем, которая может быть выполнена только на сервере и обрабатывается отдельно в функцииerror().

Также обратите внимание, что определена переменнаяsubmitted, которая сообщит нам, была ли форма отправлена ​​или нет.

Первоначально эта переменная будетfalse, а при вызове методаsaveUser() она станетtrue. Если мы не хотим, чтобы сообщения проверки отображались до того, как пользователь отправит форму, мы можем использовать переменнуюsubmitted, чтобы предотвратить это.

4.3. Форма с использованием проверки AngularJS

Чтобы использовать библиотеку AngularJS и наш модуль AngularJS, нам нужно будет добавить скрипты на нашу страницуuser.html:



Затем мы можем использовать наш модуль и контроллер, установив свойстваng-app иng-controller:

Давайте создадим нашу HTML-форму:

...

Обратите внимание, что мы должны установить атрибутnovalidate в форме, чтобы предотвратить проверку HTML5 по умолчанию и заменить его нашим собственным.

Атрибутng-class динамически добавляет класс CSSform-error в форму, если переменнаяsubmitted имеет значениеtrue.

Атрибутng-submit определяет функцию контроллера AngularJS, которая будет вызываться при отправке формы. Использованиеng-submit вместоng-click имеет то преимущество, что оно также реагирует на отправку формы с помощью клавиши ENTER.

Теперь давайте добавим четыре поля ввода для атрибутов пользователя:











Каждое поле ввода имеет привязку к свойству переменнойuser через атрибутng-model.

For setting validation rules, мы используем атрибут HTML5required и несколько атрибутов, специфичных для AngularJS:ng-minglength, ng-maxlength, ng-min, иng-trim.

Для поляemail мы также используем атрибутtype со значениемemail для проверки электронной почты на стороне клиента.

In order to add error messages corresponding to each field, AngularJS предлагает директивуng-messages, которая перебирает входной объект$errors и отображает сообщения на основе каждого правила проверки.

Давайте добавим директиву для поляemail сразу после определения ввода:

Invalid email!

Email is required!

Аналогичные сообщения об ошибках могут быть добавлены для других полей ввода.

We can control when the directive is displayed для поляemail с использованием свойстваng-show с логическим выражением. В нашем примере мы отображаем директиву, когда поле имеет недопустимое значение, то есть свойство$invalid равноtrue, а переменнаяsubmitted также являетсяtrue.

Для поля будет отображаться только одно сообщение об ошибке.

Мы также можем добавить знак галочки (представленный символом шестнадцатеричного кода ✓) после поля ввода, если поле действительно, в зависимости от свойства$valid:

Проверка AngularJS также предлагает поддержку стилей с использованием классов CSS, таких какng-valid иng-invalid, или более конкретных, таких какng-invalid-required иng-invalid-minlength.

Давайте добавим свойство CSSborder-color:red для недопустимых входных данных внутри класса формыform-error:

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

Мы также можем показать сообщения об ошибках красным цветом, используя класс CSS:

.error-messages {
    color:red;
}

Собрав все вместе, давайте посмотрим на примере того, как будет выглядеть проверка нашей клиентской формы, если заполнить ее сочетанием действительных и недопустимых значений:

AngularJS form validation example

5. Заключение

В этом руководстве мы показали, как можно сочетать проверку на стороне клиента и на стороне сервера с помощью AngularJS и Spring MVC.

Как всегда, полный исходный код примеров можно найти вover on GitHub.

Чтобы просмотреть приложение, откройте URL-адрес/userPage после его запуска.