Dubbo 的RPC调用中Consumer 和 Provider端都可以对调用的方法做传参验证,参数的验证可以通过JSR303规范 (Java Specification Requests) 提到的 Bean Validation 方式来验证,Dubbo官方也是这么推荐的。最佳实践中分包部分提到传参的数据模型定义在API的jar包中,如果你是这样做的,那么参数的验证完全可以在Consumer端完成,这样一来就可以减少网络开销并提早得到失败结果。
    
下面的介绍基于 Dubbo2.6.2  + Springboot2.1.3 的环境,内容会在Dubbo官方文档的基础上做一些扩展。
 
Hibernate Validation 是Bean Validation 的一个实现,也是Dubbo官方推荐的实现方式,使用Hibernate Validation需要引入如下依赖:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.16.Final</version>
</dependency> <!-- 若启动报错 java.lang.ExceptionInInitializerError 则还需引入如下 el-api 依赖 -->
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.0</version>
</dependency>

在传参DTO中加上 "@NotNull" 注解

@Data
public class AddressDto implements Serializable { @NotNull(message = "地址不能为空")
private String address;
private String city;
private Boolean batch; }

传参数据模型中添加需要验证的注解后, consumer端在RPC调用前会经过一个叫做 ValidationFilter 的过滤器,该过滤期获取到validator后会调用Dubbo提供的 JValidator  如下validate方法

