SpringBoot的10個(gè)參數(shù)驗(yàn)證技巧分享
1.使用驗(yàn)證注解
Spring Boot提供了內(nèi)置的驗(yàn)證注解,可以幫助簡(jiǎn)單、快速地對(duì)輸入字段進(jìn)行驗(yàn)證,例如檢查 null 或空字段、強(qiáng)制執(zhí)行長(zhǎng)度限制、使用正則表達(dá)式驗(yàn)證模式以及驗(yàn)證電子郵件地址。
一些最常用的驗(yàn)證注釋包括:
@NotNull:指定字段不能為空。
@NotEmpty:指定列表字段不能為空。
@NotBlank:指定字符串字段不得為空或僅包含空格。
@Min 和 @Max:指定數(shù)字字段的最小值和最大值。
@Pattern:指定字符串字段必須匹配的正則表達(dá)式模式。
@Email:指定字符串字段必須是有效的電子郵件地址。
具體用法參考下面例子:
public class User { @NotNull private Long id;
@NotBlank @Size(min = 2, max = 50) private String firstName; @NotBlank @Size(min = 2, max = 50) private String lastName; @Email private String email; @NotNull @Min(18) @Max(99) private Integer age; @NotEmpty private List<String> hobbies; @Pattern(regexp = "[A-Z]{2}\d{4}") private String employeeId;
2 使用自定義驗(yàn)證注解
雖然 Spring Boot 的內(nèi)置驗(yàn)證注釋很有用,但它們可能無(wú)法涵蓋所有情況。如果有特殊參數(shù)驗(yàn)證的場(chǎng)景,可以使用 Spring 的 JSR 303 驗(yàn)證框架創(chuàng)建自定義驗(yàn)證注釋。自定義注解可以讓你的的驗(yàn)證邏輯更具可重用性和可維護(hù)性。
假設(shè)我們有一個(gè)應(yīng)用程序,用戶可以在其中創(chuàng)建帖子。每個(gè)帖子都應(yīng)該有一個(gè)標(biāo)題和一個(gè)正文,并且標(biāo)題在所有帖子中應(yīng)該是唯一的。雖然 Spring Boot 提供了用于檢查字段是否為空的內(nèi)置驗(yàn)證注釋,但它沒有提供用于檢查唯一性的內(nèi)置驗(yàn)證注釋。在這種情況下,我們可以創(chuàng)建一個(gè)自定義驗(yàn)證注解來(lái)處理這種情況。
首先,我們創(chuàng)建自定義約束注解UniqueTitle :
@Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = UniqueTitleValidator.class) public @interface UniqueTitle { String message() default “Title must be unique”; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
接下來(lái),我們創(chuàng)建一個(gè)PostRepository接口,目的是從數(shù)據(jù)庫(kù)中檢索帖子:
public interface PostRepository extends JpaRepository<Post, Long> { Post findByTitle(String title); }
然后我們需要定義驗(yàn)證器類 UniqueTitleValidator,如下所示:
@Component public class UniqueTitleValidator implements ConstraintValidator<UniqueTitle, String> { @Autowired private PostRepository postRepository; @Override public boolean isValid(String title, ConstraintValidatorContext context) { if (title == null) { return true; } return Objects.isNull(postRepository.findByTitle(title)); } }
UniqueTitleValidator實(shí)現(xiàn)了ConstraintValidator接口,它有兩個(gè)泛型類型:第一個(gè)是自定義注解UniqueTitle,第二個(gè)是正在驗(yàn)證的字段類型(在本例中為String). 我們還自動(dòng)裝配了PostRepository 類以從數(shù)據(jù)庫(kù)中檢索帖子。
isValid()方法通過查詢 PostRepository 來(lái)檢查 title 是否為 null 或者它是否是唯一的。如果 title 為 null 或唯一,則驗(yàn)證成功,并返回 true。
定義了自定義驗(yàn)證注釋和驗(yàn)證器類后,我們現(xiàn)在可以使用它來(lái)驗(yàn)證 Spring Boot 應(yīng)用程序中的帖子標(biāo)題:
public class Post { @UniqueTitle private String title; @NotNull private String body; }
我們已將 @UniqueTitle 注釋應(yīng)用于 Post 類中的 title 變量。驗(yàn)證此字段時(shí),這將觸發(fā) UniqueTitleValidator 類中定義的驗(yàn)證邏輯。
3 在服務(wù)器端驗(yàn)證
除了前端或者客戶端做了驗(yàn)證意外,服務(wù)器端驗(yàn)證輸入是至關(guān)重要的。它可以確保在處理或存儲(chǔ)任何惡意或格式錯(cuò)誤的數(shù)據(jù)之前將其捕獲,這對(duì)于應(yīng)用程序的安全性和穩(wěn)定性至關(guān)重要。
假設(shè)我們有一個(gè)允許用戶創(chuàng)建新帳戶的 REST 端點(diǎn)。端點(diǎn)需要一個(gè)包含用戶用戶名和密碼的 JSON 請(qǐng)求體。為確保輸入有效,我們可以創(chuàng)建一個(gè) DTO(數(shù)據(jù)傳輸對(duì)象)類并將驗(yàn)證注釋應(yīng)用于其字段:
public class UserDTO { @NotBlank private String username; @NotBlank private String password; }
我們使用@NotBlank注解來(lái)確保username和password字段不為空或 null。
接下來(lái),我們可以創(chuàng)建一個(gè)控制器方法來(lái)處理 HTTP POST 請(qǐng)求并在創(chuàng)建新用戶之前驗(yàn)證輸入:
@RestController @RequestMapping(“/users”) @Validated public class UserController { @Autowired private UserService userService; @PostMapping public ResponseEntity<String> createUser(@Valid @RequestBody UserDTO userDto) { userService.createUser(userDto); return ResponseEntity.status(HttpStatus.CREATED).body("User created successfully"); } }
我們使用 Spring 的@Validated注解來(lái)啟用方法級(jí)驗(yàn)證,我們還將 @Valid 注釋應(yīng)用于 userDto 參數(shù)以觸發(fā)驗(yàn)證過程。
4 提供有意義的錯(cuò)誤信息
當(dāng)驗(yàn)證失敗時(shí),必須提供清晰簡(jiǎn)潔的錯(cuò)誤消息來(lái)描述出了什么問題以及如何修復(fù)它。
這是一個(gè)示例,如果我們有一個(gè)允許用戶創(chuàng)建新用戶的 RESTful API。我們要確保姓名和電子郵件地址字段不為空,年齡在 18 到 99 歲之間,除了這些字段,如果用戶嘗試使用重復(fù)的“用戶名”創(chuàng)建帳戶,我們還會(huì)提供明確的錯(cuò)誤消息或“電子郵件”。
為此,我們可以定義一個(gè)帶有必要驗(yàn)證注釋的模型類 User,如下所示:
public class User { @NotBlank(message = "用戶名不能為空") private String name; @NotBlank(message = "Email不能為空") @Email(message = "無(wú)效的Emaild地址") private String email; @NotNull(message = "年齡不能為空") @Min(value = 18, message = "年齡必須大于18") @Max(value = 99, message = "年齡必須小于99") private Integer age; }
我們使用 message屬性為每個(gè)驗(yàn)證注釋提供了自定義錯(cuò)誤消息。
接下來(lái),在我們的 Spring 控制器中,我們可以處理表單提交并使用 @Valid 注釋驗(yàn)證用戶輸入:
@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 注釋來(lái)觸發(fā) User 對(duì)象的驗(yàn)證,并使用 BindingResult 對(duì)象來(lái)捕獲任何驗(yàn)證錯(cuò)誤。
5 將 i18n 用于錯(cuò)誤消息
如果你的應(yīng)用程序支持多種語(yǔ)言,則必須使用國(guó)際化 (i18n) 以用戶首選語(yǔ)言顯示錯(cuò)誤消息。
以下是在 Spring Boot 應(yīng)用程序中使用 i18n 處理錯(cuò)誤消息的示例
首先,在資源目錄下創(chuàng)建一個(gè)包含默認(rèn)錯(cuò)誤消息的 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.
接下來(lái),為每種支持的語(yǔ)言創(chuàng)建一個(gè) messages_xx.properties 文件,例如,中文的messages_zh_CN.properties。
user.name.required=名稱不能為空. user.email.invalid=無(wú)效的email格式. user.age.invalid=年齡必須在18到99歲之間.
然后,更新您的驗(yàn)證注釋以使用本地化的錯(cuò)誤消息
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)在,當(dāng)發(fā)生驗(yàn)證錯(cuò)誤時(shí),錯(cuò)誤消息將根據(jù)隨請(qǐng)求發(fā)送的“Accept-Language”標(biāo)頭以用戶的首選語(yǔ)言顯示。
6 使用分組驗(yàn)證
驗(yàn)證組是 Spring Boot 驗(yàn)證框架的一個(gè)強(qiáng)大功能,允許您根據(jù)其他輸入值或應(yīng)用程序狀態(tài)應(yīng)用條件驗(yàn)證規(guī)則。
現(xiàn)在有一個(gè)包含三個(gè)字段的User類的情況下:firstName、lastName和email。我們要確保如果 email 字段為空,則 firstName 或 lastName 字段必須非空。否則,所有三個(gè)字段都應(yīng)該正常驗(yàn)證。
為此,我們將定義兩個(gè)驗(yàn)證組:EmailNotEmpty 和 Default。EmailNotEmpty 組將包含當(dāng) email 字段不為空時(shí)的驗(yàn)證規(guī)則,而 Default 組將包含所有三個(gè)字段的正常驗(yàn)證規(guī)則。
創(chuàng)建帶有驗(yàn)證組的 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 {} }
請(qǐng)注意,我們?cè)赨ser類中定義了兩個(gè)接口,EmailNotEmpty和 Default。這些將作為我們的驗(yàn)證組。
接下來(lái),我們更新Controller使用這些驗(yàn)證組
@RestController @RequestMapping(“/users”) @Validated public class UserController { public ResponseEntity 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注釋添加到我們的控制器,表明我們想要使用驗(yàn)證組。我們還更新了 createUser 方法,將兩個(gè) User 對(duì)象作為輸入,一個(gè)在 email 字段不為空時(shí)使用,另一個(gè)在它為空時(shí)使用。
@Validated 注釋用于指定將哪個(gè)驗(yàn)證組應(yīng)用于每個(gè) User 對(duì)象。對(duì)于 userWithEmail 參數(shù),我們指定了 EmailNotEmpty 組,而對(duì)于 userWithoutEmail 參數(shù),我們指定了 Default 組。
進(jìn)行這些更改后,現(xiàn)在將根據(jù)“電子郵件”字段是否為空對(duì)“用戶”類進(jìn)行不同的驗(yàn)證。如果為空,則 firstName 或 lastName 字段必須非空。否則,所有三個(gè)字段都將正常驗(yàn)證。
7 對(duì)復(fù)雜邏輯使用跨域驗(yàn)證
如果需要驗(yàn)證跨多個(gè)字段的復(fù)雜輸入規(guī)則,可以使用跨字段驗(yàn)證來(lái)保持驗(yàn)證邏輯的組織性和可維護(hù)性??缱侄悟?yàn)證可確保所有輸入值均有效且彼此一致,從而防止出現(xiàn)意外行為。
假設(shè)我們有一個(gè)表單,用戶可以在其中輸入任務(wù)的開始日期和結(jié)束日期,并且我們希望確保結(jié)束日期不早于開始日期。我們可以使用跨域驗(yàn)證來(lái)實(shí)現(xiàn)這一點(diǎn)。
首先,我們定義一個(gè)自定義驗(yà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)建驗(yàn)證器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注釋應(yīng)用于我們的表單對(duì)象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)在,當(dāng)用戶提交表單時(shí),驗(yàn)證框架將自動(dòng)檢查結(jié)束日期是否晚于開始日期,如果不是,則提供有意義的錯(cuò)誤消息。
8 對(duì)驗(yàn)證錯(cuò)誤使用異常處理
可以使用異常處理ExceptionHandler來(lái)統(tǒng)一捕獲和處理驗(yàn)證錯(cuò)誤。
以下是如何在 Spring Boot 中使用異常處理來(lái)處理驗(yàn)證錯(cuò)誤的示例:
@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)建了一個(gè)用 @RestControllerAdvice 注解的 RestExceptionHandler 類來(lái)處理我們的 REST API 拋出的異常。然后我們創(chuàng)建一個(gè)用@ExceptionHandler注解的方法來(lái)處理在驗(yàn)證失敗時(shí)拋出的 MethodArgumentNotValidException。
在處理程序方法中,我們創(chuàng)建了一個(gè) Map 對(duì)象來(lái)保存錯(cuò)誤響應(yīng)的詳細(xì)信息,包括時(shí)間戳、HTTP 狀態(tài)代碼和錯(cuò)誤消息列表。我們使用 MethodArgumentNotValidException 對(duì)象的 getBindingResult() 方法獲取所有驗(yàn)證錯(cuò)誤并將它們添加到錯(cuò)誤消息列表中。
最后,我們返回一個(gè)包含錯(cuò)誤響應(yīng)詳細(xì)信息的ResponseEntity對(duì)象,包括作為響應(yīng)主體的錯(cuò)誤消息列表、HTTP 標(biāo)頭和 HTTP 狀態(tài)代碼。
有了這個(gè)異常處理代碼,我們的 REST API 拋出的任何驗(yàn)證錯(cuò)誤都將被捕獲并以結(jié)構(gòu)化和有意義的格式返回給用戶,從而更容易理解和解決問題。
9 測(cè)試你的驗(yàn)證邏輯
需要為你的驗(yàn)證邏輯編寫單元測(cè)試,以幫助確保它正常工作。
@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<ConstraintViolation<User>> violations = validator.validate(user); assertEquals(1, violations.size()); assertEquals("must be a well-formed email address", violations.iterator().next().getMessage()); } }
我們使用 JUnit 5 編寫一個(gè)測(cè)試來(lái)驗(yàn)證具有無(wú)效電子郵件地址的“用戶”對(duì)象。然后我們使用 Validator 接口來(lái)驗(yàn)證 User 對(duì)象并檢查是否返回了預(yù)期的驗(yàn)證錯(cuò)誤。
10 考慮客戶端驗(yàn)證
客戶端驗(yàn)證可以通過向用戶提供即時(shí)反饋并減少對(duì)服務(wù)器的請(qǐng)求數(shù)量來(lái)改善用戶體驗(yàn)。但是,不應(yīng)依賴它作為驗(yàn)證輸入的唯一方法。客戶端驗(yàn)證很容易被繞過或操縱,因此必須在服務(wù)器端驗(yàn)證輸入,以確保安全性和數(shù)據(jù)完整性。
總結(jié)
有效的驗(yàn)證對(duì)于任何 Web 應(yīng)用程序的穩(wěn)定性和安全性都是必不可少的。Spring Boot 提供了一套工具和庫(kù)來(lái)簡(jiǎn)化驗(yàn)證邏輯并使其更易于維護(hù)。通過遵循本文中討論的最佳實(shí)踐,您可以確保您的驗(yàn)證組件有效并提供出色的用戶體驗(yàn)。
以上就是SpringBoot的10個(gè)參數(shù)驗(yàn)證技巧分享的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot參數(shù)驗(yàn)證的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- SpringBoot如何優(yōu)雅實(shí)現(xiàn)接口參數(shù)驗(yàn)證
- Springboot中攔截GET請(qǐng)求獲取請(qǐng)求參數(shù)驗(yàn)證合法性核心方法
- SpringBoot參數(shù)驗(yàn)證10個(gè)技巧值得收藏
- 10個(gè)SpringBoot參數(shù)驗(yàn)證你需要知道的技巧分享
- SpringBoot validator參數(shù)驗(yàn)證restful自定義錯(cuò)誤碼響應(yīng)方式
- SpringBoot整合Hibernate Validator實(shí)現(xiàn)參數(shù)驗(yàn)證功能
相關(guān)文章
使用Maven進(jìn)行依賴漏洞檢查的實(shí)現(xiàn)指南
在現(xiàn)代軟件開發(fā)中,開源庫(kù)的使用變得愈加普遍和重要,然而,這些開源庫(kù)中的漏洞往往會(huì)成為潛在的安全風(fēng)險(xiǎn),在本文中,我們將探討如何使用 Maven 進(jìn)行依賴漏洞檢查,以確保項(xiàng)目的安全性和穩(wěn)定性,需要的朋友可以參考下2024-05-05SpringCloud Config統(tǒng)一配置中心問題分析解決與客戶端動(dòng)態(tài)刷新實(shí)現(xiàn)
springcloud config是一個(gè)解決分布式系統(tǒng)的配置管理方案。它包含了 client和server兩個(gè)部分,server端提供配置文件的存儲(chǔ)、以接口的形式將配置文件的內(nèi)容提供出去,client端通過接口獲取數(shù)據(jù)、并依據(jù)此數(shù)據(jù)初始化自己的應(yīng)用2022-10-10SpringSecurity角色權(quán)限控制(SpringBoot+SpringSecurity+JWT)
本文主要介紹了SpringSecurity角色權(quán)限控制(SpringBoot+SpringSecurity+JWT),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-05-05rabbitmq的消息持久化處理開啟,再關(guān)閉后,消費(fèi)者啟動(dòng)報(bào)錯(cuò)問題
這篇文章主要介紹了rabbitmq的消息持久化處理開啟,再關(guān)閉后,消費(fèi)者啟動(dòng)報(bào)錯(cuò)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11Java實(shí)現(xiàn)生成自定義時(shí)長(zhǎng)的靜音音頻
這篇文章主要介紹了如何通過Java實(shí)現(xiàn)一個(gè)音頻工具類,可以實(shí)現(xiàn)生成一段自定義時(shí)長(zhǎng)(精確到毫秒)的wav音頻。感興趣的小伙伴可以了解一下2022-01-01詳解rabbitmq使用springboot實(shí)現(xiàn)fanout模式
這篇文章主要介紹了rabbitmq使用springboot實(shí)現(xiàn)fanout模式,Fanout特點(diǎn)是發(fā)布與訂閱模式,是一種廣播機(jī)制,它是沒有路由key的模式,需要的朋友可以參考下2023-07-07SpringBoot處理請(qǐng)求參數(shù)中包含特殊符號(hào)
今天寫代碼遇到了一個(gè)問題,請(qǐng)求參數(shù)是個(gè)路徑“D:/ExcelFile”,本文就詳細(xì)的介紹一下該錯(cuò)誤的解決方法,感興趣的可以了解一下2021-06-06SpringBoot使用MockMvc測(cè)試get和post接口的示例代碼
Spring Boot MockMvc是一個(gè)用于單元測(cè)試的模塊,它是Spring框架的一部分,專注于簡(jiǎn)化Web應(yīng)用程序的測(cè)試,MockMvc主要用來(lái)模擬一個(gè)完整的HTTP請(qǐng)求-響應(yīng)生命周期,本文給大家介紹了SpringBoot使用MockMvc測(cè)試get和post接口,需要的朋友可以參考下2024-06-06