Dubbo:参数校验


#Dubbo


dubbo:servicedubbo:referencedubbo:consumerdubbo:provider 中可以配置布尔类型的 validation 属性,含义是:

是否启用JSR303标准注解验证,如果启用,将根据方法参数上的注解进行校验

JSR303 用于 Bean Validation ,规范地址: https://beanvalidation.org/1.0/spec/

不过现在已经有 JSR380 (Bean Validation 2.0)了,规范地址:https://beanvalidation.org/2.0/

Java Hibernate validator 校验框架的旧版本实现了 JSR303,大版本6 实现了 JSR380 。

从在 dubbo 中的测试效果来看,JSR380 和 JSR303 的实现版本都可以用。

本文示例代码基于 Dubbo:使用 Spring 注解构建 Dubbo 服务 中的示例项目。

项目结构

.
├── README.md
├── build.gradle
├── settings.gradle
├── consumer
│   ├── src
│       └── main
│           ├── java
│           │   └── demo
│           │       └── consumer
│           │           ├── ConsumerMainV1.java
│           │           └── ConsumerMainV2.java
│           └── resources
│               ├── dubbo-consumer.xml
│               ├── dubbo.properties
│               └── log4j.properties
├── contract
│   ├── src
│       └── main
│           └── java
│               └── demo
│                   └── contract
│                       ├── DemoService.java
│                       └── requset
│                           └── HiRequest.java
├── provider
│   ├── src
│   │   └── main
│           ├── java
│           │   └── demo
│           │       └── provider
│           │           ├── DemoServiceImpl.java
│           │           └── ProviderMain.java
│           └── resources
│               ├── dubbo-provider.xml
│               ├── dubbo.properties
│               └── log4j.properties

build.gradle 内容如下:

group 'com.example'
version '1.0-SNAPSHOT'

apply plugin: 'java'

sourceCompatibility = 1.8

ext {
  dubboVersion = '2.7.1'
  zookeeperVersion = '3.4.10'
  zkclientVersion = '0.10'
  junitVersion = '4.12'
}

subprojects {
  apply plugin: 'java'

  repositories {
    maven { url 'https://maven.aliyun.com/repository/public/' }
    mavenCentral()
  }

  // 配置除了 contract 模块之外的所有模块
  if (!it.name.endsWith("contract")) {
    dependencies {
      compile group: 'org.hibernate.validator', name: 'hibernate-validator', version: '6.0.11.Final'
      compile group: 'org.glassfish', name: 'javax.el', version: '3.0.1-b09'
    }
  }

  dependencies {
    compile group: 'org.projectlombok', name: 'lombok', version: '1.18.0'
  }

}

// 配置子项目 contract
project(":contract") {

  dependencies {
    compile "javax.validation:validation-api:2.0.1.Final"
    testCompile "junit:junit:$junitVersion"
  }
}

// 配置子项目 consumer
project(":consumer") {

  dependencies {
    compile project(":contract") // 依赖子项目 contract
    compile "org.apache.dubbo:dubbo:$dubboVersion"
    compile "org.apache.dubbo:dubbo-dependencies-zookeeper:$dubboVersion"

    testCompile "junit:junit:$junitVersion"
  }

}

project(":provider") {  // 配置子项目 provider

  dependencies {

    compile project(":contract") // 依赖子项目 contract
    compile "org.apache.dubbo:dubbo:$dubboVersion"
    compile "org.apache.dubbo:dubbo-dependencies-zookeeper:$dubboVersion"

    testCompile "junit:junit:$junitVersion"
  }

}

协议定义

contract 中 DemoService 内容如下:

package demo.contract;

import demo.contract.requset.HiRequest;

import javax.validation.constraints.NotBlank;


public interface DemoService {

    String sayHello(@NotBlank(message = "name不能为空") String name);

    String sayHi(HiRequest request);

}

其中,HiRequest 类内容如下:

package demo.contract.requset;

import lombok.Data;
import lombok.EqualsAndHashCode;

import javax.validation.constraints.NotBlank;
import java.io.Serializable;

@Data
@EqualsAndHashCode(callSuper = false)
public class HiRequest implements Serializable {

    @NotBlank(message = "msg不能为空")
    private String msg;

}

