溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么通過自定義spring?invalidator注解校驗數(shù)據(jù)合法性

發(fā)布時間:2022-07-04 13:42:59 來源:億速云 閱讀:136 作者:iii 欄目:開發(fā)技術

今天小編給大家分享一下怎么通過自定義spring invalidator注解校驗數(shù)據(jù)合法性的相關知識點,內(nèi)容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

自定義spring invalidator注解校驗數(shù)據(jù)合法性

在項目中經(jīng)常會對用戶輸入的數(shù)據(jù),或者外部導入到系統(tǒng)的數(shù)據(jù)做合法性檢查。在spring boot框架的微服務中可以使用invalidator注解對數(shù)據(jù)做合法性,安全性校驗。

下面給一個樣例說明如何自定義注解實現(xiàn)校驗邏輯。

1、定義校驗屬性字符串長度的注解

package com.elon.springbootdemo.manager.invalidator;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
/**
 * 屬性字段長度校驗注解定義。
 * 
 * @author elon
 * @version 2018年9月19日
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = FieldLengthInvalidatorImpl.class)
@Documented
public @interface FieldLengthInvalidator {
    // 字段支持的最大長度(字符數(shù))
    int maxLength() default 50;
    // 校驗失敗后返回的錯誤信息
    String message() default "";
    // 分組
    Class<?>[] groups() default {};
    // 負載
    Class<? extends Payload>[] payload() default {};
}

在定義注解時可聲明變量用于輔助校驗。上面的注解中定義了maxLength變量用于指定最大長度限制。變量可以設置默認值,使用注解時不傳參數(shù),變量就使用默認值。

2、實現(xiàn)校驗邏輯,校驗失敗后返回錯誤提示

package com.elon.springbootdemo.manager.invalidator;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
 * 字段長度校驗實現(xiàn)類。
 * 
 * @author elon
 * @version 2018年9月19日
 */
public class FieldLengthInvalidatorImpl implements ConstraintValidator<FieldLengthInvalidator, String> {
    private int maxLength = 0;
    @Override
    public void initialize(FieldLengthInvalidator invalidator) {
        maxLength = invalidator.maxLength();
    }
    @Override
    public boolean isValid(String fieldValue, ConstraintValidatorContext context) {
        if (fieldValue.length() > maxLength) {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate("對象屬性長度超過限制。").addConstraintViolation();
            // 校驗失敗返回false。返回true上游收集不到錯誤信息。
            return false;
        }
        return true;
    }
}

3、在模型字段屬性上增加校驗的注解

public class User
{
    private int userId = -1;
    @FieldLengthInvalidator(maxLength=10)
    private String name = "";
}

4、提供統(tǒng)一的校驗方法

package com.elon.springbootdemo.manager;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
/**
 * 有效性校驗管理類。對外提供統(tǒng)一的校驗調(diào)用接口。
 * @author elon
 * @version 2018年9月19日
 */
public class InvalidatorMgr {
    private InvalidatorMgr() {
        
    }
    
    /**
     * 獲取單例對象。
     * 
     * @return 單例對象
     */
    public static InvalidatorMgr instance() {
        return InvalidatorMgrBuilder.instance;
    }
    
    /**
     * 校驗模型所有屬性的有效性。
     * 
     * @param model 待校驗模型
     * @return 錯誤信息列表
     */
    public <T> List<String> validate(T model) {
        
        ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
        Validator validator = validatorFactory.getValidator();
        Set<ConstraintViolation<T>> resultSet = validator.validate(model);
        
        List<String> messageList = new ArrayList<>();
        resultSet.forEach((r)->messageList.add(r.getMessage()));        
        return messageList;
    }
    
    /**
     * 單例構建器。
     * @author elon
     * @version 2018年9月19日
     */
    private static class InvalidatorMgrBuilder{
        private static InvalidatorMgr instance = new InvalidatorMgr();
    }
}

5、業(yè)務層調(diào)用校驗方法

        User user = new User();
        user.setName("ahskahskhqlwjqlwqlwhqlhwlqjwlqhwlhqwhqlwjjqlwl");
        List<String> messageList = InvalidatorMgr.instance().validate(user);
        System.out.println(messageList);

invalidator注解主要用于實現(xiàn)長度,范圍,非法字符等通用的規(guī)則校驗。不適合用于做業(yè)務邏輯的校驗,特定的業(yè)務校驗寫在業(yè)務層。 

springboot 參數(shù)驗證 validation

1、綜述

springboot提供了強大的基于注解的、開箱即用的驗證功能,這種基于bean validation的實現(xiàn)和 hibernate validator類似

2、依賴

創(chuàng)建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.h3database</groupId> 
    <artifactId>h3</artifactId>
    <version>1.4.197</version> 
    <scope>runtime</scope>
</dependency>

3、定義實體類

測試項目為了方便,直接用JPA,使用@NotBlank指定非空字段,message是驗證觸發(fā)后返回的信息,還有@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    
    
}

創(chuàng)建JPA的repository定義增刪改查接口

@Repository
public interface UserRepository extends CrudRepository<User, Long> {}

4、創(chuàng)建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發(fā)現(xiàn)帶有@Valid注解的參數(shù)時,會自動引導默認的JSR 380驗證器驗證參數(shù)。當目標參數(shù)未能通過驗證時,Spring Boot將拋出一個MethodArgumentNotValidException

5、實現(xiàn)ExceptionHandler

直接拋出異常顯然是不合理的,大部分情況需要經(jīng)過處理返回給前端更友好的提示信息,通過@ExceptionHandler來處理拋出的異常實現(xiàn)該功能

@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執(zhí)行validition觸發(fā)時會調(diào)用此實現(xiàn),該方法將每個無效字段的名稱和驗證后錯誤消息存儲在映射中,然后它將映射作為JSON表示形式發(fā)送回客戶端進行進一步處理。

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實現(xiàn)的一組靜態(tài)方法測試請求和響應。測試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\" : \"bob@domain.com\"}";
    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\" : \"bob@domain.com\"}";
    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", "bob@domain.com");
            User user2 = new User("Jenny", "jenny@domain.com");
            userRepository.save(user1);
            userRepository.save(user2);
            userRepository.findAll().forEach(System.out::println);
        };
    }
}

如果用沒有用戶名或郵箱的數(shù)據(jù)發(fā)送請求會收到返回的提示信息

{
  "name":"Name is mandatory",
  "email":"Email is mandatory"
}

以上就是“怎么通過自定義spring invalidator注解校驗數(shù)據(jù)合法性”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI