知識庫 / Spring / Spring Data RSS 訂閱

Spring Data REST 驗證器指南

REST,Spring Data
HongKong
12
03:55 AM · Dec 06 ,2025

1. 概述

本文檔介紹 Spring Data REST 驗證器的基本介紹。如果您需要先回顧 Spring Data REST 的基本概念,請務必訪問本文檔以鞏固基礎知識。

簡單來説,通過 Spring Data REST,我們可以通過 REST API 輕鬆地向數據庫添加新的條目,但我們當然也需要確保在實際持久化之前數據有效。

本文檔延續了之前的文章,我們將重用我們之前設置好的項目。

 

如果您正在尋找如何開始使用 Spring Data REST – 這是一個快速入門的好方法:

2. 使用驗證器

從 Spring 3 開始,框架引入了 Validator 接口,可用於驗證對象。

2.1. 動機

在上一篇文章中,我們定義了實體具有兩個屬性——nameemail

並且,為了創建新的資源,我們只需要運行:

curl -i -X POST -H "Content-Type:application/json" -d 
  '{ "name" : "Test", "email" : "[email protected]" }' 
  http://localhost:8080/users

這個 POST 請求會將提供的 JSON 對象保存到我們的數據庫中,操作完成後將返回:

{
  "name" : "Test",
  "email" : "[email protected]",
  "_links" : {
    "self" : {
        "href" : "http://localhost:8080/users/1"
    },
    "websiteUser" : {
        "href" : "http://localhost:8080/users/1"
    }
  }
}

由於我們提供了有效數據,因此預計結果將是積極的。但是,如果刪除 name 屬性,或者將值設置為空字符串 String ,會發生什麼情況?

為了測試第一個場景,我們將運行之前修改過的命令,其中將空字符串設置為 name 屬性的值:

curl -i -X POST -H "Content-Type:application/json" -d 
  '{ "name" : "", "email" : "Baggins" }' http://localhost:8080/users

使用該命令,我們將會得到以下響應:

{
  "name" : "",
  "email" : "Baggins",
  "_links" : {
    "self" : {
        "href" : "http://localhost:8080/users/1"
    },
    "websiteUser" : {
        "href" : "http://localhost:8080/users/1"
    }
  }
}

對於第二個場景,我們將從請求中移除 屬性:

curl -i -X POST -H "Content-Type:application/json" -d 
  '{ "email" : "Baggins" }' http://localhost:8080/users

對於該命令,我們將會得到以下響應:

{
  "name" : null,
  "email" : "Baggins",
  "_links" : {
    "self" : {
        "href" : "http://localhost:8080/users/2"
    },
    "websiteUser" : {
        "href" : "http://localhost:8080/users/2"
    }
  }
}

如我們所見,這兩個請求都已成功,我們可以確認其狀態碼為 201,以及指向我們對象的 API 鏈接。

這種行為不可接受,因為我們希望避免將部分數據插入到數據庫中。

2.2. Spring Data REST 事件

在每次對 Spring Data REST API 的調用過程中,Spring Data REST 導出器會生成各種事件,列出如下:

  • BeforeCreateEvent
  • AfterCreateEvent
  • BeforeSaveEvent
  • AfterSaveEvent
  • BeforeLinkSaveEvent
  • AfterLinkSaveEvent
  • BeforeDeleteEvent
  • AfterDeleteEvent

由於所有事件的處理方式類似,我們只展示如何處理 beforeCreateEvent,該事件在將新對象保存到數據庫之前生成。

2.3. 定義驗證器 (Validator)

為了創建我們自己的驗證器,我們需要實現 org.springframework.validation.Validator 接口,並提供 supportsvalidate 方法。

supports 方法用於檢查驗證器是否支持提供的請求,而 validate 方法用於驗證請求中的數據。

讓我們定義一個 WebsiteUserValidator 類:

public class WebsiteUserValidator implements Validator {

    @Override
    public boolean supports(Class<?> clazz) {
        return WebsiteUser.class.equals(clazz);
    }

    @Override
    public void validate(Object obj, Errors errors) {
        WebsiteUser user = (WebsiteUser) obj;
        if (checkInputString(user.getName())) {
            errors.rejectValue("name", "name.empty");
        }
   
        if (checkInputString(user.getEmail())) {
            errors.rejectValue("email", "email.empty");
        }
    }

    private boolean checkInputString(String input) {
        return (input == null || input.trim().length() == 0);
    }
}

Errors 對象是一個特殊類,旨在包含 validate 方法提供的所有錯誤。稍後我們將演示如何使用 Errors 對象中提供的消息。
要添加新的錯誤消息,我們必須調用 errors.rejectValue(nameOfField, errorMessage)

在定義驗證器之後,我們需要將其映射到在請求接受後生成的特定事件。

