4 提供有意義的錯誤信息
當驗證失敗時,必須提供清晰簡潔的錯誤消息來描述出了什么問題以及如何修復它。
這是一個示例,如果我們有一個允許用戶創(chuàng)建新用戶的 RESTful API。我們要確保姓名和電子郵件地址字段不為空,年齡在 18 到 99 歲之間,除了這些字段,如果用戶嘗試使用重復的“用戶名”創(chuàng)建帳戶,我們還會提供明確的錯誤消息或“電子郵件”。
為此,我們可以定義一個帶有必要驗證注釋的模型類 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注釋來觸發(fā)User對象的驗證,并使用BindingResult對象來捕獲任何驗證錯誤。
5 將 i18n 用于錯誤消息
如果你的應用程序支持多種語言,則必須使用國際化 (i18n) 以用戶首選語言顯示錯誤消息。
以下是在 Spring Boot 應用程序中使用 i18n 處理錯誤消息的示例
- 首先,在資源目錄下創(chuàng)建一個包含默認錯誤消息的
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.
- 接下來,為每種支持的語言創(chuàng)建一個
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;
}
}
- 現(xiàn)在,當發(fā)生驗證錯誤時,錯誤消息將根據(jù)隨請求發(fā)送的“Accept-Language”標頭以用戶的首選語言顯示。
6 使用分組驗證
驗證組是 Spring Boot 驗證框架的一個強大功能,允許您根據(jù)其他輸入值或應用程序狀態(tài)應用條件驗證規(guī)則。
現(xiàn)在有一個包含三個字段的User類的情況下:firstName、lastName和email。我們要確保如果 email 字段為空,則 firstName 或 lastName 字段必須非空。否則,所有三個字段都應該正常驗證。
為此,我們將定義兩個驗證組:EmailNotEmpty 和 Default。EmailNotEmpty 組將包含當 email 字段不為空時的驗證規(guī)則,而 Default 組將包含所有三個字段的正常驗證規(guī)則。
- 創(chuàng)建帶有驗證組的
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參數(shù),我們指定了EmailNotEmpty組,而對于userWithoutEmail參數(shù),我們指定了Default組。
- 進行這些更改后,現(xiàn)在將根據(jù)“電子郵件”字段是否為空對“用戶”類進行不同的驗證。如果為空,則
firstName或lastName字段必須非空。否則,所有三個字段都將正常驗證。
7 對復雜邏輯使用跨域驗證
如果需要驗證跨多個字段的復雜輸入規(guī)則,可以使用跨字段驗證來保持驗證邏輯的組織性和可維護性??缱侄悟炞C可確保所有輸入值均有效且彼此一致,從而防止出現(xiàn)意外行為。
假設我們有一個表單,用戶可以在其中輸入任務的開始日期和結(jié)束日期,并且我們希望確保結(jié)束日期不早于開始日期。我們可以使用跨域驗證來實現(xiàn)這一點。
- 首先,我們定義一個自定義驗證注解
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 {};
}
- 然后,我們創(chuàng)建驗證器
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;
}
現(xiàn)在,當用戶提交表單時,驗證框架將自動檢查結(jié)束日期是否晚于開始日期,如果不是,則提供有意義的錯誤消息。
8 對驗證錯誤使用異常處理
可以使用異常處理ExceptionHandler來統(tǒng)一捕獲和處理驗證錯誤。
以下是如何在 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);
}
}
在這里,我們創(chuàng)建了一個用 @RestControllerAdvice 注解的 RestExceptionHandler 類來處理我們的 REST API 拋出的異常。然后我們創(chuàng)建一個用@ExceptionHandler注解的方法來處理在驗證失敗時拋出的 MethodArgumentNotValidException。
在處理程序方法中,我們創(chuàng)建了一個 Map 對象來保存錯誤響應的詳細信息,包括時間戳、HTTP 狀態(tài)代碼和錯誤消息列表。我們使用 MethodArgumentNotValidException 對象的 getBindingResult() 方法獲取所有驗證錯誤并將它們添加到錯誤消息列表中。
最后,我們返回一個包含錯誤響應詳細信息的ResponseEntity對象,包括作為響應主體的錯誤消息列表、HTTP 標頭和 HTTP 狀態(tài)代碼。
有了這個異常處理代碼,我們的 REST API 拋出的任何驗證錯誤都將被捕獲并以結(jié)構(gòu)化和有意義的格式返回給用戶,從而更容易理解和解決問題。
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 考慮客戶端驗證
客戶端驗證可以通過向用戶提供即時反饋并減少對服務器的請求數(shù)量來改善用戶體驗。但是,不應依賴它作為驗證輸入的唯一方法??蛻舳蓑炞C很容易被繞過或操縱,因此必須在服務器端驗證輸入,以確保安全性和數(shù)據(jù)完整性。
總結(jié)
有效的驗證對于任何 Web 應用程序的穩(wěn)定性和安全性都是必不可少的。Spring Boot 提供了一套工具和庫來簡化驗證邏輯并使其更易于維護。通過遵循本文中討論的最佳實踐,您可以確保您的驗證組件有效并提供出色的用戶體驗。
-
參數(shù)
+關(guān)注
關(guān)注
11文章
1868瀏覽量
33866 -
spring
+關(guān)注
關(guān)注
0文章
341瀏覽量
15849 -
驗證
+關(guān)注
關(guān)注
0文章
65瀏覽量
15692 -
Boot
+關(guān)注
關(guān)注
0文章
154瀏覽量
37613
發(fā)布評論請先 登錄
Spring Boot嵌入式Web容器原理是什么
Spring Boot從零入門1 詳述
Spring Boot實現(xiàn)各種參數(shù)校驗
Spring Boot特有的實踐
強大的Spring Boot 3.0要來了
Spring Boot Web相關(guān)的基礎(chǔ)知識
簡述Spring Boot數(shù)據(jù)校驗
SpringBoot參數(shù)驗證的10個技巧1
Spring Boot應用中如何做好參數(shù)校驗?
Spring Boot Actuator快速入門
Spring Boot啟動 Eureka流程
Spring Boot的啟動原理
Spring Boot應用中如何做好參數(shù)校驗?2
評論