告别 if-else 校验代码参数校验这么写才足够优雅_第1页
告别 if-else 校验代码参数校验这么写才足够优雅_第2页
告别 if-else 校验代码参数校验这么写才足够优雅_第3页
告别 if-else 校验代码参数校验这么写才足够优雅_第4页
告别 if-else 校验代码参数校验这么写才足够优雅_第5页
已阅读5页,还剩14页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

告别if-else校验代码,参数校验这么写才足够优雅很痛苦遇到大量的参数进行校验,在业务中还要抛出异常或者不断的返回异常时的校验信息,在代码中相当冗长,充满了if-else这种校验代码,今天我们就来学习spring的javax.validation注解式参数校验.为什么要用validatorjavax.validation的一系列注解可以帮我们完成参数校验,免去繁琐的串行校验不然我们的代码就像下面这样:

//

http://localhost:8080/api/user/save/serial

/**

*

走串行校验

*

*

@param

userVO

*

@return

*/

@PostMapping("/save/serial")

public

Object

save(@RequestBody

UserVO

userVO)

{

String

mobile

=

userVO.getMobile();

//手动逐个

参数校验~

写法

if

(StringUtils.isBlank(mobile))

{

return

RspDTO.paramFail("mobile:手机号码不能为空");

}

else

if

(!Pattern.matches("^[1][3,4,5,6,7,8,9][0-9]{9}$",

mobile))

{

return

RspDTO.paramFail("mobile:手机号码格式不对");

}

//抛出自定义异常等~写法

if

(StringUtils.isBlank(userVO.getUsername()))

{

throw

new

BizException(Constant.PARAM_FAIL_CODE,

"用户名不能为空");

}

//

比如写一个map返回

if

(StringUtils.isBlank(userVO.getSex()))

{

Map

result

=

new

HashMap<>(5);

result.put("code",

Constant.PARAM_FAIL_CODE);

result.put("msg",

"性别不能为空");

return

result;

}

//.........各种写法

...

userService.save(userVO);

return

RspDTO.success();

}

这被大佬看见,一定说,都9102了还这么写,然后被劝退了.....什么是javax.validationJSR303是一套JavaBean参数校验的标准,它定义了很多常用的校验注解,我们可以直接将这些注解加在我们JavaBean的属性上面(面向注解编程的时代),就可以在需要校验的时候进行校验了,在SpringBoot中已经包含在starter-web中,再其他项目中可以引用依赖,并自行调整版本:

javax.validation

validation-api

1.1.0.Final

org.hibernate

hibernate-validator

5.2.0.Final

注解说明

