目录

一、为什么使用 Validation 来验证参数 

二、如何使用Validation相关注解进行参数校验

三、Validation类的相关注解及描述

四、使用 Validation API 进行参数效验步骤

五、 Spring Validation的三种校验方式

五、springboot项目中实战演练

补充:使用自定义参数注解


一、为什么使用 Validation 来验证参数 

  通常我们在使用spring框架编写接口时,对于部分接口的参数我们要进行判空或者格式校验来避免程序出现异常。那是我们一般都是使用if-else逐个对参数进行校验。这种方法按逻辑来说也是没有问题的,同样也能实现预期效果。但是,这样的代码从可读性以及美观程序来看,是非常糟糕的。那么,我们就可以使用@valid注解来帮助我们优雅的校验参数。

二、如何使用Validation相关注解进行参数校验

  ①为实体类中的参数或者对象添加相应的注解;②在控制器层进行注解声明,或者手动调用校验方法进行校验;③对异常进行处理;

三、Validation类的相关注解及描述

验证注解 验证的数据类型 说明
@AssertFalse Boolean,boolean 验证注解的元素值是false
@AssertTrue Boolean,boolean 验证注解的元素值是true
@NotNull 任意类型 验证注解的元素值不是null
@Null 任意类型 验证注解的元素值是null
@Min(value=值) BigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存储的是数字)子类型 验证注解的元素值大于等于@Min指定的value值
@Max(value=值) 和@Min要求一样 验证注解的元素值小于等于@Max指定的value值
@DecimalMin(value=值) 和@Min要求一样 验证注解的元素值大于等于@ DecimalMin指定的value值
@DecimalMax(value=值) 和@Min要求一样 验证注解的元素值小于等于@ DecimalMax指定的value值
@Digits(integer=整数位数, fraction=小数位数) 和@Min要求一样 验证注解的元素值的整数位数和小数位数上限
@Size(min=下限, max=上限) 字符串、Collection、Map、数组等 验证注解的元素值的在min和max(包含)指定区间之内,如字符长度、集合大小
@Past java.util.Date,java.util.Calendar;Joda Time类库的日期类型 验证注解的元素值(日期类型)比当前时间早
@Future 与@Past要求一样 验证注解的元素值(日期类型)比当前时间晚
@NotBlank CharSequence子类型 验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的首位空格
@Length(min=下限, max=上限) CharSequence子类型 验证注解的元素值长度在min和max区间内
@NotEmpty CharSequence子类型、Collection、Map、数组 验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@Range(min=最小值, max=最大值) BigDecimal,BigInteger,CharSequence, byte, short, int, long等原子类型和包装类型 验证注解的元素值在最小值和最大值之间
@Email(regexp=正则表达式,flag=标志的模式) CharSequence子类型(如String) 验证注解的元素值是Email,也可以通过regexp和flag指定自定义的email格式
@Pattern(regexp=正则表达式,flag=标志的模式) String,任何CharSequence的子类型 验证注解的元素值与指定的正则表达式匹配
@Valid 任何非原子类型 指定递归验证关联的对象如用户对象中有个地址对象属性,如果想在验证用户对象时一起验证地址对象的话,在地址对象上加@Valid注解即可级联验证

此处只列出Validator提供的大部分验证约束注解,请参考hibernate validator官方文档了解其他验证约束注解和进行自定义的验证约束注解定义。

四、使用 Validation API 进行参数效验步骤

  整个过程如下图所示,用户访问接口,然后进行参数效验。 对于GET请求的参数可以使用@validated注解配合上面相应的注解进行校验或者按照原先if-else方式进行效验。而对于POST请求,大部分是以表单数据即以实体对象为参数,可以使用@Valid注解方式进行效验。如果效验通过,则进入业务逻辑,否则抛出异常,交由全局异常处理器进行处理。

五、 Spring Validation的三种校验方式

第一种:在Controller方法参数前加@Valid注解——校验不通过时直接抛异常,get请求直接在平面参数前添加相应的校验规则注解,使用这种的话一般结合统一异常处理进行处理;

第二种:在Controller方法参数前加@Valid注解,参数后面定义一个BindingResult类型参数——执行时会将校验结果放进bindingResult里面,用户自行判断并处理。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

/**

  * 将校验结果放进BindingResult里面,用户自行判断并处理

  *

  * @param userInfo

  * @param bindingResult

  * @return

  */

 @PostMapping("/testBindingResult")

 public String testBindingResult(@RequestBody @Valid UserInfo userInfo, BindingResult bindingResult) {

     // 参数校验

     if (bindingResult.hasErrors()) {

         String messages = bindingResult.getAllErrors()

                 .stream()

                 .map(ObjectError::getDefaultMessage)

                 .reduce((m1, m2) -> m1 + ";" + m2)

                 .orElse("参数输入有误!");

         //这里可以抛出自定义异常,或者进行其他操作

         throw new IllegalArgumentException(messages);

     }

     return "操作成功!";

 }

这里我们是直接抛出了异常,如果没有进行全局异常处理的话,接口将会返回如下信息:

第三种:用户手动调用对应API执行校验——Validation.buildDefault ValidatorFactory().getValidator().validate(xxx)

这种方法适用于校验任意一个有valid注解的实体类,并不仅仅是只能校验接口中的参数;

这里我提取出一个工具类,如下:

MyValidationUtils.class

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

import org.springframework.util.CollectionUtils;

import javax.validation.ConstraintViolation;

import javax.validation.Valid;

import javax.validation.Validation;

import java.util.Set;

/**

 * 手动调用api方法校验对象

 */

public class MyValidationUtils {

    public static void validate(@Valid Object user) {

        Set<ConstraintViolation<@Valid Object>> validateSet = Validation.buildDefaultValidatorFactory()

                .getValidator()

                .validate(user, new Class[0]);

        if (!CollectionUtils.isEmpty(validateSet)) {

            String messages = validateSet.stream()

                    .map(ConstraintViolation::getMessage)

                    .reduce((m1, m2) -> m1 + ";" + m2)

                    .orElse("参数输入有误!");

            throw new IllegalArgumentException(messages);

        }

    }

}

五、springboot项目中实战演练

spring-boot-starter-web依赖已经集成相关jar,无需额外引入。

1.对实体类的变量进行注解标注

实体类中添加 @Valid 相关验证注解,并在注解中添加出错时的响应消息。

User.class

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

import org.hibernate.validator.constraints.Length;

import javax.validation.Valid;

import javax.validation.constraints.NotBlank;

import javax.validation.constraints.NotNull;

import javax.validation.constraints.Pattern;

@Data

public class User {

    @NotBlank(message = "姓名不能为空")

    private String username;

    @NotBlank(message = "密码不能为空")

    @Length(min = 6, max = 16, message = "密码长度为6-16位")

    private String password;

    @Pattern(regexp = "0?(13|14|15|17|18|19)[0-9]{9}", message = "手机号格式不正确")

    private String phone;

    // 嵌套必须加 @Valid,否则嵌套中的验证不生效

    @Valid

    @NotNull(message = "userinfo不能为空")

    private UserInfo userInfo;

}

如果是嵌套的实体对象,并且也要校验该对象,则需要在最外层属性上添加 @Valid 注解

UserInfo.class

1

2

3

4

5

6

7

8

9

10

11

import javax.validation.constraints.Max;

import javax.validation.constraints.NotBlank;

@Data

public class UserInfo {

    @NotBlank(message = "年龄不为空")

    @Max(value = 18, message = "不能超过18岁")

    private String age;

    @NotBlank(message = "性别不能为空")

    private String gender;

}

2.创建自定义异常

自定义异常类,方便我们处理手动抛出的异常。

1

2

3

4

5

6

7

8

9

public class ParamaErrorException extends RuntimeException {

    public ParamaErrorException() {

    }

    public ParamaErrorException(String message) {

        super(message);

    }

}

3.自定义响应枚举类

定义一个返回信息的枚举类,方便我们快速响应信息,不必每次都写返回消息和响应码。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

public enum ResultEnum {

    SUCCESS(1000"请求成功"),

    PARAMETER_ERROR(1001"请求参数有误!"),

    UNKNOWN_ERROR(9999"未知的错误!");

    private Integer code;

    private String message;

    ResultEnum(Integer code, String message) {

        this.code = code;

        this.message = message;

    }

    public Integer getCode() {

        return code;

    }

    public String getMessage() {

        return message;

    }

}

4.自定义响应对象类

创建用于返回调用方的响应信息的实体类。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

import com.sue.demo.enums.ResultEnum;

import lombok.Data;

@Data

public class ResponseResult {

    private Integer code;

    private String msg;

    public ResponseResult() {

    }

    public ResponseResult(ResultEnum resultEnum) {

        this.code = resultEnum.getCode();

        this.msg = resultEnum.getMessage();

    }

    public ResponseResult(Integer code, String msg) {

        this.code = code;

        this.msg = msg;

    }

}

5.添加全局异常处理

全局异常用于处理校验不通过时抛出的异常,并通过接口返回,同时对其他未知异常进行处理。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

import com.sue.demo.controller.ResponseResult;

import com.sue.demo.enums.ResultEnum;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.http.HttpStatus;

import org.springframework.http.converter.HttpMessageNotReadableException;

import org.springframework.util.StringUtils;

import org.springframework.validation.BindingResult;

import org.springframework.validation.FieldError;

import org.springframework.validation.ObjectError;

import org.springframework.web.bind.MethodArgumentNotValidException;

import org.springframework.web.bind.MissingServletRequestParameterException;

import org.springframework.web.bind.annotation.ExceptionHandler;

import org.springframework.web.bind.annotation.ResponseStatus;

import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.List;

@RestControllerAdvice("com.sue.demo.controller")

public class GlobalExceptionHandler {

    private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**

     * 忽略参数异常处理器

     *

     * @param e 忽略参数异常

     * @return ResponseResult

     */

    @ResponseStatus(HttpStatus.BAD_REQUEST)

    @ExceptionHandler(MissingServletRequestParameterException.class)

    public ResponseResult parameterMissingExceptionHandler(MissingServletRequestParameterException e) {

        logger.error("", e);

        return new ResponseResult(ResultEnum.PARAMETER_ERROR.getCode(), "请求参数 " + e.getParameterName() + " 不能为空");

    }

    /**

     * 缺少请求体异常处理器

     *

     * @param e 缺少请求体异常

     * @return ResponseResult

     */

    @ResponseStatus(HttpStatus.BAD_REQUEST)

    @ExceptionHandler(HttpMessageNotReadableException.class)

    public ResponseResult parameterBodyMissingExceptionHandler(HttpMessageNotReadableException e) {

        logger.error("", e);

        return new ResponseResult(ResultEnum.PARAMETER_ERROR.getCode(), "参数体不能为空");

    }

    /**

     * 参数效验异常处理器

     *

     * @param e 参数验证异常

     * @return ResponseInfo

     */

    @ResponseStatus(HttpStatus.BAD_REQUEST)

    @ExceptionHandler(MethodArgumentNotValidException.class)

    public ResponseResult parameterExceptionHandler(MethodArgumentNotValidException e) {

        logger.error("", e);

        // 获取异常信息

        BindingResult exceptions = e.getBindingResult();

        // 判断异常中是否有错误信息,如果存在就使用异常中的消息,否则使用默认消息

        if (exceptions.hasErrors()) {

            List<ObjectError> errors = exceptions.getAllErrors();

            if (!errors.isEmpty()) {

                // 这里列出了全部错误参数,按正常逻辑,只需要第一条错误即可

                FieldError fieldError = (FieldError) errors.get(0);

                return new ResponseResult(ResultEnum.PARAMETER_ERROR.getCode(), fieldError.getDefaultMessage());

            }

        }

        return new ResponseResult(ResultEnum.PARAMETER_ERROR);

    }

    /**

     * 自定义参数错误异常处理器

     *

     * @param e 自定义参数

     * @return ResponseInfo

     */

    @ResponseStatus(HttpStatus.BAD_REQUEST)

    @ExceptionHandler({ParamaErrorException.class})

    public ResponseResult paramExceptionHandler(ParamaErrorException e) {

        logger.error("", e);

        // 判断异常中是否有错误信息,如果存在就使用异常中的消息,否则使用默认消息

        if (!StringUtils.isEmpty(e.getMessage())) {

            return new ResponseResult(ResultEnum.PARAMETER_ERROR.getCode(), e.getMessage());

        }

        return new ResponseResult(ResultEnum.PARAMETER_ERROR);

    }

    /**

     * 其他异常

     *

     * @param e

     * @return

     */

    @ResponseStatus(HttpStatus.BAD_REQUEST)

