«1. Обзор

В нескольких последних статьях серии «Регистрация» здесь, на Baeldung, мы создали большую часть необходимой нам функциональности в стиле MVC.

Теперь мы собираемся перевести некоторые из этих API на более RESTful подход.

2. Операция регистрации

Начнем с основной операции регистрации:

@PostMapping("/user/registration")
public GenericResponse registerUserAccount(
      @Valid UserDto accountDto, HttpServletRequest request) {
    logger.debug("Registering user account with information: {}", accountDto);
    User registered = createUserAccount(accountDto);
    if (registered == null) {
        throw new UserAlreadyExistException();
    }
    String appUrl = "http://" + request.getServerName() + ":" + 
      request.getServerPort() + request.getContextPath();
   
    eventPublisher.publishEvent(
      new OnRegistrationCompleteEvent(registered, request.getLocale(), appUrl));

    return new GenericResponse("success");
}

Итак, чем же отличается эта реализация от оригинальной реализации, ориентированной на MVC?

Вот:

    Запрос теперь правильно сопоставлен с HTTP POST, теперь мы возвращаем правильный DTO и упорядочиваем его непосредственно в теле ответа, мы больше не имеем дело с обработкой ошибок в методе на all

Мы также удаляем старую функцию showRegistrationPage(), так как она не нужна для простого отображения страницы регистрации.

3. Registration.html

С этими изменениями теперь нам нужно изменить Registration.html, чтобы:

    использовать Ajax для отправки регистрационной формы получать результаты операции в виде JSON

Вот и все :

<html>
<head>
<title th:text="#{label.form.title}">form</title>
</head>
<body>
<form action="/" method="POST" enctype="utf8">
    <input  name="firstName" value="" />
    <span id="firstNameError" style="display:none"></span>
 
    <input  name="lastName" value="" />
    <span id="lastNameError" style="display:none"></span>
                     
    <input  name="email" value="" />           
    <span id="emailError" style="display:none"></span>
     
    <input name="password" value="" type="password" />
    <span id="passwordError" style="display:none"></span>
                 
    <input name="matchingPassword" value="" type="password" />
    <span id="globalError" style="display:none"></span>
 
    <a href="#" onclick="register()" th:text="#{label.form.submit}>submit</a>
</form>
             
 
<script src="jquery.min.js"></script>
<script type="text/javascript">
var serverContext = [[@{/}]];

function register(){
    $(".alert").html("").hide();
    var formData= $('form').serialize();
    $.post(serverContext + "/user/registration",formData ,function(data){
        if(data.message == "success"){
            window.location.href = serverContext +"/successRegister.html";
        }
    })
    .fail(function(data) {
        if(data.responseJSON.error.indexOf("MailError") > -1)
        {
            window.location.href = serverContext + "/emailError.html";
        }
        else if(data.responseJSON.error.indexOf("InternalError") > -1){
            window.location.href = serverContext + 
              "/login.html?message=" + data.responseJSON.message;
        }
        else if(data.responseJSON.error == "UserAlreadyExist"){
            $("#emailError").show().html(data.responseJSON.message);
        }
        else{
            var errors = $.parseJSON(data.responseJSON.message);
            $.each( errors, function( index,item ){
                $("#"+item.field+"Error").show().html(item.defaultMessage);
            });
            errors = $.parseJSON(data.responseJSON.error);
            $.each( errors, function( index,item ){
                $("#globalError").show().append(item.defaultMessage+"<br>");
            });
 }
}
</script>
</body>
</html>

4. Обработка исключений

Наряду с RESTful API, логика обработки исключений, конечно, также станет более зрелой.

Мы используем тот же механизм @ControllerAdvice для корректной обработки исключений, создаваемых приложением, — и теперь нам нужен новый тип исключений.

Это исключение BindException, которое выдается при проверке UserDto (если оно недействительно). Мы переопределим метод ResponseEntityExceptionHandler по умолчанию handleBindException(), чтобы добавить ошибки в тело ответа: который уже существует:

@Override
protected ResponseEntity<Object> handleBindException
  (BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
    logger.error("400 Status Code", ex);
    BindingResult result = ex.getBindingResult();
    GenericResponse bodyOfResponse = 
      new GenericResponse(result.getFieldErrors(), result.getGlobalErrors());
    
    return handleExceptionInternal(
      ex, bodyOfResponse, new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
}

5. GenericResponse

@ExceptionHandler({ UserAlreadyExistException.class })
public ResponseEntity<Object> handleUserAlreadyExist(RuntimeException ex, WebRequest request) {
    logger.error("409 Status Code", ex);
    GenericResponse bodyOfResponse = new GenericResponse(
      messages.getMessage("message.regError", null, request.getLocale()), "UserAlreadyExist");
    
    return handleExceptionInternal(
      ex, bodyOfResponse, new HttpHeaders(), HttpStatus.CONFLICT, request);
}

Нам также необходимо улучшить реализацию GenericResponse, чтобы она содержала эти ошибки проверки:

6. Пользовательский интерфейс — поля и глобальные ошибки

public class GenericResponse {

    public GenericResponse(List<FieldError> fieldErrors, List<ObjectError> globalErrors) {
        super();
        ObjectMapper mapper = new ObjectMapper();
        try {
            this.message = mapper.writeValueAsString(fieldErrors);
            this.error = mapper.writeValueAsString(globalErrors);
        } catch (JsonProcessingException e) {
            this.message = "";
            this.error = "";
        }
    }
}

Наконец, давайте посмотрим, как обрабатывать как поля, так и глобальные ошибки с помощью jQuery:

Обратите внимание, что:

var serverContext = [[@{/}]];

function register(){
    $(".alert").html("").hide();
    var formData= $('form').serialize();
    $.post(serverContext + "/user/registration",formData ,function(data){
        if(data.message == "success"){
            window.location.href = serverContext +"/successRegister.html";
        }
    })
    .fail(function(data) {
        if(data.responseJSON.error.indexOf("MailError") > -1)
        {
            window.location.href = serverContext + "/emailError.html";
        }
        else if(data.responseJSON.error.indexOf("InternalError") > -1){
            window.location.href = serverContext + 
              "/login.html?message=" + data.responseJSON.message;
        }
        else if(data.responseJSON.error == "UserAlreadyExist"){
            $("#emailError").show().html(data.responseJSON.message);
        }
        else{
            var errors = $.parseJSON(data.responseJSON.message);
            $.each( errors, function( index,item ){
                $("#"+item.field+"Error").show().html(item.defaultMessage);
            });
            errors = $.parseJSON(data.responseJSON.error);
            $.each( errors, function( index,item ){
                $("#globalError").show().append(item.defaultMessage+"<br>");
            });
 }
}

Если есть ошибки проверки, то объект сообщения содержит ошибки поля, а объект ошибки содержит глобальные ошибки. Мы отображаем каждую ошибку поля рядом с ее полем. Мы отображаем все глобальные ошибки в одном месте в конце формы.

    7. Заключение. показать простой способ работы с этим API во внешнем интерфейсе.

Внешний интерфейс jQuery сам по себе не является основным объектом внимания — это всего лишь базовый потенциальный клиент, который может быть реализован в любом количестве JS-фреймворков, при этом API остается точно таким же.

Полную реализацию этого руководства можно найти в проекте github — это проект на основе Eclipse, поэтому его легко импортировать и запускать как есть.

«

The full implementation of this tutorial can be found in the github project – this is an Eclipse based project, so it should be easy to import and run as it is.

Next »

Spring Security – Reset Your Password

« Previous

Registration with Spring Security – Password Encoding