例如,在我們的案例中,beforeCreateEvent 在我們想要將新對象插入到數據庫中時生成。但是,由於我們想要在請求中驗證對象,因此我們需要首先定義我們的驗證器。

這可以通過三種方式完成:

  • 添加帶有名稱 "beforeCreateWebsiteUserValidator" 的 Component 註解。 Spring Boot 將識別前綴 beforeCreate,這決定了我們想要捕獲的事件,並且它還將識別 WebsiteUser 類來自 Component 名稱。
@Component("beforeCreateWebsiteUserValidator")
public class WebsiteUserValidator implements Validator {
    ...
}
  • 創建在 Application Context 中 Bean,使用 @Bean 註解:
  • @Bean
    public WebsiteUserValidator beforeCreateWebsiteUserValidator() {
        return new WebsiteUserValidator();
    }
  • 手動註冊:
  • @SpringBootApplication
    public class SpringDataRestApplication implements RepositoryRestConfigurer {
        public static void main(String[] args) {
            SpringApplication.run(SpringDataRestApplication.class, args);
        }
    
        @Override
        public void configureValidatingRepositoryEventListener(
          ValidatingRepositoryEventListener v) {
            v.addValidator("beforeCreate", new WebsiteUserValidator());
        }
    }
    <ul>
      <li>對於本例,<em >WebsiteUserValidator</em> 類無需任何註釋。</li>
    </ul>
    

    2.4. 事件發現 Bug

    目前,Spring Data REST 中存在一個 Bug – 它影響了事件的發現。

    如果調用生成 beforeCreate 事件的 POST 請求,我們的應用程序將不會調用驗證器,因為該事件不會被發現,這是由於該 Bug 造成的。

    針對此問題,一個簡單的解決方法是將其所有事件插入到 Spring Data REST 的 ValidatingRepositoryEventListener 類中:

    @Configuration
    public class ValidatorEventRegister implements InitializingBean {
    
        @Autowired
        ValidatingRepositoryEventListener validatingRepositoryEventListener;
    
        @Autowired
        private Map<String, Validator> validators;
    
        @Override
        public void afterPropertiesSet() throws Exception {
            List<String> events = Arrays.asList("beforeCreate");
            for (Map.Entry<String, Validator> entry : validators.entrySet()) {
                events.stream()
                  .filter(p -> entry.getKey().startsWith(p))
                  .findFirst()
                  .ifPresent(
                    p -> validatingRepositoryEventListener
                   .addValidator(p, entry.getValue()));
            }
        }
    }

    3. 測試

    第 2.1 節 中,我們展示了,在缺少驗證器的情況下,我們可以將不包含名稱屬性的對象添加到數據庫中,這並不是我們希望的行為,因為我們沒有檢查數據完整性。

    如果我們想要添加相同對象但沒有 屬性,並且使用了提供的驗證器,將會得到以下錯誤:

    curl -i -X POST -H "Content-Type:application/json" -d 
      '{ "email" : "[email protected]" }' http://localhost:8080/users
    {  
       "timestamp":1472510818701,
       "status":406,
       "error":"Not Acceptable",
       "exception":"org.springframework.data.rest.core.
        RepositoryConstraintViolationException",
       "message":"Validation failed",
       "path":"/users"
    }

    正如我們所見,來自請求的缺失數據已被檢測到,對象未被保存到數據庫中。我們的請求返回了 500 HTTP 狀態碼和內部錯誤消息。

    錯誤消息中並未提及請求中的問題。為了使其更具信息量,我們需要修改響應對象。

    在《Spring 異常處理》一文中,我們展示瞭如何處理框架生成的異常,所以這絕對是一個值得一讀的資料。

    由於我們的應用程序生成了 RepositoryConstraintViolationException 異常,我們將創建一個處理程序來修改響應消息。

    這是我們的 RestResponseEntityExceptionHandle 類:

    @ControllerAdvice
    public class RestResponseEntityExceptionHandler extends
      ResponseEntityExceptionHandler {
    
        @ExceptionHandler({ RepositoryConstraintViolationException.class })
        public ResponseEntity<Object> handleAccessDeniedException(
          Exception ex, WebRequest request) {
              RepositoryConstraintViolationException nevEx = 
                (RepositoryConstraintViolationException) ex;
    
              String errors = nevEx.getErrors().getAllErrors().stream()
                .map(p -> p.toString()).collect(Collectors.joining("\n"));
              
              return new ResponseEntity<Object>(errors, new HttpHeaders(),
                HttpStatus.PARTIAL_CONTENT);
        }
    }

    使用此自定義處理程序,我們的返回對象將包含有關所有檢測到的錯誤的詳細信息。

    4. 結論

    在本文中,我們展示了驗證器對於任何 Spring Data REST API 來説都是必不可少的,它為數據插入提供了一層額外的安全保障。

    我們還説明了如何輕鬆創建新的驗證器,並使用註解。

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

    發佈 評論

    Some HTML is okay.