    @ExceptionHandler({Exception.class})

    public ResponseResult otherExceptionHandler(Exception e) {

        logger.error("其他异常", e);

        // 判断异常中是否有错误信息,如果存在就使用异常中的消息,否则使用默认消息

        if (!StringUtils.isEmpty(e.getMessage())) {

            return new ResponseResult(ResultEnum.UNKNOWN_ERROR.getCode(), e.getMessage());

        }

        return new ResponseResult(ResultEnum.UNKNOWN_ERROR);

    }

}

5.接口类中添加相关注解

处理get请求直接在参数前添加验证注解,处理post请求时在对象前添加@Valid注解

TestController.class

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

import com.sue.demo.entity.User;

import com.sue.demo.entity.UserInfo;

import com.sue.demo.enums.ResultEnum;

import com.sue.demo.exception.ParamaErrorException;

import com.sue.demo.util.MyValidationUtils;

import com.sue.demo.util.ResponseResult;

import io.swagger.annotations.Api;

import io.swagger.annotations.ApiOperation;

import org.hibernate.validator.constraints.Length;

import org.springframework.validation.BindingResult;

import org.springframework.validation.ObjectError;

import org.springframework.validation.annotation.Validated;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RequestBody;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

import java.util.List;

@Validated

@RestController

@Api(value = "测试使用validation验证参数")

public class TestController {

    /**

     * 测试get方法,手动if进行判空,校验失败时手动抛出自定义异常

     *

     * @param username 姓名

     * @return ResponseResult

     */

    @ApiOperation(value = "测试get方法", notes = "输入用户名")

    @GetMapping("/testGet")

    public ResponseResult testGet(String username) {

        if (username == null || "".equals(username)) {

            throw new ParamaErrorException("username 不能为空");

        }

        return new ResponseResult(ResultEnum.SUCCESS);

    }

    /**

     * 使用注解校验get请求平面参数,需要在Controller类头部添加@Validated注解,否则不能成功校验,这种方法不用手动抛出异常

     *

     * @param username

     * @return

     */

    @ApiOperation(value = "测试get方法", notes = "输入用户名")

    @GetMapping("/testGetByValidated")

    public ResponseResult testGetByValidated(@Length(max = 4@RequestParam("username") String username) {

        return new ResponseResult(ResultEnum.SUCCESS);

    }

    /**

     * post方法传入单个对象进行校验,在参数前添加@Valid注解,校验失败时会抛出异常并使用全局异常进行处理

     *

     * @param userInfo 用户信息

     * @return ResponseResult

     */

    @ApiOperation(value = "post方法传入单个对象", notes = "传入json对象")

    @PostMapping("/testUserInfo")

    public ResponseResult testUserInfo(@Valid @RequestBody UserInfo userInfo) {

        return new ResponseResult(ResultEnum.SUCCESS);

    }

    /**

     * post方法传入对象,手动校验,此时参数前没有添加@Valid注解,所以不会自动进行校验,手动调用validate方法进行校验,失败时会抛出异常

     *

     * @param userInfo

     * @return ResponseResult

     */

    @ApiOperation(value = "post方法传入对象,手动测试", notes = "单个对象")

    @PostMapping("/checkByMethod")

    public ResponseResult checkByMethod(@RequestBody UserInfo userInfo) {

        //调用api校验

        MyValidationUtils.validate(userInfo);

        return new ResponseResult(ResultEnum.SUCCESS);

    }

    /**

     * post方法传入多个对象,当使用@Valid校验对象集合时,要在控制层添加@Validated注解,否则不会对集合中的每个对象进行校验

     *

     * @param userInfo

     * @return ResponseResult

     */

    @ApiOperation(value = "post方法传入多个对象", notes = "多个对象")

    @PostMapping("/testUserList")

    public ResponseResult testUserList(@Valid @RequestBody List<UserInfo> userInfo) {

        return new ResponseResult(ResultEnum.SUCCESS);

    }

    /**

     * 测试对象中嵌套对象的情况,此时也要在对象属性上添加@Valid注解

     *

     * @param user

     * @return

     */

    @ApiOperation(value = "测试对象中嵌套对象的情况")

    @PostMapping("/checkUser")

    public ResponseResult checkUser(@Valid @RequestBody User user) {

        return new ResponseResult(ResultEnum.SUCCESS);

    }

    /**

     * 将校验结果放进BindingResult里面,用户自行判断并处理

     *

     * @param userInfo

     * @param bindingResult

     * @return

     */

    @PostMapping("/testBindingResult")

    public String testBindingResult(@RequestBody @Valid UserInfo userInfo, BindingResult bindingResult) {

        // 参数校验

        if (bindingResult.hasErrors()) {

            String messages = bindingResult.getAllErrors()

                    .stream()

                    .map(ObjectError::getDefaultMessage)

                    .reduce((m1, m2) -> m1 + ";" + m2)

                    .orElse("参数输入有误!");

            //这里可以抛出自定义异常,或者进行其他操作

            throw new IllegalArgumentException(messages);

        }

        return "操作成功!";

    }

}

6.进行测试


补充:使用自定义参数注解

1.我们这里创建一个身份证校验注解

1

2

3

4

5

6

7

8

9

10

11

12

@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 {};

}

