Как освободиться от проверки параметра if-else?

Spring Boot

задний план

При разработке часто бывает необходимо написать некоторые коды проверки полей, такие как непустое, ограничение длины, проверка формата почтового ящика и т. д., что приводит к заполнениюif-elseКод не только довольно многословен, но и сводит с ума.

hibernate validator(Официальные документы) обеспечивает относительно полную и удобную реализацию проверки. Он определяет многие часто используемые аннотации проверки, мы можем напрямую добавить эти аннотации в нашJavaBeanНа свойства выше, вы можете проверить, когда вам нужно проверить. существуетSpring BootFiery теперь, инструмент был включен в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. Для получения дополнительных комментариев, пожалуйста, попробуйте сами;
  2. Результаты теста доказывают, что проверка параметра действительна, и аномальная информация возвращается в соответствии с набором результатов, который мы установили.

1.5 Общие аннотации проверки

  1. @Null: Аннотированный элемент должен бытьnull
  2. @NotNull: аннотируемый элемент не должен бытьnull
  3. @AssertTrue: Аннотированный элемент должен бытьtrue
  4. @AssertFalse: Аннотированный элемент должен бытьfalse
  5. @Min(value): аннотируемый элемент должен быть числом, значение которого должно быть больше или равно указанному минимальному значению.
  6. @Max(value): Аннотируемый элемент должен быть числом, значение которого должно быть меньше или равно указанному максимальному значению.
  7. @DecimalMin(value): аннотируемый элемент должен быть числом, значение которого должно быть больше или равно указанному минимальному значению.
  8. @DecimalMax(value): Аннотируемый элемент должен быть числом, значение которого должно быть меньше или равно указанному максимальному значению.
  9. @Size(max=, min=): Размер аннотируемого элемента должен быть в пределах указанного диапазона.
  10. @Digits (integer, fraction): аннотируемый элемент должен быть числом, и его значение должно быть в допустимом диапазоне.
  11. @Past: аннотируемый элемент должен быть датой в прошлом
    @Future: аннотированный элемент должен быть датой в будущем
  12. @Pattern(regex=,flag=): Аннотированный элемент должен соответствовать указанному регулярному выражению
  13. @NotBlank(message =): Убедитесь, что строка неnull, а длина должна быть больше 0
  14. @Email: аннотированный элемент должен быть адресом электронной почты.
  15. @Length(min=,max=): размер аннотированной строки должен быть в пределах указанного диапазона.
  16. @NotEmpty: Аннотированная строка должна быть непустой.
  17. @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 Технический обмен

  1. Пыль Блог
  2. Пыль Блог - Блог Парк
  3. Пыль Блог — CSDN

Обратите внимание на публичный счет, узнайте больше:

风尘博客

4.2 Адрес источника

Пример кода на гитхабе