4 提供有意義的錯誤信息
當驗證失敗時,必須提供清晰簡潔的錯誤消息來描述出了什么問題以及如何修復它。
這是一個示例,如果我們有一個允許用戶創建新用戶的 RESTful API
。我們要確保姓名和電子郵件地址字段不為空,年齡在 18 到 99 歲之間,除了這些字段,如果用戶嘗試使用重復的“用戶名”創建帳戶,我們還會提供明確的錯誤消息或“電子郵件”。
為此,我們可以定義一個帶有必要驗證注釋的模型類 User,如下所示:
public class User {
@NotBlank(message = "用戶名不能為空")
private String name;
@NotBlank(message = "Email不能為空")
@Email(message = "無效的Emaild地址")
private String email;
@NotNull(message = "年齡不能為空")
@Min(value = 18, message = "年齡必須大于18")
@Max(value = 99, message = "年齡必須小于99")
private Integer age;
}
- 我們使用 message屬性為每個驗證注釋提供了自定義錯誤消息。
接下來,在我們的 Spring 控制器中,我們可以處理表單提交并使用 @Valid
注釋驗證用戶輸入:
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public ResponseEntity<String> createUser(@Valid @RequestBody User user, BindingResult result) {
if (result.hasErrors()) {
List<String> errorMessages = result.getAllErrors().stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.toList());
return ResponseEntity.badRequest().body(errorMessages.toString());
}
// save the user to the database using UserService
userService.saveUser(user);
return ResponseEntity.status(HttpStatus.CREATED).body("User created successfully");
}
}
- 我們使用
@Valid
注釋來觸發User
對象的驗證,并使用BindingResult
對象來捕獲任何驗證錯誤。
5 將 i18n 用于錯誤消息
如果你的應用程序支持多種語言,則必須使用國際化 (i18n) 以用戶首選語言顯示錯誤消息。
以下是在 Spring Boot 應用程序中使用 i18n 處理錯誤消息的示例
- 首先,在資源目錄下創建一個包含默認錯誤消息的
messages.properties
文件
# messages.properties
user.name.required=Name is required.
user.email.invalid=Invalid email format.
user.age.invalid=Age must be a number between 18 and 99.
- 接下來,為每種支持的語言創建一個
messages_xx.properties
文件,例如,中文的messages_zh_CN.properties
。
user.name.required=名稱不能為空.
user.email.invalid=無效的email格式.
user.age.invalid=年齡必須在18到99歲之間.
- 然后,更新您的驗證注釋以使用本地化的錯誤消息
public class User {
@NotNull(message = "{user.id.required}")
private Long id;
@NotBlank(message = "{user.name.required}")
private String name;
@Email(message = "{user.email.invalid}")
private String email;
@NotNull(message = "{user.age.required}")
@Min(value = 18, message = "{user.age.invalid}")
@Max(value = 99, message = "{user.age.invalid}")
private Integer age;
}
- 最后,在 Spring 配置文件中配置
MessageSource bean
以加載i18n
消息文件
@Configuration
public class AppConfig {
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
@Bean
public LocalValidatorFactoryBean validator() {
LocalValidatorFactoryBean validatorFactoryBean = new LocalValidatorFactoryBean();
validatorFactoryBean.setValidationMessageSource(messageSource());
return validatorFactoryBean;
}
}
- 現在,當發生驗證錯誤時,錯誤消息將根據隨請求發送的“Accept-Language”標頭以用戶的首選語言顯示。
6 使用分組驗證
驗證組是 Spring Boot 驗證框架的一個強大功能,允許您根據其他輸入值或應用程序狀態應用條件驗證規則。
現在有一個包含三個字段的User
類的情況下:firstName
、lastName
和email
。我們要確保如果 email
字段為空,則 firstName
或 lastName
字段必須非空。否則,所有三個字段都應該正常驗證。
為此,我們將定義兩個驗證組:EmailNotEmpty
和 Default
。EmailNotEmpty
組將包含當 email
字段不為空時的驗證規則,而 Default
組將包含所有三個字段的正常驗證規則。
- 創建帶有驗證組的
User
類
public class User {
@NotBlank(groups = Default.class)
private String firstName;
@NotBlank(groups = Default.class)
private String lastName;
@Email(groups = EmailNotEmpty.class)
private String email;
// getters and setters omitted for brevity
public interface EmailNotEmpty {}
public interface Default {}
}
- 請注意,我們在
User
類中定義了兩個接口,EmailNotEmpty
和Default
。這些將作為我們的驗證組。
- 接下來,我們更新
Controller
使用這些驗證組
@RestController
@RequestMapping("/users")
@Validated
public class UserController {
public ResponseEntity<String> createUser(
@Validated({org.example.model.ex6.User.EmailNotEmpty.class}) @RequestBody User userWithEmail,
@Validated({User.Default.class}) @RequestBody User userWithoutEmail)
{
// Create the user and return a success response
}
}
- 我們已將
@Validated
注釋添加到我們的控制器,表明我們想要使用驗證組。我們還更新了createUser
方法,將兩個User
對象作為輸入,一個在email
字段不為空時使用,另一個在它為空時使用。 @Validated
注釋用于指定將哪個驗證組應用于每個User
對象。對于userWithEmail
參數,我們指定了EmailNotEmpty
組,而對于userWithoutEmail
參數,我們指定了Default
組。
- 進行這些更改后,現在將根據“電子郵件”字段是否為空對“用戶”類進行不同的驗證。如果為空,則
firstName
或lastName
字段必須非空。否則,所有三個字段都將正常驗證。
7 對復雜邏輯使用跨域驗證
如果需要驗證跨多個字段的復雜輸入規則,可以使用跨字段驗證來保持驗證邏輯的組織性和可維護性。跨字段驗證可確保所有輸入值均有效且彼此一致,從而防止出現意外行為。
假設我們有一個表單,用戶可以在其中輸入任務的開始日期和結束日期,并且我們希望確保結束日期不早于開始日期。我們可以使用跨域驗證來實現這一點。
- 首先,我們定義一個自定義驗證注解
EndDateAfterStartDate
:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EndDateAfterStartDateValidator.class)
public @interface EndDateAfterStartDate {
String message() default "End date must be after start date";
Class?[] groups() default {};
Class? extends Payload[] payload() default {};
}
- 然后,我們創建驗證器
EndDateAfterStartDateValidator
:
public class EndDateAfterStartDateValidator implements ConstraintValidator<EndDateAfterStartDate, TaskForm> {
@Override
public boolean isValid(TaskForm taskForm, ConstraintValidatorContext context) {
if (taskForm.getStartDate() == null || taskForm.getEndDate() == null) {
return true;
}
return taskForm.getEndDate().isAfter(taskForm.getStartDate());
}
}
- 最后,我們將
EndDateAfterStartDate
注釋應用于我們的表單對象TaskForm
:
@EndDateAfterStartDate
public class TaskForm {
@NotNull
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate startDate;
@NotNull
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate endDate;
}
現在,當用戶提交表單時,驗證框架將自動檢查結束日期是否晚于開始日期,如果不是,則提供有意義的錯誤消息。
8 對驗證錯誤使用異常處理
可以使用異常處理ExceptionHandler
來統一捕獲和處理驗證錯誤。
以下是如何在 Spring Boot 中使用異常處理來處理驗證錯誤的示例:
@RestControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
HttpHeaders headers, HttpStatus status,
WebRequest request) {
Map<String, Object> body = new LinkedHashMap<>();
body.put("timestamp", LocalDateTime.now());
body.put("status", status.value());
// Get all errors
List<String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(x -> x.getDefaultMessage())
.collect(Collectors.toList());
body.put("errors", errors);
return new ResponseEntity<>(body, headers, status);
}
}
在這里,我們創建了一個用 @RestControllerAdvice
注解的 RestExceptionHandler
類來處理我們的 REST API 拋出的異常。然后我們創建一個用@ExceptionHandler
注解的方法來處理在驗證失敗時拋出的 MethodArgumentNotValidException
。
在處理程序方法中,我們創建了一個 Map
對象來保存錯誤響應的詳細信息,包括時間戳、HTTP 狀態代碼和錯誤消息列表。我們使用 MethodArgumentNotValidException
對象的 getBindingResult()
方法獲取所有驗證錯誤并將它們添加到錯誤消息列表中。
最后,我們返回一個包含錯誤響應詳細信息的ResponseEntity
對象,包括作為響應主體的錯誤消息列表、HTTP 標頭和 HTTP 狀態代碼。
有了這個異常處理代碼,我們的 REST API 拋出的任何驗證錯誤都將被捕獲并以結構化和有意義的格式返回給用戶,從而更容易理解和解決問題。
9 測試你的驗證邏輯
需要為你的驗證邏輯編寫單元測試,以幫助確保它正常工作。
@DataJpaTest
public class UserValidationTest {
@Autowired
private TestEntityManager entityManager;
@Autowired
private Validator validator;
@Test
public void testValidation() {
User user = new User();
user.setFirstName("John");
user.setLastName("Doe");
user.setEmail("invalid email");
Set
我們使用 JUnit 5 編寫一個測試來驗證具有無效電子郵件地址的“用戶”對象。然后我們使用 Validator
接口來驗證 User
對象并檢查是否返回了預期的驗證錯誤。
10 考慮客戶端驗證
客戶端驗證可以通過向用戶提供即時反饋并減少對服務器的請求數量來改善用戶體驗。但是,不應依賴它作為驗證輸入的唯一方法。客戶端驗證很容易被繞過或操縱,因此必須在服務器端驗證輸入,以確保安全性和數據完整性。
總結
有效的驗證對于任何 Web 應用程序的穩定性和安全性都是必不可少的。Spring Boot 提供了一套工具和庫來簡化驗證邏輯并使其更易于維護。通過遵循本文中討論的最佳實踐,您可以確保您的驗證組件有效并提供出色的用戶體驗。
-
參數
+關注
關注
11文章
1865瀏覽量
32770 -
spring
+關注
關注
0文章
340瀏覽量
14790 -
驗證
+關注
關注
0文章
62瀏覽量
15415 -
Boot
+關注
關注
0文章
153瀏覽量
36440
發布評論請先 登錄
相關推薦
Spring Boot嵌入式Web容器原理是什么
Spring Boot從零入門1 詳述
Spring Boot實現各種參數校驗
Spring Boot特有的實踐
強大的Spring Boot 3.0要來了
Spring Boot Web相關的基礎知識
簡述Spring Boot數據校驗
SpringBoot參數驗證的10個技巧1
Spring Boot應用中如何做好參數校驗?
Spring Boot Actuator快速入門
Spring Boot啟動 Eureka流程

Spring Boot的啟動原理

評論