1. 簡介
在任何應用程序中,驗證用户輸入都是一項常見的需求。在本教程中,我們將探討如何將對象列表作為參數傳遞給 Spring 控制器,並對其進行驗證。
我們將為控制器添加驗證,以確保用户提供的有效數據滿足指定的條件。
2. 為字段添加約束
為了我們的示例,我們將使用一個簡單的 Spring 控制器,該控制器管理電影數據庫。我們將重點關注一個接受電影列表的方法,並在對列表執行驗證後將其添加到數據庫中。
因此,我們首先使用 javax validation 為 Movie 類添加約束:
public class Movie {
private String id;
@NotEmpty(message = "Movie name cannot be empty.")
private String name;
// standard setters and getters
}3. 在控制器中添加驗證註釋
讓我們看一下我們的控制器。首先,我們將添加@Validated註釋到控制器類中:
@Validated
@RestController
@RequestMapping("/movies")
public class MovieController {
@Autowired
private MovieService movieService;
//...
}接下來,讓我們編寫控制器方法,其中我們將驗證傳入的 Movie 對象列表。
我們將向我們的電影對象列表添加 @NotEmpty 註解,以驗證列表中至少應包含一個元素。同時,我們還將添加 @Valid 註解,以確保 Movie 對象本身有效:
@PostMapping
public void addAll(
@RequestBody
@NotEmpty(message = "Input movie list cannot be empty.")
List<@Valid Movie> movies) {
movieService.addAll(movies);
}如果調用控制器方法時傳入一個空Movie列表,則由於@NotEmpty註解,驗證將失敗,我們將會看到以下消息:
Input movie list cannot be empty.@Valid 註解將確保在列表中的每個對象上評估 Movie 類中指定的約束。因此,如果在列表中傳遞一個名稱為空的 Movie 對象,驗證將失敗並顯示以下消息:
Movie name cannot be empty.4. 自定義驗證器
我們還可以將自定義約束驗證器添加到輸入列表。
對於我們的示例,自定義約束將驗證輸入列表的大小限制為最多四個元素。 讓我們創建此自定義約束註釋:
@Constraint(validatedBy = MaxSizeConstraintValidator.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface MaxSizeConstraint {
String message() default "The input list cannot contain more than 4 movies.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}現在,我們將創建一個驗證器,它將應用上述約束:
public class MaxSizeConstraintValidator implements ConstraintValidator<MaxSizeConstraint, List<Movie>> {
@Override
public boolean isValid(List<Movie> values, ConstraintValidatorContext context) {
return values.size() <= 4;
}
}最後,我們將添加 @MaxSizeConstraint 註解到我們的控制器方法中:
@PostMapping
public void addAll(
@RequestBody
@NotEmpty(message = "Input movie list cannot be empty.")
@MaxSizeConstraint
List<@Valid Movie> movies) {
movieService.addAll(movies);
}在這裏,@MaxSizeConstraint 將會驗證輸入的大小。因此,如果我們將超過四個 @Movie 對象傳遞到輸入列表中,驗證將會失敗。
5. 處理異常
如果任何驗證失敗,將會拋出 <a title="ConstraintViolationException javadoc" href="https://javaee.github.io/javaee-spec/javadocs/javax/validation/ConstraintViolationException.html">ConstraintViolationException</a> 異常。 接下來,讓我們看看如何添加一個異常處理組件來捕獲該異常。
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity handle(ConstraintViolationException constraintViolationException) {
Set<ConstraintViolation<?>> violations = constraintViolationException.getConstraintViolations();
String errorMessage = "";
if (!violations.isEmpty()) {
StringBuilder builder = new StringBuilder();
violations.forEach(violation -> builder.append(" " + violation.getMessage()));
errorMessage = builder.toString();
} else {
errorMessage = "ConstraintViolationException occured.";
}
return new ResponseEntity<>(errorMessage, HttpStatus.BAD_REQUEST);
}6. 測試 API
現在,我們將使用有效和無效的輸入測試我們的控制器。
首先,讓我們向 API 提供有效輸入:
curl -v -d '[{"name":"Movie1"}]' -H "Content-Type: application/json" -X POST http://localhost:8080/movies 在本場景中,我們將收到一個 HTTP 狀態碼 200 的響應:
...
HTTP/1.1 200
...接下來,我們將檢查在傳遞無效輸入時我們的API響應。
讓我們嘗試一個空列表:
curl -d [] -H "Content-Type: application/json" -X POST http://localhost:8080/movies在本場景中,我們將收到一個 HTTP 400 響應。這是因為輸入沒有滿足 @NotEmpty 約束。
Input movie list cannot be empty.接下來,我們嘗試將五個 Movie 對象傳遞到列表中:
curl -d '[{"name":"Movie1"},{"name":"Movie2"},{"name":"Movie3"},{"name":"Movie4"},{"name":"Movie5"}]'\
-H "Content-Type: application/json" -X POST http://localhost:8080/movie這也會導致 HTTP 狀態碼 400 響應,因為我們未能滿足 @MaxSizeConstraint 約束:
The input list cannot contain more than 4 movies.7. 結論
在本文中,我們學習瞭如何驗證 Spring 中對象的列表。