服务端实现

provider 模块中 DemoServiceImpl 如下:

package demo.provider;

import demo.contract.DemoService;
import demo.contract.requset.HiRequest;
import org.springframework.stereotype.Service;

@Service
public class DemoServiceImpl implements DemoService {

    @Override
    public String sayHello(String name) {
        return "Hello, " + name;
    }

    @Override
    public String sayHi(HiRequest request) {
        return "Hi, " + request.getMsg();
    }

}

ProviderMain 内容见源码。

dubbo-provider.xml 内容在下面测试时会给出。

消费端实现

consumer 模块中 ConsumerMainV1 类内容:

package demo.consumer;

import demo.contract.DemoService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

@Configuration
@ImportResource({"dubbo-consumer.xml"})
public class ConsumerMainV1 {

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(ConsumerMainV1.class);
        DemoService demoService = ctx.getBean(DemoService.class);

        String s = demoService.sayHello("");  
        System.out.println(s);

    }

}

ConsumerMainV2 类:

package demo.consumer;

import demo.contract.DemoService;
import demo.contract.requset.HiRequest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

@Configuration
@ImportResource({"dubbo-consumer.xml"})
public class ConsumerMainV2 {

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(ConsumerMainV2.class);
        DemoService demoService = ctx.getBean(DemoService.class);

        HiRequest hiRequest = new HiRequest();
        hiRequest.setMsg(" ");
        String result = demoService.sayHi(hiRequest);
        System.out.println(result);
    }

}

测试:消费端校验参数,服务端不校验参数

consumer 模块中的 dubbo-consumer.xml 内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <dubbo:application name="consumer-of-helloworld-app"/>
    <dubbo:consumer timeout="5000" />
    <dubbo:registry address="zookeeper://127.0.0.1:2181" check="false"/>

    <dubbo:reference id="demoService"
                     interface="demo.contract.DemoService"
                     check="false"
                     validation="true"
    />

</beans>

注意,增加了validation="true",表示要对参数进行校验。

provider 模块中的 dubbo-provider.xml 内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <dubbo:application name="hello-world-app"></dubbo:application>
    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
    <dubbo:protocol name="dubbo" port="20881"/>

    <dubbo:service interface="demo.contract.DemoService"
                   class="demo.provider.DemoServiceImpl"
                   validation="false"
    />

</beans>

运行服务端的 ProviderMain 类,然后测试消费端运行结果。

执行 consumer 中的 ConsumerMainV1 ,会看到以下异常:

ERROR [main]  -  [DUBBO] Failed to validate service: demo.contract.DemoService, method: sayHello, cause: [ConstraintViolationImpl{interpolatedMessage='name不能为空', propertyPath=sayHelloArgument0, rootBeanClass=class demo.contract.DemoService_SayHelloParameter_java.lang.String, messageTemplate='name不能为空'}], dubbo version: 2.7.1, current host: 127.0.0.1
Exception in thread "main" javax.validation.ConstraintViolationException: Failed to validate service: demo.contract.DemoService, method: sayHello, cause: [ConstraintViolationImpl{interpolatedMessage='name不能为空', propertyPath=sayHelloArgument0, rootBeanClass=class demo.contract.DemoService_SayHelloParameter_java.lang.String, messageTemplate='name不能为空'}]
	at org.apache.dubbo.validation.support.jvalidation.JValidator.validate(JValidator.java:276)
	at org.apache.dubbo.validation.filter.ValidationFilter.invoke(ValidationFilter.java:85)
	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:73)
	at org.apache.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:88)
	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:73)
	at org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter.invoke(FutureFilter.java:49)
	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:73)
	at org.apache.dubbo.rpc.filter.ConsumerContextFilter.invoke(ConsumerContextFilter.java:54)
	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:73)
	at org.apache.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:56)
	at org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker.doInvoke(FailoverClusterInvoker.java:80)
	at org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker.invoke(AbstractClusterInvoker.java:242)
	at org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker.invoke(MockClusterInvoker.java:76)
	at org.apache.dubbo.rpc.proxy.InvokerInvocationHandler.invoke(InvokerInvocationHandler.java:57)
	at org.apache.dubbo.common.bytecode.proxy0.sayHello(proxy0.java)
	at demo.consumer.ConsumerMainV1.main(ConsumerMainV1.java:18)

