知識庫 / REST RSS 訂閱

註冊 API 變為 RESTful 風格

REST,Spring Security
HongKong
5
04:11 AM · Dec 06 ,2025
該文章是系列的一部分
• Spring Security 註冊系列
• 使用 Spring Security 的註冊流程
• 通過電子郵件激活新賬户
• Spring Security 註冊 – 發送驗證郵件
• 使用 Spring Security 註冊 – 密碼編碼
• 註冊 API 變為 RESTful (當前文章)
• Spring Security – 重置密碼
• 註冊 – 密碼強度和規則
• 更新您的密碼
• 通知用户從新設備或位置登錄

1. 概述

在之前的《註冊系列》文章中,我們以 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 並直接將其序列化到響應體中
  • 我們不再需要在方法中處理任何錯誤處理邏輯

我們還移除了舊的 showRegistrationPage() – 因為這對於僅僅顯示註冊頁面來説是不必要的。

3. 修改 registration.html

此外,我們需要修改 registration.html 以實現以下功能:

  • 使用 Ajax 提交註冊表單
  • 接收操作結果作為 JSON

以下是修改內容:

<html>
<head>
<title th:text="#{label.form.title}">form</title>
</head>
<body>
<form 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. 異常處理

通常,實施良好的異常處理策略可以使 REST 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);
}

接下來,我們還需要處理自定義的 Exception UserAlreadyExistException – 當用户使用已存在的電子郵件註冊時,會拋出該異常:

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

5. 通用響應 (GenericResponse)

我們還需要改進 GenericResponse 的實現,以容納這些驗證錯誤:

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 = "";
        }
    }
}

6. UI – Field 和 Global 錯誤

最後,讓我們看看如何使用 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>");
            });
 }
}

請注意:

  • 如果存在驗證錯誤,則 message 對象包含字段錯誤,error 對象包含全局錯誤
  • 我們會在每個字段旁邊顯示字段錯誤
  • 我們會在表單末尾的一處顯示所有全局錯誤

7. 結論

本文的重點是引導API朝着更符合RESTful風格的方向發展,並展示一種簡單的前端處理方式。

jQuery前端本身並非重點——它只是一個基礎的潛在客户端,可以在各種JS框架中實現,而API保持不變。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.