1.@NotNull:不能为null,但可以为empty("","

","

")

2.@NotEmpty:不能为null,而且长度必须大于0

("

","

")

3.@NotBlank:只能作用在String上,不能为null,而且调用trim()后,长度必须大于0("test")

即:必须有实际字符

验证注解验证的数据类型说明@AssertFalseBoolean,boolean验证注解的元素值是false@AssertTrueBoolean,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(包含)指定区间之内,如字符长度、集合大小@Pastjava.util.Date,java.util.Calendar;JodaTime类库的日期类型验证注解的元素值(日期类型)比当前时间早@Future与@Past要求一样验证注解的元素值(日期类型)比当前时间晚@NotBlankCharSequence子类型验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的首位空格@Length(min=下限,max=上限)CharSequence子类型验证注解的元素值长度在min和max区间内@NotEmptyCharSequence子类型、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注解即可级联验证此处只列出HibernateValidator提供的大部分验证约束注解,请参考hibernatevalidator官方文档了解其他验证约束注解和进行自定义的验证约束注解定义。实战演练话不多说,直接走实践路线,同样使用的是SpringBoot的快速框架,详细代码见:/leaJone/myb…在公众号后端架构师后台回复“架构整洁”,获取一份惊喜礼包。1.@Validated声明要检查的参数这里我们在控制器层进行注解声明

/**

*

走参数校验注解

*

*

@param

userDTO

*

@return

*/

@PostMapping("/save/valid")

public

RspDTO

save(@RequestBody

@Validated

UserDTO

userDTO)

{

userService.save(userDTO);

return

RspDTO.success();

}

2.对参数的字段进行注解标注import

lombok.Data;

import

org.hibernate.validator.constraints.Length;

import

javax.validation.constraints.*;

import

java.io.Serializable;

import

java.util.Date;

/**

*

@author

LiJing

*

@ClassName:

UserDTO

*

@Description:

用户传输对象

*

@date

2019/7/30

13:55

*/

@Data

public

class

UserDTO

implements

Serializable

{

private

static

final

long

serialVersionUID

=

1L;

/***

用户ID*/

@NotNull(message

=

"用户id不能为空")

private

Long

userId;

/**

用户名*/

@NotBlank(message

=

"用户名不能为空")

@Length(max

=

20,

message

=

"用户名不能超过20个字符")

@Pattern(regexp

=

"^[\\u4E00-\\u9FA5A-Za-z0-9\\*]*$",

message

=

"用户昵称限制:最多20字符,包含文字、字母和数字")

private

String

username;

/**

手机号*/

@NotBlank(message

=

"手机号不能为空")

@Pattern(regexp

=

"^[1][3,4,5,6,7,8,9][0-9]{9}$",

message

=

"手机号格式有误")

private

String

mobile;

/**性别*/

private

String

sex;

/**

邮箱*/

@NotBlank(message

=

"联系邮箱不能为空")

@Email(message

=

"邮箱格式不对")

private

String

email;

/**

密码*/

private

String

password;

/***

创建时间

*/

@Future(message

=

"时间必须是将来时间")

private

Date

createTime;

}

3.在全局校验中增加校验异常MethodArgumentNotValidException是springBoot中进行绑定参数校验时的异常,需要在springBoot中处理,其他需要处理ConstraintViolationException异常进行处理.为了优雅一点,我们将参数异常,业务异常,统一做了一个全局异常,将控制层的异常包装到我们自定义的异常中为了优雅一点,我们还做了一个统一的结构体,将请求的code,和msg,data一起统一封装到结构体中,增加了代码的复用性import

com.boot.lea.mybot.dto.RspDTO;

import

org.slf4j.Logger;

import

org.slf4j.LoggerFactory;

import

org.springframework.dao.DuplicateKeyException;

import

org.springframework.web.bind.MethodArgumentNotValidException;

import

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

import

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

import

org.springframework.web.servlet.NoHandlerFoundException;

import

javax.validation.ConstraintViolationException;

import

javax.validation.ValidationException;

/**

*

@author

LiJing

*

@ClassName:

GlobalExceptionHandler

*

@Description:

全局异常处理器

*

@date

2019/7/30

13:57

*/

@RestControllerAdvice

public

class

GlobalExceptionHandler

{

private

Logger

logger

=

LoggerFactory.getLogger(getClass());

private

static

int

DUPLICATE_KEY_CODE

=

1001;

private

static

int

PARAM_FAIL_CODE

=

1002;

private

static

int

VALIDATION_CODE

=

1003;

/**

*

处理自定义异常

*/

@ExceptionHandler(BizException.class)

public

RspDTO

handleRRException(BizException

e)

{

logger.error(e.getMessage(),

e);

return

new

RspDTO(e.getCode(),

e.getMessage());

}

/**

*

方法参数校验

*/

@ExceptionHandler(MethodArgumentNotValidException.class)

public

RspDTO

handleMethodArgumentNotValidException(MethodArgumentNotValidException

e)

{

logger.error(e.getMessage(),

e);

return

new

RspDTO(PARAM_FAIL_CODE,

e.getBindingResult().getFieldError().getDefaultMessage());

}

/**

*

ValidationException

*/

@ExceptionHandler(ValidationException.class)

public

RspDTO

handleValidationException(ValidationException

e)

{

logger.error(e.getMessage(),

e);

return

new

RspDTO(VALIDATION_CODE,

e.getCause().getMessage());

}

/**

*

ConstraintViolationException

*/

@ExceptionHandler(ConstraintViolationException.class)

public

RspDTO

handleConstraintViolationException(ConstraintViolationException

e)

{

logger.error(e.getMessage(),

e);

return

new

RspDTO(PARAM_FAIL_CODE,

e.getMessage());

}

@ExceptionHandler(NoHandlerFoundException.class)

public

RspDTO

handlerNoFoundException(Exception

e)

{

logger.error(e.getMessage(),

e);

return

new

RspDTO(404,

"路径不存在,请检查路径是否正确");

}

@ExceptionHandler(DuplicateKeyException.class)

public

RspDTO

handleDuplicateKeyException(DuplicateKeyException

e)

{

logger.error(e.getMessage(),

e);

return

new

RspDTO(DUPLICATE_KEY_CODE,

"数据重复,请检查后提交");

}

@ExceptionHandler(Exception.class)

public

RspDTO

handleException(Exception

e)

{

logger.error(e.getMessage(),

e);

return

new

RspDTO(500,

"系统繁忙,请稍后再试");

}

}

4.测试如下确实做到了参数校验时返回异常信息和对应的code,方便了我们不再繁琐的处理参数校验在ValidationMperties就是校验的message,有着已经写好的默认的message,且是支持i18n的,大家可以阅读源码赏析自定义参数注解1.比如我们来个自定义身份证校验注解@Documented

@Target({ElementType.PARAMETER,

ElementType.FIELD})

@Retention(RetentionPolicy.RUNTIME)

@Constraint(validatedBy

=

IdentityCardNumberValidator.class)

public

@interface

IdentityCardNumber

{

String

message()

default

"身份证号码不合法";

Class[]

groups()

default

{};

Class[]

payload()

default

{};

}

这个注解是作用在Field字段上,运行时生效,触发的是IdentityCardNumber这个验证类。message定制化的提示信息,主要是从ValidationMperties里提取,也可以依据实际情况进行定制groups这里主要进行将validator进行分类,不同的类group中会执行不同的validator操作payload主要是针对bean的,使用不多。2.然后自定义Validator这个是真正进行验证的逻辑代码: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在项目源码中,可自行查看3.使用自定义的注解

@NotBlank(message

=

"身份证号不能为空")

@IdentityCardNumber(message

=

"身份证信息有误,请核对后提交")

private

String

clientCardNo;

4.使用groups的校验有的宝宝说同一个对象要复用,比如UserDTO在更新时候要校验userId,在保存的时候不需要校验userId,在两种情况下都要校验username,那就用上groups了:在公众号顶级架构师后台回复“架构”,获取一份惊喜礼包。先定义groups的分组接口Create和Updateimport

javax.validation.groups.Default;

public

interface

Create

extends

Default

{

}

import

javax.validation.groups.Default;

public

interface

Update

extends

Default{

}

再在需要校验的地方@Validated声明校验组

/**

*

走参数校验注解的

groups

组合校验

*

*

@param

userDTO

*

@return

*/

@PostMapping("/update/groups")

public

RspDTO

update(@RequestBody

@Validated(Update.class)

UserDTO

userDTO)

{

userService.updateById(userDTO);

return

RspDTO.success();

}

在DTO中的字段上定义好groups={}的分组类型@Data

public

class

UserDTO

implements

Serializable

{

private

static

final

long

serialVersionUID

=

1L;

/***

用户ID*/

@NotNull(message

=

"用户id不能为空",

groups

=

Update.class)

private

Long

userId;

/**

*

用户名

*/

@NotBlank(message

=

"用户名不能为空")

@Length(max

=

20,

message

=

"用户名不能超过20个字符",

groups

=

{Create.class,

Update.class})

@Pattern(regexp

=

"^[\\u4E00-\\u9FA5A-Za-z0-9\\*]*$",

message

=

"用户昵称限制:最多20字符,包含文字、字母和数字")

private

String

username;

/**

*

手机号

*/

@NotBlank(message

=

"手机号不能为空")

@Pattern(regexp

=

"^[1][3,4,5,6,7,8,9][0-9]{9}$",

message

=

"手机号格式有误",

groups

=

{Create.class,

Update.class})

private

String

mobile;

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论