执行 consumer 中的 ConsumerMainV2 ,会看到以下异常:

ERROR [main]  -  [DUBBO] Failed to validate service: demo.contract.DemoService, method: sayHi, cause: [ConstraintViolationImpl{interpolatedMessage='msg不能为空', propertyPath=msg, rootBeanClass=class demo.contract.requset.HiRequest, messageTemplate='msg不能为空'}], dubbo version: 2.7.1, current host: 127.0.0.1
Exception in thread "main" javax.validation.ConstraintViolationException: Failed to validate service: demo.contract.DemoService, method: sayHi, cause: [ConstraintViolationImpl{interpolatedMessage='msg不能为空', propertyPath=msg, rootBeanClass=class demo.contract.requset.HiRequest, messageTemplate='msg不能为空'}]
	at org.apache.dubbo.validation.support.jvalidation.JValidator.validate(JValidator.java:276)
	at org.apache.dubbo.validation.filter.ValidationFilter.invoke(ValidationFilter.java:85)
	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:73)
	at org.apache.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:88)
	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:73)
	at org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter.invoke(FutureFilter.java:49)
	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:73)
	at org.apache.dubbo.rpc.filter.ConsumerContextFilter.invoke(ConsumerContextFilter.java:54)
	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:73)
	at org.apache.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:56)
	at org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker.doInvoke(FailoverClusterInvoker.java:80)
	at org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker.invoke(AbstractClusterInvoker.java:242)
	at org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker.invoke(MockClusterInvoker.java:76)
	at org.apache.dubbo.rpc.proxy.InvokerInvocationHandler.invoke(InvokerInvocationHandler.java:57)
	at org.apache.dubbo.common.bytecode.proxy0.sayHi(proxy0.java)
	at demo.consumer.ConsumerMainV2.main(ConsumerMainV2.java:21)

测试:服务端校验参数,消费端不校验参数

这种情况下,消费端的请求会走到服务端,然后服务端会会对参数进行校验。若校验失败,则将失败信息返回给消费端。

consumer 模块中的 dubbo-consumer.xml 内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <dubbo:application name="consumer-of-helloworld-app"/>
    <dubbo:consumer timeout="5000" />
    <dubbo:registry address="zookeeper://127.0.0.1:2181" check="false"/>

    <!--将 validation 设置为 false-->
    <dubbo:reference id="demoService"
                     interface="demo.contract.DemoService"
                     check="false"
                     validation="false"
    />

</beans>

provider 模块中的 dubbo-provider.xml 内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <dubbo:application name="hello-world-app"></dubbo:application>
    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
    <dubbo:protocol name="dubbo" port="20881"/>

    <!--将 validation 设置为 true-->
    <dubbo:service interface="demo.contract.DemoService"
                   class="demo.provider.DemoServiceImpl"
                   validation="true"
    />

</beans>

运行服务端的 ProviderMain 类,然后测试消费端运行结果。

执行 consumer 中的 ConsumerMainV1 ,会看到以下异常:

Exception in thread "main" org.apache.dubbo.rpc.RpcException: Failed to invoke the method sayHello in the service demo.contract.DemoService. Tried 3 times of the providers [127.0.0.1:20881] (1/1) from the registry 127.0.0.1:2181 on the consumer 127.0.0.1 using the dubbo version 2.7.1. Last error is: Failed to invoke remote method: sayHello, provider: dubbo://127.0.0.1:20881/demo.contract.DemoService?anyhost=true&application=consumer-of-helloworld-app&bean.name=demo.contract.DemoService&check=false&class=demo.provider.DemoServiceImpl&default.deprecated=false&default.dynamic=false&default.lazy=false&default.register=true&default.sticky=false&default.timeout=5000&deprecated=false&dubbo=2.0.2&dynamic=false&generic=false&interface=demo.contract.DemoService&lazy=false&methods=sayHello,sayHi&pid=79466&qos.port=33333&register=true&register.ip=127.0.0.1&release=2.7.1&remote.application=hello-world-app&remote.timestamp=1557814984617&side=consumer&sticky=false&timestamp=1557815109410&validation=false, cause: Failed to send response: Response [id=2, version=2.0.2, status=20, event=false, error=null, result=RpcResult [result=null, exception=javax.validation.ConstraintViolationException: Failed to validate service: demo.contract.DemoService, method: sayHello, cause: [ConstraintViolationImpl{interpolatedMessage='name不能为空', propertyPath=sayHelloArgument0, rootBeanClass=class demo.contract.DemoService_SayHelloParameter_java.lang.String, messageTemplate='name不能为空'}]]], cause: java.lang.RuntimeException: Serialized class demo.contract.DemoService_SayHelloParameter_java.lang.String must implement java.io.Serializable
 Java field: private final java.lang.Object org.hibernate.validator.internal.engine.ConstraintViolationImpl.leafBeanInstance
 Java field: private final java.util.Set javax.validation.ConstraintViolationException.constraintViolations