这个注解是作用在Field字段上,运行时生效,触发的是IdentityCardNumber这个验证类。

  • message 定制化的提示信息,主要是从ValidationMessages.properties里提取,也可以依据实际情况进行定制
  • groups 这里主要进行将validator进行分类,不同的类group中会执行不同的validator操作
  • payload 主要是针对bean的,使用不多。

2.自定义Validator

1

2

3

4

5

6

7

8

9

10

11

12

13

14

import javax.validation.ConstraintValidator;

import javax.validation.ConstraintValidatorContext;

public class IdentityCardNumberValidator implements ConstraintValidator<IdentityCardNumber, Object> {

    @Override

    public void initialize(IdentityCardNumber identityCardNumber) {

    }

    @Override

    public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {

        return IdCardValidatorUtils.isValidate18Idcard(o.toString());

    }

}

校验工具类IdCardValidatorUtils.class

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

import java.text.ParseException;

import java.text.SimpleDateFormat;

import java.util.Calendar;

import java.util.Date;

import java.util.regex.Pattern;

/**

 * 根据〖中华人民共和国国家标准GB11643-1999〗中有关公民身份号码的规定,

 * 公民身份号码是特征组合码,由十七位数字本体码和一位数字校验码组成。

 * 排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。

 * 顺序码: 表示在同一地址码所标识的区域范围内,对同年、同月、同 日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配 给女性。

 * 1.前1、2位数字表示:所在省份的代码;

 * 2.第3、4位数字表示:所在城市的代码;

 * 3.第5、6位数字表示:所在区县的代码;

 * 4.第7~14位数字表示:出生年、月、日;

 * 5.第15、16位数字表示:所在地的派出所的代码;

 * 6.第17位数字表示性别:奇数表示男性,偶数表示女性;

 * 7.第18位数字是校检码:也有的说是个人信息码,一般是随计算机的随机产生,用来检验身份证的正确性。

 * 校检码可以是0~9的数字,有时也用x表示。

 * <p>

 * 2、第十八位数字(校验码)的计算方法

 * 1.将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分别为:7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2

 * 2.将这17位数字和系数相乘的结果相加。

 * 3.用加出来和除以11,看余数是多少?

 * 4.余数只可能有0 1 2 3 4 5 6 7 8 9 10这11个数字。其分别对应的最后一位身份证的号码为1 0 X 9 8 7 6 5 4 3 2。

 * 5.通过上面得知如果余数是2,就会在身份证的第18位数字上出现罗马数字的Ⅹ。如果余数是10,身份证的最后一位号码就是2。

 */

public class IdCardValidatorUtils {

    /**

     * 省,直辖市代码表: { 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:"国外"}

     */

    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[] = {7910584216379105842};

    // 第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(612);

            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(06) + 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(017);

        // 获取第18位

        String idcard18Code = idcard.substring(1718);

        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;

    }

}

3. 使用自定义的注解

1

2

3

@NotBlank(message = "身份证号不能为空")

@IdentityCardNumber(message = "身份证信息有误,请核对后提交")

private String clientCardNo;

