Mikilin introduction
This framework is an attribute verification framework for objects. Hibernate. Validate directly, but is simpler to use and extend than it is functional. Adhering to the principle of simplicity, the introduction of checker and matcher mechanism can make all kinds of complex matching very simple. There are two built-in checkers: blacklist and whitelist. Matchers set different matching strategies for different types. Most matches are based on primitive types, and complex types (collections, maps, or custom types) are made up of primitive types. The framework supports the interception of complex types by dismantling them and checking the internal matching types. The framework has the following features:
Functional:
- Full type: You can check all types, basic types, complex types, collections, maps, and other types with fixed attributes
- Matchers: Mechanisms for matching types: grouping, value lists, attribute classes, specified model types, regular expressions, system callbacks (extensions), enumerated types, range decisions (supports time ranges), and expression language decisions
- Black and white mechanism: after matching, whether data is rejected or received. Receive Indicates that only matched values are received, which is a whitelist concept. Reject Indicates that only matched values are rejected, which is a blacklist concept
Non-functional:
- Zero intrusion: Zero intrusion into code and exists only as a utility class
- Easy to use: super simple to use, one class, two types of checkers, three annotations, multiple matchers
- High performance: all checks are directly called in memory. After building the matching tree for the first time, there is no need to rebuild it
- Extensible: For properties that are difficult to verify, you can either customize the matcher properties or use Spring beans as system matcher classes
Using document
Mikilin document
A quick start
This tool is extremely simple to use, so to speak, as long as you can use a annotation Matcher and a method mkvalidators.check (Object obj). MkValidators. Check (Object obj) fails if the value matches the attributes in the annotations. And through MkValidators. GetErrMsgChain get all error messages or through MkValidators getErrMsg get one wrong information.
Maven is introduced into
Has been released to the central warehouse, self – available
<dependency>
<groupId>com.github.simonalong</groupId>
<artifactId>mikilin</artifactId>
<! -- Please replace with the latest version -->
<version>${latest.release.version}</version>
</dependency>
Copy the code
use
The framework is extremely simple to use (just refer to the Spring-validate framework) as follows: Annotate the attributes you want to intercept
@Data
@Accessors(chain = true)
public class WhiteAEntity {
// Modify the attribute name, allowing only a, b,c, and null
@ the Matcher (value = {" a ", "b", "c", "null"}, errMsg = "input value is not in conformity with the requirements")
private String name;
private String address;
}
Copy the code
Hard coded
Add verification at the interception location, here is a layer of verification, in business code it is recommended to wrap in AOP to implement interception invisible to the business user
import lombok.SneakyThrows;
@Test
@SneakyThrows
public void test1(a){
WhiteAEntity whiteAEntity = new WhiteAEntity();
whiteAEntity.setName("d");
// You can use checks with return values
if(! MkValidators.check(whiteAEntity)) {// Output: data verification failed --> property name value D is not in available only list [NULL, a, b, C] --> type WhiteAEntity verification failed
System.out.println(MkValidators.getErrMsgChain());
// Output: the input value does not meet the requirement
System.out.println(MkValidators.getErrMsg());
}
// Alternatively, you can use an exception throw check. This API is mkvalidators. check with exceptions
MkValidators.validate(whiteAEntity);
}
Copy the code
Automatic verification
Version: >= 1.6.0 If the @autoCheck annotation is added in version 1.6.0 to modify classes and methods, the system automatically checks parameters in methods or methods of classes. If the parameters do not meet requirements, an MkException is reported
/** * auto-verify annotations ** <p> Verifies arguments in modified classes and methods * <ul> * <li>1. Modifier class: checks all parameters of all functions below the class </li> * <li>2. Modifier function: all parameters of the function </li> * </ul> * are checked@author shizi
* @since 2020/6/25 11:20 AM
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface AutoCheck {
With {/ * * *@link AutoCheck#group()}
* @returnGrouping * /
String value(a) default MkConstant.DEFAULT_GROUP;
/** * Check the group *@returnGrouping * /
String group(a) default MkConstant.DEFAULT_GROUP;
}
Copy the code
usage
@AutoCheck
@RequestMapping("${api-prefix}/deploy")
@RestController
public class DeployController {
/ /...
/** * start build */
@AutoCheck("startBuild")
@PutMapping("startBuild")
public Integer startBuild(@RequestBody AppIdReq appIdReq) {
/ /...
}
/ /...
}
Copy the code
2. Common scenarios
The following scenarios are based on actual service scenarios
1. The field cannot be empty
The user ID cannot be empty
/** * Invited user id */
@Matcher(notNull = "true")
private Long userId;
Copy the code
2. The string attribute cannot be empty
The user name cannot be empty
/** * User name */
@Matcher(notBlank = "true")
private String name;
Copy the code
3. The value can be 0 or 1
The type of the corresponding attribute can only be two or more values
/** * Required, 0: unnecessary, 1: mandatory */
@Matcher(value = {"0", "1"})
private Integer needKey;
Copy the code
4. Multiple fixed values of type
The state of the corresponding attribute can be 0, 1, 2, 3, 4, 5, 6, or 7
/** * status: 0 not built, 1 Compiling, 2 packaging, 3 Deploying, 4 testing, 5 tested completed, 6 Released, 7 released */
/ / @ the Matcher (value = {" 0 ", "1", "2", "3", "4", "5", "6", "7"}) can also use the following
@Matcher(range = "[0, 8]")
private Integer deployStatus = 0;
Copy the code
5. The corresponding value is mailbox
The field is mailbox judgment. In addition to email, there are four fixed types of judgment: mobile phone number, fixed phone number, ID number and IP address.
/** * email */
@matcher (notNull = "false", model = fieldmodel. MAIL, errMsg = "email: #current not valid ")
private String email;
Copy the code
6. Length of corresponding set
A maximum of three images can be uploaded from the front end
/** * The maximum number of preview images is three */
@Matcher(range = "(, 3]")
private List<String> prePicUrlList;
Copy the code
7. The maximum length is 128 characters
The length of characters passed by the front end is limited to 128 because database fields are stored up to 128
/** * Address length */
@Matcher(range = "[0, 128]")
private String nameStr;
Copy the code
8. If the data is empty or not empty, the length cannot exceed 200
Where the matching between multiple attributes inside @matcher is the relationship of or
/** * Project description, which can be empty but cannot exceed 200 */
@matcher (notBlank = "false", range = "[0, 200]", errMsg ="
private String proDesc;
Copy the code
9. The id passed from the front end must exist in the DB, and the data cannot be empty
@matcher supports multiple superpositions, representing the actions and actions of multiple conditions
@Matcher(notNull = "true")
@matcher (customize = "com.xxx.yyy.ExistMatch#proIdExist", errMsg = "proId: #current does not exist in db ")
private Long projectId;
Copy the code
The matching method
@Service
public class ExistMatch {
@Autowired
private ProjectService projectService;
/** * appId exists */
public boolean proIdExist(Long proId) {
returnprojectService.exist(proId); }}Copy the code
10. The project name cannot be empty and cannot exist in the database. If it exists, reject it
The accept attribute is rejected if the previous condition matches. The default value is true
/** * Project name */
@Matcher(notBlank = "true")
@Matcher(customize = "com.isyscore.iop.panda.service.ProjectService#projectNameExist", accept = false, ErrMsg = "project with name #current already exists ")
private String proName;
Copy the code
11. Value of the corresponding field in a configuration item
Service scenario: When the value of field 1 is 1, the other field cannot be empty
/** * Processing type: 0, added; 1. editing. 2. search. 3, table display; 4, table extension */
@matcher (range = "[0, 4]", errMsg ="
private Integer handleType;
/** * Disable table field */ in edit mode
@Matcher(condition = "(#current == null && #root.handleType ! = 1) || (#current ! = null && ! #current. IsEmpty () && #root.handleType == 1)", errMsg = "cantEditColumnList needs to have a value when handleType is 1 ")
private List<String> cantEditColumnList;
Copy the code
12. Time must be in the past
/** * App release time */
@Matcher(range = "past")
@apiModelProperty (value = "Application release time ")
private Date createTime;
Copy the code
13. Paging data must be >0
@Matcher(range = "[0, )", errMsg = "Paging data is not satisfied")
private Integer pageNo;
@Matcher(range = "[0.)", errMsg = "PageSize data does not meet the requirement")
private Integer pageSize;
Copy the code
14. [Complex Scenario] The application ID is processed differently in different scenarios
- When starting a build, the state must be in the “Start” phase
- In the test completion action, the state must be in the ‘Test’ phase
- To initiate the release action, the state must be in the Test Completed phase
- Before stopping the action, the state must precede the Deploy state
- Exit the application before it is deployed
In addition, the most basic is that the application ID cannot be empty and must exist in db. The actions above are all different interfaces, but all the parameters are the same, so using group is the best way. Multiple groups can be added to the group. If the group is the same, the relationship between the two @Mather is and
@Data
public class AppIdReq {
@Matchers({ @Matcher(notNull = "true"), @Matcher(group = {MkConstant.DEFAULT_GROUP, "startBuild", "finishTest", "StartDeploy ", "stop", "quite"}, customize = "com.xxx.yyy.ExistMatch#appIdExist", errMsg =" application id: # current does not exist "), / / start the build action status verification @ the Matcher (group = "startBuild", customize = "com. XXX, yyy DeployStatusMatch# startBuild", ErrMsg = "appid: #current not in stage 'not compiled', Please exit "), / / test make the action of state verification @ the Matcher (group = "finishTest", customize = "com. XXX, yyy DeployStatusMatch# finishTest", ErrMsg = "appid: @matcher (group = "startDeploy", Customize = "com. XXX, yyy DeployStatusMatch# startDeploy", errMsg = "application id: # current stage is not 'test' "), / / stop status verification @ the Matcher (group = "stop", customize = "com. XXX, yyy DeployStatusMatch# stopDeploy", @matcher (group = "quite", Customize = "com. XXX, yyy DeployStatusMatch# stopDeploy", errMsg = "exit actions need to make sure that the application # current before the deployment of state")})
@apiModelProperty (value = "appId ", example = "42342354")
private Long appId;
}
Copy the code
Corresponding interface used
@AutoCheck
@RequestMapping("${api-prefix}/deploy")
@RestController
public class DeployController {.../** * start build */
@AutoCheck("startBuild")
@PutMapping("startBuild")
public Integer startBuild(@RequestBody AppIdReq appIdReq) {
Long appId = appIdReq.getAppId();
devopsService.startBuild(appId);
return 1;
}
/** * Complete the test */
@AutoCheck("finishTest")
@PutMapping("finishTest")
public Integer finishTest(@RequestBody AppIdReq appIdReq) {
Long appId = appIdReq.getAppId();
appService.chgDeployStatus(appId, DeployStatusEnum.TEST_FINISH);
return 1;
}
/** * Start publishing */
@AutoCheck("startDeploy")
@PutMapping("startDeploy")
public Integer startDeploy(@RequestBody AppIdReq appIdReq) {
Long appId = appIdReq.getAppId();
appService.startDeploy(appId, UserInfoContext.getUserContext().getUserId());
return 1;
}
/** * Stop * Only before publishing, the stop button is visible */
@AutoCheck("stop")
@PutMapping("stop")
public Integer stop(@RequestBody AppIdReq appIdReq) {
appService.stopApp(appIdReq.getAppId());
return 1;
}
/** * Exit * You cannot exit if you have already published, and the exit button can exit only after you have stopped */
@AutoCheck("quite")
@PutMapping("quite")
public Integer quite(@RequestBody AppIdReq appIdReq) {
appService.quite(appIdReq.getAppId());
return 1; }}Copy the code
15. Check the parameters
Version > = v1.6.3
The @matcher and @matchers annotations modify the input parameters of functions in addition to the Field type as shown in the following example
@Slf4j
@AutoCheck
@RequestMapping("/api/test/mikilin")
@RestController
public class TestController {
@PostMapping("fun1")
public String fun1(
@Matcher(value = {"song", "zhou"}) String name,
@Matcher(range = "[0, 3]") Integer age
) {
return name + "-"+ age; }}Copy the code
Detailed introduction
For details, see the Mikilin documentation