java.lang.RuntimeException: Serialized class demo.contract.DemoService_SayHelloParameter_java.lang.String must implement java.io.Serializable

这个错误有点奇怪,先留着。

执行 consumer 中的 ConsumerMainV2 ,会看到以下异常:

Exception in thread "main" org.apache.dubbo.rpc.RpcException: Failed to invoke the method sayHi in the service demo.contract.DemoService. Tried 3 times of the providers [127.0.0.1:20881] (1/1) from the registry 127.0.0.1:2181 on the consumer 127.0.0.1 using the dubbo version 2.7.1. Last error is: Failed to invoke remote method: sayHi, provider: dubbo://127.0.0.1:20881/demo.contract.DemoService?anyhost=true&application=consumer-of-helloworld-app&bean.name=demo.contract.DemoService&check=false&class=demo.provider.DemoServiceImpl&default.deprecated=false&default.dynamic=false&default.lazy=false&default.register=true&default.sticky=false&default.timeout=5000&deprecated=false&dubbo=2.0.2&dynamic=false&generic=false&interface=demo.contract.DemoService&lazy=false&methods=sayHello,sayHi&pid=79488&qos.port=33333&register=true&register.ip=127.0.0.1&release=2.7.1&remote.application=hello-world-app&remote.timestamp=1557814984617&side=consumer&sticky=false&timestamp=1557815279401&validation=false, cause: com.alibaba.com.caucho.hessian.io.HessianFieldException: org.hibernate.validator.internal.engine.ConstraintViolationImpl.constraintDescriptor: 'org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl' could not be instantiated
com.alibaba.com.caucho.hessian.io.HessianFieldException: org.hibernate.validator.internal.engine.ConstraintViolationImpl.constraintDescriptor: 'org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl' could not be instantiated

这个错误依然很奇怪,原因 dubbo 默认的 Hessian 序列化协议对 hibernate validator 的报错信息序列化有问题,简单说,是 ConstraintDescriptorImpl 没有实现无参构造函数。这个应该是 dubbo 框架本身去解决这个问题,但是目前没有解决。具体可以参考Dubbo使用jsr303框架hibernate-validator遇到的问题 这篇文章中的讨论。

修复服务端参数校验问题

在上面的测试中,我们发现服务端参数校验给出的错误信息很奇怪,Dubbo使用jsr303框架hibernate-validator遇到的问题 给出了原因和解决方案。其中一个解决方案是修改hibernate-validator的原生类,修改Dubbo ValidationFilter,这也是我最终采用的方法

修改原生类有很多方法,可以是直接修改源码,之后打包,只给自己的项目用。也可以通过 javaagent 动态添加。

还有一个更简单的解决方案:新建一个 validation filter ,并使用自定义的异常代表参数校验失败。

项目结构:

.
├── README.md
├── settings.gradle
├── build.gradle
├── consumer
│   ├── src
│       └── main
│           ├── java
│           │   └── demo
│           │       └── consumer
│           │           ├── ConsumerMainV1.java
│           │           └── ConsumerMainV2.java
│           └── resources
│               ├── dubbo-consumer.xml
│               ├── dubbo.properties
│               └── log4j.properties
├── contract
│   ├── src
│       └── main
│           └── java
│               └── demo
│                   └── contract
│                       ├── DemoService.java
│                       ├── exception
│                       │   └── ParamCheckFailException.java  // 新增异常类
│                       └── requset
│                           └── HiRequest.java
├── provider
    ├── src
        └── main
            ├── java
            │   └── demo
            │       └── provider
            │           ├── CustomValidationFilter.java   // 校验用的 filter
            │           ├── DemoServiceImpl.java
            │           └── ProviderMain.java
            └── resources
                ├── META-INF
                │   └── dubbo
                │       └── org.apache.dubbo.rpc.Filter   // 定义 filter
                ├── dubbo-provider.xml
                ├── dubbo.properties
                └── log4j.properties

ParamCheckFailException 异常类:

package demo.contract.exception;

/**
 * 参数校验失败异常
 */
public class ParamCheckFailException extends RuntimeException {
    public ParamCheckFailException(String msg) {
        super(msg);
    }
}

CustomValidationFilter 过滤器(改造自 org.apache.dubbo.validation.filter.ValidationFilter):

package demo.provider;

import demo.contract.exception.ParamCheckFailException;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.ConfigUtils;
import org.apache.dubbo.validation.Validation;
import org.apache.dubbo.validation.Validator;

import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcResult;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Set;

@Slf4j
@Activate(group = {Constants.CONSUMER, Constants.PROVIDER}, value = Constants.VALIDATION_KEY, order = 10000)
public class CustomValidationFilter implements Filter {

    private Validation validation;

    public void setValidation(Validation validation) {
        this.validation = validation;
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        if (validation != null && !invocation.getMethodName().startsWith("$")
                && ConfigUtils.isNotEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.VALIDATION_KEY))) {
            try {
                Validator validator = validation.getValidator(invoker.getUrl());
                if (validator != null) {
                    validator.validate(invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
                }
            } catch (RpcException e) {
                throw e;
            } catch (ConstraintViolationException e) {
                Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
                log.info("校验错误详情:{}", constraintViolations);
                if (constraintViolations.size() > 0) {
                    // 不用关心所有的校验失败情况,关心一个就够了
                    return new RpcResult(new ParamCheckFailException(constraintViolations.iterator().next().getMessage()));
                }
                throw new ParamCheckFailException("校验失败");

            } catch (Throwable t) {
                return new RpcResult(t);
            }
        }
        return invoker.invoke(invocation);
    }

}

provider 模块中的 META-INF/dubbo/org.apache.dubbo.rpc.Filter 文件内容如下:

customValidation=demo.provider.CustomValidationFilter

在 dubbo-provider.xml 配置过滤器:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <dubbo:application name="hello-world-app"></dubbo:application>
    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
    <dubbo:protocol name="dubbo" port="20881"/>

    <!--配置过滤器,去掉默认的 validation,增加自定义的 customValidation-->
    <dubbo:provider filter="default,-validation,customValidation" />

    <dubbo:service interface="demo.contract.DemoService"
                   class="demo.provider.DemoServiceImpl"
                   validation="true"
    />

</beans>

测试:

运行 ProviderMain 。

执行 consumer 中的 ConsumerMainV1 ,会看到以下异常:

Exception in thread "main" demo.contract.exception.ParamCheckFailException: name不能为空
	at demo.provider.CustomValidationFilter.invoke(CustomValidationFilter.java:47)
	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:73)
	at org.apache.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:63)
	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:73)
	at org.apache.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:88)
	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:73)
	at org.apache.dubbo.rpc.filter.TimeoutFilter.invoke(TimeoutFilter.java:54)
	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:73)
	at org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter.invoke(TraceFilter.java:80)
	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:73)
	at org.apache.dubbo.rpc.filter.ContextFilter.invoke(ContextFilter.java:79)
	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:73)
	at org.apache.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:137)
	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:73)
	at org.apache.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:38)
	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:73)
	at org.apache.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:39)
	at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:73)
	at org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:128)
	at org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:103)
	at org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:200)
	at org.apache.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:51)
	at org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:57)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

执行 consumer 中的 ConsumerMainV2 ,会看到以下异常:

Exception in thread "main" demo.contract.exception.ParamCheckFailException: msg不能为空
......
......

是在服务端配置参数校验,还是在消费端配置?

服务端必须配置,消费端最好配置。



( 本文完 )