springboot使用Validation API和全局异常优雅的校验方法参数相关推荐

  1. springboot系列六、springboot配置错误页面及全局异常

    springboot系列六.springboot配置错误页面及全局异常 参考文章: (1)springboot系列六.springboot配置错误页面及全局异常 (2)https://www.cnbl ...

  2. express捕获全局异常的三种方法

    express捕获全局异常的三种方法 参考文章: (1)express捕获全局异常的三种方法 (2)https://www.cnblogs.com/chenqionghe/p/11349521.htm ...

  3. 如何优雅的处理异常?SpringBoot全局异常与数据校验

    要思考的问题 在现在的前后端交互中,通常都规范了接口返回方式,如返回的接口状态(成功|失败)以及要返回的数据在那个字段取,或者说失败了以后提示信息从接口哪里返回,因此,如果想做全局异常,并且异常发生后 ...

  4. springboot 监听所有异常_SpringBoot 全局异常配置及返回自定义页面

    1. 全局异常的配置 1. 创建异常处理类 package com.test.spring_boot_demo.domain; import org.slf4j.Logger; import org. ...

  5. bean validation校验方法参数_Spring Boot 之使用 validation 验证参数

    前言 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/bin392328206/six-finger种一棵树最好的时间是十年前,其次是现在 我知道很多人不玩qq了 ...

  6. bean validation校验方法参数_SpringBoot参数校验 从入门到精通 解决繁琐的参数验证工作...

    ● 手把手教你实现 SpringBoot与Vue整合开发 前后端分离 简单例子 详解●SQL优化经历  SQL执行效率提高了1000w倍●Java面试题 详解 由易到难● SQL语句大全详解 增删改查 ...

  7. bean validation校验方法参数_Spring Validation最佳实践及其实现原理,参数校验没那么简单!

    本文同名博客老炮说Java:https://www.laopaojava.com/,每天更新Spring/SpringMvc/SpringBoot/实战项目等文章资料 顺便再给大家推荐一套Spring ...

  8. bean validation校验方法参数_项目启动时首先校验Spring Boot配置参数

    1. 概述 在项目实际开发过程中,为了更好的复用,我们参考Spring Boot Starters,封装了许多企业内部中间件的starter.这些中间件的接入都需要申请并在项目中配置一些特定的参数.我 ...

  9. bean validation校验方法参数_Springboot 使用校验框架validation校验

    作者:双斜杠少年 blog.csdn.net/u012373815/article/details/72049796 b/s系统中对http请求数据的校验多数在客户端进行,这也是出于简单及用户体验性上 ...

最新文章

  1. 大型网站架构不得不考虑的10个问题,互联网营销
  2. Eclipse VIM
  3. zookeeper配置中心
  4. ad09只在一定范围内查找相似对象_dxp查找相似对象
  5. win32api window2con 模块 -系统注册表操作
  6. 百度地图POI数据爬取,突破百度地图API爬取数目“400条“的限制11。
  7. python moviepy 音量_python moviepy 视频剪辑代码
  8. 304902阿里巴巴Java开发手册1.4.0
  9. php读取西门子plc_第三方设备如何读取PLC数据
  10. 录音文件下载_录音啦(文字语音转换)软件安装教程
  11. Intellij idea 最新版永久破解
  12. EXCEL 根据超链接直接显示图片
  13. 北大编程网格【练习题】,北医计算概论课程编程题答案整理,C语言/python/C++
  14. linux中sed如何替换换行符,linux sed命令,如何替换换行符“\n”
  15. W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0x419b4c50)
  16. android查ip地址,安卓手机查看IP地址的两种方法,  二、进入手机状态
  17. linux发行版本排行,2020年10种最受欢迎的Linux发行版排名
  18. Android Pixel手机Notification小图标显示白方块问题
  19. SparkRDD——行动算子
  20. React的React.FC与React.Component的初步认识

热门文章

  1. 强化学习系列 - 刘建平Pinard
  2. vba 判断文本框内容是否为空_VBA代码解决方案第119讲:如何遍历工作表中的图形...
  3. Android应用在应用宝上线
  4. flash-热风焊盘的制作
  5. 苹果8p电池多少毫安的_给苹果8p用12w的充电器充电会怎么样?会不会对电池不好?-苹果...
  6. 如何短期通过PMP项目管理考试?
  7. 天载网上炒股数字经济超60万亿市场空间待启
  8. 快速存拷贝 fast_memcpy
  9. 为什么你的手机能上网打电话?
  10. 本地计算机上的MySQL服务启动后停止。某些服务在未由其他服务或程序使用时将自动停止