задний план
При разработке часто бывает необходимо написать некоторые коды проверки полей, такие как непустое, ограничение длины, проверка формата почтового ящика и т. д., что приводит к заполнению
if-else
Код не только довольно многословен, но и сводит с ума.
hibernate validator
(Официальные документы) обеспечивает относительно полную и удобную реализацию проверки. Он определяет многие часто используемые аннотации проверки, мы можем напрямую добавить эти аннотации в нашJavaBean
На свойства выше, вы можете проверить, когда вам нужно проверить. существуетSpring Boot
Fiery теперь, инструмент был включен вspring-boot-starter-web
, никаких дополнительных пакетов вводить не нужно.
1. Быстрый старт
1.1UserDTO
Объявить параметры для проверки
Инструкции по проверке см. в комментариях в коде.
@Data
public class UserDTO {
/**
性别(不校验)
*/
private String sex;
/**
用户名(校验:不能为空,不能超过20个字符串)
*/
@NotBlank(message = "用户名不能为空")
@Length(max = 20, message = "用户名不能超过20个字符")
private String userName;
/**
* 手机号(校验:不能为空且按照正则校验格式)
*/
@NotBlank(message = "手机号不能为空")
@Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误")
private String mobile;
/**
邮箱(校验:不能唯恐且校验邮箱格式)
*/
@NotBlank(message = "联系邮箱不能为空")
@Email(message = "邮箱格式不对")
private String email;
}
1.2 Объявить параметры для проверки на интерфейсе
нуждаться в
Controller
Используется положение входного параметра слоя@Validated
Выявление аннотации
@RestController
@RequestMapping("/demo")
public class ValidatorDemoController {
/**
* 注解参数校验案例
* @param userDTO
* @return
*/
@PostMapping("/test")
public HttpResult test(@Validated UserDTO userDTO) {
return HttpResult.success(userDTO);
}
}
здесь
HttpResult
Это результирующий набор, инкапсулированный самим Ваном.Подробности см. в исходном коде по адресу Github в конце статьи.
1.3 Глобальный захват веб-исключений
@Valid
существуетSpring Boot
При выполнении проверки параметра привязки вSpring Boot
в процессе.
@RestControllerAdvice
@Slf4j
public class WebExceptionHandler {
/**
* 方法参数校验
* @param e
* @return
*/
@ExceptionHandler(BindException.class)
public HttpResult handleMethodArgumentNotValidException(BindException e) {
log.error(e.getMessage(), e);
return HttpResult.failure(400,e.getBindingResult().getFieldError().getDefaultMessage());
}
@ExceptionHandler(Exception.class)
public HttpResult handleException(Exception e) {
log.error(e.getMessage(), e);
return HttpResult.failure(400, "系统繁忙,请稍后再试");
}
}
1.4 Тестирование
Тестовый инструмент используется
postman
- Метод запроса: POST
- Адрес запроса: localhost:8080/demo/test
- Параметры запроса:
userName:Van
mobile:17098705205
email:123
- Возвращаемый результат:
{
"success": false,
"code": 400,
"data": null,
"message": "邮箱格式不对"
}
- инструкция
- Для получения дополнительных комментариев, пожалуйста, попробуйте сами;
- Результаты теста доказывают, что проверка параметра действительна, и аномальная информация возвращается в соответствии с набором результатов, который мы установили.
1.5 Общие аннотации проверки
-
@Null
: Аннотированный элемент должен бытьnull
-
@NotNull
: аннотируемый элемент не должен бытьnull
-
@AssertTrue
: Аннотированный элемент должен бытьtrue
-
@AssertFalse
: Аннотированный элемент должен бытьfalse
-
@Min(value)
: аннотируемый элемент должен быть числом, значение которого должно быть больше или равно указанному минимальному значению. -
@Max(value)
: Аннотируемый элемент должен быть числом, значение которого должно быть меньше или равно указанному максимальному значению. -
@DecimalMin(value)
: аннотируемый элемент должен быть числом, значение которого должно быть больше или равно указанному минимальному значению. -
@DecimalMax(value)
: Аннотируемый элемент должен быть числом, значение которого должно быть меньше или равно указанному максимальному значению. -
@Size(max=, min=)
: Размер аннотируемого элемента должен быть в пределах указанного диапазона. -
@Digits (integer, fraction)
: аннотируемый элемент должен быть числом, и его значение должно быть в допустимом диапазоне. -
@Past
: аннотируемый элемент должен быть датой в прошлом
@Future
: аннотированный элемент должен быть датой в будущем -
@Pattern(regex=,flag=)
: Аннотированный элемент должен соответствовать указанному регулярному выражению -
@NotBlank(message =)
: Убедитесь, что строка неnull
, а длина должна быть больше 0 -
@Email
: аннотированный элемент должен быть адресом электронной почты. -
@Length(min=,max=)
: размер аннотированной строки должен быть в пределах указанного диапазона. -
@NotEmpty
: Аннотированная строка должна быть непустой. -
@Range(min=,max=,message=)
: Аннотируемый элемент должен находиться в соответствующем диапазоне.
2. Проверка пользовательских аннотаций
hibernate validator
Встроенные аннотации могут выполнять простую проверку параметров, а при регулярном написании они могут решить большинство ситуаций проверки параметров. Однако в некоторых случаях, таких как: проверка входа в систему, нам необходимо настроить проверку аннотаций. Для удобства тестирования я беру проверку удостоверения личности в качестве примера, чтобы завершить процесс пользовательской проверки.
2.1 Средства проверки личности
public class IdCardValidatorUtils {
protected String codeAndCity[][] = {{"11", "北京"}, {"12", "天津"},
{"13", "河北"}, {"14", "山西"}, {"15", "内蒙古"}, {"21", "辽宁"},
{"22", "吉林"}, {"23", "黑龙江"}, {"31", "上海"}, {"32", "江苏"},
{"33", "浙江"}, {"34", "安徽"}, {"35", "福建"}, {"36", "江西"},
{"37", "山东"}, {"41", "河南"}, {"42", "湖北"}, {"43", "湖南"},
{"44", "广东"}, {"45", "广西"}, {"46", "海南"}, {"50", "重庆"},
{"51", "四川"}, {"52", "贵州"}, {"53", "云南"}, {"54", "西藏"},
{"61", "陕西"}, {"62", "甘肃"}, {"63", "青海"}, {"64", "宁夏"},
{"65", "新疆"}, {"71", "台湾"}, {"81", "香港"}, {"82", "澳门"},
{"91", "国外"}};
private String cityCode[] = {"11", "12", "13", "14", "15", "21", "22",
"23", "31", "32", "33", "34", "35", "36", "37", "41", "42", "43",
"44", "45", "46", "50", "51", "52", "53", "54", "61", "62", "63",
"64", "65", "71", "81", "82", "91"};
// 每位加权因子
private static int power[] = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
// 第18位校检码
private String verifyCode[] = {"1", "0", "X", "9", "8", "7", "6", "5",
"4", "3", "2"};
/**
* 验证所有的身份证的合法性
*
* @param idcard
* @return
*/
public static boolean isValidatedAllIdcard(String idcard) {
if (idcard.length() == 15) {
idcard = convertIdcarBy15bit(idcard);
}
return isValidate18Idcard(idcard);
}
/**
* 将15位的身份证转成18位身份证
*
* @param idcard
* @return
*/
public static String convertIdcarBy15bit(String idcard) {
String idcard17 = null;
// 非15位身份证
if (idcard.length() != 15) {
return null;
}
if (isDigital(idcard)) {
// 获取出生年月日
String birthday = idcard.substring(6, 12);
Date birthdate = null;
try {
birthdate = new SimpleDateFormat("yyMMdd").parse(birthday);
} catch (ParseException e) {
e.printStackTrace();
}
Calendar cday = Calendar.getInstance();
cday.setTime(birthdate);
String year = String.valueOf(cday.get(Calendar.YEAR));
idcard17 = idcard.substring(0, 6) + year + idcard.substring(8);
char c[] = idcard17.toCharArray();
String checkCode = "";
if (null != c) {
int bit[] = new int[idcard17.length()];
// 将字符数组转为整型数组
bit = converCharToInt(c);
int sum17 = 0;
sum17 = getPowerSum(bit);
// 获取和值与11取模得到余数进行校验码
checkCode = getCheckCodeBySum(sum17);
// 获取不到校验位
if (null == checkCode) {
return null;
}
// 将前17位与第18位校验码拼接
idcard17 += checkCode;
}
} else { // 身份证包含数字
return null;
}
return idcard17;
}
/**
* @param idCard
* @return
*/
public static boolean isValidate18Idcard(String idCard) {
// 非18位为假
if (idCard.length() != 18) {
return false;
}
// 获取前17位
String idcard17 = idCard.substring(0, 17);
// 获取第18位
String idcard18Code = idCard.substring(17, 18);
char c[] = null;
String checkCode = "";
// 是否都为数字
if (isDigital(idcard17)) {
c = idcard17.toCharArray();
} else {
return false;
}
if (null != c) {
int bit[] = new int[idcard17.length()];
bit = converCharToInt(c);
int sum17 = 0;
sum17 = getPowerSum(bit);
// 将和值与11取模得到余数进行校验码判断
checkCode = getCheckCodeBySum(sum17);
if (null == checkCode) {
return false;
}
// 将身份证的第18位与算出来的校码进行匹配,不相等就为假
if (!idcard18Code.equalsIgnoreCase(checkCode)) {
return false;
}
}
return true;
}
/**
* 18位身份证号码的基本数字和位数验校
*
* @param idCard
* @return
*/
public boolean is18Idcard(String idCard) {
return Pattern.matches("^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([\\d|x|X]{1})$", idCard);
}
/**
* 数字验证
*
* @param str
* @return
*/
public static boolean isDigital(String str) {
return str == null || "".equals(str) ? false : str.matches("^[0-9]*$");
}
/**
* 将身份证的每位和对应位的加权因子相乘之后,再得到和值
*
* @param bit
* @return
*/
public static int getPowerSum(int[] bit) {
int sum = 0;
if (power.length != bit.length) {
return sum;
}
for (int i = 0; i < bit.length; i++) {
for (int j = 0; j < power.length; j++) {
if (i == j) {
sum = sum + bit[i] * power[j];
}
}
}
return sum;
}
/**
* 将和值与11取模得到余数进行校验码判断
*
* @param sum17
* @return 校验位
*/
public static String getCheckCodeBySum(int sum17) {
String checkCode = null;
switch (sum17 % 11) {
case 10:
checkCode = "2";
break;
case 9:
checkCode = "3";
break;
case 8:
checkCode = "4";
break;
case 7:
checkCode = "5";
break;
case 6:
checkCode = "6";
break;
case 5:
checkCode = "7";
break;
case 4:
checkCode = "8";
break;
case 3:
checkCode = "9";
break;
case 2:
checkCode = "x";
break;
case 1:
checkCode = "0";
break;
case 0:
checkCode = "1";
break;
}
return checkCode;
}
/**
* 将字符数组转为整型数组
*
* @param c
* @return
* @throws NumberFormatException
*/
public static int[] converCharToInt(char[] c) throws NumberFormatException {
int[] a = new int[c.length];
int k = 0;
for (char temp : c) {
a[k++] = Integer.parseInt(String.valueOf(temp));
}
return a;
}
public static void main(String[] args) {
String idCardForFalse = "350583199108290106";
String idCardForTrue = "350583197106150219";
if (IdCardValidatorUtils.isValidatedAllIdcard(idCardForTrue)) {
System.out.println("身份证校验正确");
} else {
System.out.println("身份证校验错误!");
}
}
}
2.2 Пользовательские аннотации
@Documented
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IdentityCardNumberValidator.class)
public @interface IdentityCardNumber {
String message() default "身份证号码格式不正确";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Осторожно, вы обнаружите, что по сравнению с обычными пользовательскими аннотациями эта аннотация:
@Constraint(validatedBy = IdentityCardNumberValidator.class)
, функция аннотации — вызвать средство проверки идентификатора.
2,3 дюймаUserDTO
В поле необходимо проверить добавленную выписку
/**
* 身份证号(校验:自定义注解校验)
*/
@IdentityCardNumber
private String idNumber;
2.4 Интерфейс плоскости управления
@RestController
@RequestMapping("/custom")
public class ValidatorCustomController {
/**
* 自定义注解参数校验案例
* @param userDTO
* @return
*/
@PostMapping("/test")
public HttpResult test(@Validated UserDTO userDTO) {
return HttpResult.success(userDTO);
}
}
2.5 Тестирование пользовательских аннотаций
- Метод запроса: POST
- Адрес запроса: localhost:8080/private/test
- Параметры запроса:
userName:Van
mobile:17098705205
email:110@qq.com
idNumber:350583199108290106
- Возвращаемый результат:
{
"success": false,
"code": 400,
"data": null,
"message": "身份证号码格式不正确"
}
3. Групповая проверка
Помимо вышеперечисленной проверки, могут быть такие требования:
При создании информации о пользователе проверка не требуетсяuserId
; Но при обновлении информации о пользователе нужно проверятьuserId
, а также имя пользователя, адрес электронной почты и т. д. должны быть проверены. В этом случае вы можетеПроверка пакетовРешать.
3.1 Определение пакетного интерфейса
Create.java
import javax.validation.groups.Default;
public interface Create extends Default {
}
Update.java
import javax.validation.groups.Default;
public interface Update extends Default {
}
3,2 дюймаUserDTO
Добавьте объявления в поля, которые необходимо проверить
/**
* 用户id(只有在有Update分组中校验非空)
*/
@NotNull(message = "id 不能为空", groups = Update.class)
private Long userId;
3.3 Объявление позиции входного параметра слоя управления
@RestController
@RequestMapping("/groups")
public class ValidatorGroupsController {
/**
* 更新数据,需要传入userID
* @param userDTO
* @return
*/
@PostMapping("/update")
public HttpResult updateData(@Validated(Update.class)UserDTO userDTO) {
return HttpResult.success(userDTO);
}
/**
* 新增数据,不需要传入userID
* @param userDTO
* @return
*/
@PostMapping("/create")
public HttpResult createData(@Validated(Create.class)UserDTO userDTO) {
return HttpResult.success(userDTO);
}
}
3.4 Тест групповой верификации - новый тест
- Метод запроса: POST
- Адрес запроса: localhost: 8080/groups/create
- Параметры запроса:
userName:Van
mobile:17098705205
email:110@qq.com
idNumber:350583197106150219
userId:
- Возвращаемый результат:
{
"success": true,
"code": 200,
"data": {
"userId": null,
"sex": null,
"userName": "Van",
"mobile": "17098705205",
"email": "110@qq.com",
"idNumber": "350583197106150219",
"passWord": null
},
"message": null
}
Запрос выполнен успешно, что указывает на новый запрос, без проверкиuserId
,СейчасuserId
Может быть пустым.
3.5 Тест групповой верификации - тест обновления
- Метод запроса: POST
- Адрес запроса: localhost:8080/groups/update
- Параметры запроса: те же, что и выше (3.4)
- Возвращаемый результат:
{
"success": false,
"code": 400,
"data": null,
"message": "id 不能为空"
}
запрос не выполнен, заявив запрос на обновление, проверьтеuserId
,СейчасuserId
Не может быть пустым.
Результаты теста объединили 3,4 и 3,5, что указывает на то, что контрольная сумма пакета прошла успешно.
4. Резюме
Я надеюсь, что каждая написанная вами строчка кода — это бизнес-потребность, а не скучная и бесконечная проверка параметров.
4.1 Технический обмен
Обратите внимание на публичный счет, узнайте больше: