springboot 參數驗證 validation

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註解了,下面來介紹一下如何自己定義一個驗證註解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章