@Override
public void validate(String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Exception {
List<Class<?>> groups = new ArrayList<Class<?>>();
String methodClassName = clazz.getName() + "$" + toUpperMethoName(methodName);
Class<?> methodClass = null;
try {
methodClass = Class.forName(methodClassName, false, Thread.currentThread().getContextClassLoader());
groups.add(methodClass);
} catch (ClassNotFoundException e) {
}
Set<ConstraintViolation<?>> violations = new HashSet<ConstraintViolation<?>>();
Method method = clazz.getMethod(methodName, parameterTypes);
Class<?>[] methodClasses = null;
if (method.isAnnotationPresent(MethodValidated.class)){
methodClasses = method.getAnnotation(MethodValidated.class).value();
groups.addAll(Arrays.asList(methodClasses));
}
// add into default group
groups.add(0, Default.class);
groups.add(1, clazz); // convert list to array
Class<?>[] classgroups = groups.toArray(new Class[0]); Object parameterBean = getMethodParameterBean(clazz, method, arguments);
if (parameterBean != null) {
violations.addAll(validator.validate(parameterBean, classgroups ));
} for (Object arg : arguments) {
validate(violations, arg, classgroups);
} if (!violations.isEmpty()) {
logger.error("Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations);
throw new ConstraintViolationException("Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations, violations);
}
}
  
然后在for循环的中调用了另一 validate的私有方法,该方法对传参DTO的类型做了判断后 使用了Hibernate Validation 提供的 ValidatorImpl.validate 方法来完成的参数验证,验证完之后返回 ConstraintVoilation 的集合,在以上方法最后对集合violations做判断是否非空,非空则代表验证失败, 并在后续抛出 ConstraintViolationException 异常,这个异常在经过你controller层之后,是可以被我们定义的 ControllerAdvice 捕获到。全局异常处理方式参考,这样只需要增加一个 ExceptionHandler 处理ConstraintViolationException 就能将 Validation 中用到的注解的message返回调用端,
@ResponseBody
@ExceptionHandler(value = ValidationException.class)
public Result handleAddressException(ValidationException e){
log.error(e.getMessage());
String validateMsg = null;
// 若判断是 ConstraintViolationException异常 则取出message信息
if(e instanceof ConstraintViolationException){
ConstraintViolationException cve = (ConstraintViolationException)e;
Set<ConstraintViolation<?>> constraintViolations = cve.getConstraintViolations();
for(ConstraintViolation cv : constraintViolations){
validateMsg = cv.getMessage();
break;
} return ResultUtil.error(ResultEnum.VALIDATION_FAILURE.getCode(), validateMsg);
}
return ResultUtil.error(ResultEnum.VALIDATION_FAILURE.getCode(),e.getMessage());
}

最终在调用端获取到JSON结果如下

{
"requestId": null,
"success": false,
"business": null,
"code": "20001",
"message": "地址不能为空",
"date": null,
"version": null,
"obj": null
}

参考:

[1] Dubbo官方文档
  

Dubbo RPC调用参数校验---错误message自动返回的更多相关文章

  1. SpringMVC参数校验(针对`@RequestBody`返回`400`)

    SpringMVC参数校验(针对@RequestBody返回400) 前言 习惯别人帮忙做事的结果是自己不会做事了.一直以来,spring帮我解决了程序运行中的各种问题,我只要关心我的业务逻辑,设计好 ...

  2. dubbo rpc调用抛出的Exception处理

    关于dubbo的Exception堆栈被吃处理,网上已经有比较多的解决方法,在我们的应用场景中,不希望RPC调用对方抛出业务exception,而是通过Resp中的errorCode,errorMsg ...

  3. dubbo rpc调用,接收到的bean为null原因?

    前几天对接公司内部其他部门的系统,用dubbo调用,dubbo看起来很简单,但是却让我们调试了好久啊! 下面是调试纪录: 1. 调用该服务时,直接调不通,查看错误为 no provider ? 然后就 ...

  4. 更加灵活的参数校验,Spring-boot自定义参数校验注解

    上文我们讨论了如何使用@Min.@Max等注解进行参数校验,主要是针对基本数据类型和级联对象进行参数校验的演示,但是在实际中我们往往需要更为复杂的校验规则,比如注册用户的密码和确认密码进行校验,这个时 ...

  5. 后端参数校验器v1.0(调用一个方法校验所有参数并得到校验结果,且包括错误原因)

    一:介绍 在写后端时,面对多个参数,比如手机号码.密码等我们常常需要写验证逻辑,当需要验证的参数较多的时候我们会需要写很多的判断语句,这就造成了大量的代码冗余.因此我开发了一套参数验证器,只需要调用参 ...

  6. SpringBoot 如何进行参数校验,老鸟们都这么玩的!

    大家好,我是飘渺. 前几天写了一篇 SpringBoot如何统一后端返回格式?老鸟们都是这样玩的! 阅读效果还不错,而且被很多号主都转载过,今天我们继续第二篇,来聊聊在SprinBoot中如何集成参数 ...

  7. Java Bean Validation(参数校验) 最佳实践

    转载来自:http://www.cnblogs.com 参数校验是我们程序开发中必不可少的过程.用户在前端页面上填写表单时,前端js程序会校验参数的合法性,当数据到了后端,为了防止恶意操作,保持程序的 ...

  8. 利用 Bean Validation 来简化接口请求参数校验

    团队新来了个校招实习生静静,相互交流后发现竟然是我母校同实验室的小学妹,小学妹很热情地认下了我这个失散多年的大湿哥,后来... 小学妹:大湿哥,咱们项目里的 Controller 怎么都看不到参数校验 ...

  9. 全局异常处理及参数校验-SpringBoot 2.7 实战基础 (建议收藏)

    优雅哥 SpringBoot 2.7 实战基础 - 08 - 全局异常处理及参数校验 前后端分离开发非常普遍,后端处理业务,为前端提供接口.服务中总会出现很多运行时异常和业务异常,本文主要讲解在 Sp ...

随机推荐

  1. web移动开发小贴士

    1.判断手机类型 var u = navigator.userAgent; || u.indexOf(; //android var isiOS = !!u.match(/\(i[^;]+;( U;) ...

  2. HDU-6703-array-2019CCPC选拔赛

    我TM真是一个弟弟... 题意: 给出一串1-N的数字 你每次可以把某个位置的值+1000000 或者找一个值,所有a[1]...a[r]序列的数都不能等于这个值,并且这个值>w 当时比赛觉得肯 ...

  3. hdu 3234 Exclusive-OR (并查集)

    Problem - 3234 题意不难理解,就是给出一些断言,以及一些查询,回答查询或者在找到断言矛盾以后沉默不做任何事. 这题其实就是一个并查集的距离存储问题,只要记录并查集元素的相对值以及绝对值就 ...

  4. javascript 宽度和高度

    宽度和高度 对于编写css代码时,宽度和高度就是width和height 但是在JavaScript中,还有其他的宽度和高度,比如offsetWidth,offsetHeight,clientX,cl ...

  5. 如果用HTML5做一个在线视频聊天【原创】

    首先使用node.js 搭建一个简易的 websocket服务器: var cons = new Array(); var ws = require('ws').Server; var server ...

  6. 2016年NOIP普及组复赛题解

    题目涉及算法: 买铅笔:入门题: 回文日期:枚举: 海港:双指针: 魔法阵:数学推理. 买铅笔 题目链接:https://www.luogu.org/problem/P1909 设至少要买 \(num ...

  7. mybatis PageHelper分页插件 和 LRU算法缓存读取数据

    分页: PageHelper的优点是,分页和Mapper.xml完全解耦.实现方式是以插件的形式,对Mybatis执行的流程进行了强化,添加了总数count和limit查询.属于物理分页. 一.首先注 ...

  8. Vue实现同级组件的通信

    一.文件结构 二.vue.js 打开此链接 https://cdn.bootcss.com/vue/2.6.10/vue.js 复制粘贴页面的所有内容 三.index.html <!DOCTYP ...

  9. git clone和download zip的区别

    采用git clone的项目包含.git目录,这里面有历史版本信息 采用下载zip文件的是没有版本历史信息的.只是当前分支的最新版本 克隆指令: $ git clone git://github.co ...

  10. 如何查看 Python 全部内置变量和内置函数?

    https://jingyan.baidu.com/article/7082dc1c071649e40a89bdb8.html Python 解释器内置了一些常量和函数,叫做内置常量(Built-in ...