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. oracle函数 INSTRB(C1,C2[,I[,J]])

    [功能]在一个字符串中搜索指定的字符,返回发现指定的字符的位置; [说明]多字节符(汉字.全角符等),按2个字符计算 [参数] C1    被搜索的字符串 C2    希望搜索的字符串 I     搜 ...

  2. NetBeans配置

    NetBeans下载链接:https://netbeans.org/downloads/8.2/ 选择PHP×64版本 NetBeans 安装插件Darcula LAF for NetBeansctr ...

  3. Android ListView显示底部的分割线

    有些时候,我们会提出这样的需求,希望ListView显示底部(顶部)的分割线,这样做,会使得UI效果更加精致,如下图所示: 如果搜索资料,大家会搜到一堆相关的方法,最多的莫过于设置listview的f ...

  4. H3C TCP/UDP端口号

  5. 创建JAVASCRIPT对象3种方法

    创建JAVASCRIPT对象3种方法 方法一:直接定义并创建对象实例 var obj = new Object();    //创建对象实例 //添加属性obj.num = 5;   //添加属性 o ...

  6. Spring Boot 集成日志logback + 控制台打印SQL

    一: 控制台打印SQL application.properties中添加如下即可在控制台打印sql logging.level.com.fx.fxxt.mapper=debug 二:日志 因为Spr ...

  7. POJ 2488 深搜dfs、

    题意:模拟国际象棋中马的走棋方式,其实和中国象棋的马走的方式其实是一样的,马可以从给定的方格棋盘中任意点开始,问是否能遍历全部格子,能的话输出字典序最小的走棋方式,否则输出impossible 思路: ...

  8. laravel怎么开启调试模式

      在.env文件里有个APP_DEBUG=true,如果是false,改成true就好了.

  9. Linux 内核驱动结构嵌入

    如同大部分驱动核心结构的情形, device_driver 结构常常被发现嵌到一个更高级的, 总 线特定的结构. lddbus 子系统不会和这样的趋势相反, 因此它已定义了它自己的 ldd_drive ...

  10. Java并发编程(您不知道的线程池操作), 最受欢迎的 8 位 Java 大师,Java并发包中的同步队列SynchronousQueue实现原理

    Java_并发编程培训 java并发程序设计教程 JUC Exchanger 一.概述 Exchanger 可以在对中对元素进行配对和交换的线程的同步点.每个线程将条目上的某个方法呈现给 exchan ...