1、綜述
springboot提供了強大的基於註解的、開箱即用的驗證功能,這種基於bean validation的實現和 hibernate validator類似
2、依賴
創建springboot項目,包含以下依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.197</version>
<scope>runtime</scope>
</dependency>
3、定義實體類
測試項目爲了方便,直接用JPA,使用@NotBlank指定非空字段,message是驗證觸發後返回的信息,還有@Null、@NotNull、@NotBlank、@Email、@Max、@Min、@Size、@Negative、@DecimalMax、@DecimalMin、@Positive、@PositiveOrZero、@NegativeOrZero、@AssertTrue、@AssertFalse、@Future、@FutureOrPresent、@Past、@PastOrPresent、@Pattern
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@NotBlank(message = "Name is mandatory")
private String name;
@NotBlank(message = "Email is mandatory")
private String email;
// standard constructors / setters / getters / toString
}
創建JPA的repository定義增刪改查接口
@Repository
public interface UserRepository extends CrudRepository<User, Long> {}
4、創建rest controller
@RestController
public class UserController {
@PostMapping("/users")
ResponseEntity<String> addUser(@Valid @RequestBody User user) {
// persisting the user
return ResponseEntity.ok("User is valid");
}
// standard constructors / other methods
}
接收到的user對象添加了@Valid,當Spring Boot發現帶有@Valid註解的參數時,會自動引導默認的JSR 380驗證器驗證參數。當目標參數未能通過驗證時,Spring Boot將拋出一個MethodArgumentNotValidException
5、實現ExceptionHandler
直接拋出異常顯然是不合理的,大部分情況需要經過處理返回給前端更友好的提示信息,通過@ExceptionHandler來處理拋出的異常實現該功能
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public Map<String, String> handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return errors;
}
MethodArgumentNotValidException作爲上一步拋出的異常,當springboot執行validition觸發時會調用此實現,該方法將每個無效字段的名稱和驗證後錯誤消息存儲在映射中,然後它將映射作爲JSON表示形式發送回客戶端進行進一步處理。
6、寫測試代碼
使用springboot自帶的插件進行測試rest controller,
@RunWith(SpringRunner.class)
@WebMvcTest
@AutoConfigureMockMvc
public class UserControllerIntegrationTest {
@MockBean
private UserRepository userRepository;
@Autowired
UserController userController;
@Autowired
private MockMvc mockMvc;
//...
}
@WebMvcTest允許我們使用MockMvcRequestBuilders和MockMvcResultMatchers實現的一組靜態方法測試請求和響應。測試addUser()方法,在請求體中傳遞一個有效的User對象和一個無效的User對象。
@Test
public void whenPostRequestToUsersAndValidUser_thenCorrectResponse() throws Exception {
MediaType textPlainUtf8 = new MediaType(MediaType.TEXT_PLAIN, Charset.forName("UTF-8"));
String user = "{\"name\": \"bob\", \"email\" : \"[email protected]\"}";
mockMvc.perform(MockMvcRequestBuilders.post("/users")
.content(user)
.contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content()
.contentType(textPlainUtf8));
}
@Test
public void whenPostRequestToUsersAndInValidUser_thenCorrectResponse() throws Exception {
String user = "{\"name\": \"\", \"email\" : \"[email protected]\"}";
mockMvc.perform(MockMvcRequestBuilders.post("/users")
.content(user)
.contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(MockMvcResultMatchers.status().isBadRequest())
.andExpect(MockMvcResultMatchers.jsonPath("$.name", Is.is("Name is mandatory")))
.andExpect(MockMvcResultMatchers.content()
.contentType(MediaType.APPLICATION_JSON_UTF8));
}
}
也可以使用postman或fiddler來測試REST controller API。
7、跑測試
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public CommandLineRunner run(UserRepository userRepository) throws Exception {
return (String[] args) -> {
User user1 = new User("Bob", "[email protected]");
User user2 = new User("Jenny", "[email protected]");
userRepository.save(user1);
userRepository.save(user2);
userRepository.findAll().forEach(System.out::println);
};
}
}
如果用沒有用戶名或郵箱的數據發送請求會收到返回的提示信息
{
"name":"Name is mandatory",
"email":"Email is mandatory"
}
8、自定義註解
在進行參數驗證的時候,往往存在現有的約束註解不能滿足的情況,此時就需要我們自己定義validation註解了,下面來介紹一下如何自己定義一個驗證註解。