dubbo可以实现类似spring mvc validation一样的功能,基于注解实现参数的校验
此处默认认为已经有可用的 dubbo环境,,
测试项目使用 SpringBoot 2.X + dubbo2.7.X
1、添加 校验相关的 依赖
<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.2.0.Final</version>
</dependency>
定义接口和实现
public interface IUserService {
CommonResponse<Long> addUser(UserAddBO userAddBO);
}
@Service
public class UserService implements IUserService {
@Override
public CommonResponse<Long> addUser(@NotNull UserAddBO userAddBO) {
System.out.println(userAddBO.toString());
return CommonResponse.success(1L);
}
}
@Data
public class UserAddBO implements Serializable {
@NotNull
private String name;
@NotNull
@Min(value = 1,message = "年龄不能小于0")
private Integer age;
@NotNull
@Size(min = 1, max = 50,message = "地址长度不能大于50")
private String address;
}
默认情况下,校验不通过会抛出异常,此处,我们包装一下异常,返回相应的错误信息和状态码给上游调用方
实现 dubbo中的 ListenableFilter
@Slf4j
@Activate(group = CommonConstants.PROVIDER)
public class DubboExceptionFilter extends ListenableFilter {
public DubboExceptionFilter() {
super.listener = new DubboExceptionFilter.ExceptionListener();
}
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
return invoker.invoke(invocation);
}
static class ExceptionListener implements Listener {
@Override
public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
if (appResponse.hasException() && GenericService.class != invoker.getInterface()) {
try {
Throwable exception = appResponse.getException();
if (exception instanceof ConstraintViolationException) {
ConstraintViolationException realException = (ConstraintViolationException) exception;
if (invocation instanceof RpcInvocation) {
RpcInvocation realInvocation = (RpcInvocation) invocation;
Class<?> returnType = realInvocation.getReturnType();
if (returnType.isAssignableFrom(CommonResponse.class)) {
String errorMsg = CollectionUtils.isEmpty(realException.getConstraintViolations())
? PARAMS_INVALID_EMPTY
: realException.getConstraintViolations()
.stream().map(item -> item.getPropertyPath().toString() + ":" + item.getMessage()).collect(Collectors.joining(","));
appResponse.setValue(BaseResponse.error(PARAMS_INVALID_EMPTY, errorMsg));
appResponse.setException(null);
}
}
}
return;
} catch (Throwable e) {
log.error("error",e);
return;
}
}
}
@Override
public void onError(Throwable e, Invoker<?> invoker, Invocation invocation) {
log.error("Got unchecked and undeclared exception which called by {},service: {}, method:{}",
RpcContext.getContext().getRemoteHost(), invoker.getInterface().getName(), invocation.getMethodName(), e);
}
}
}
在 src/main/resources/META-INF/dubbo 中新建文件 org.apache.dubbo.rpc.Filter
内容如下
DubboExceptionFilter=com.example.dubboserver.filter.DubboExceptionFilter
最后,在resources目录下的配置文件中(例如application.properties,application.yml)加上
dubbo.provider.validation=true
说明:
dubbo.provider.validation=true代表在 接口提供方校验,
也可以指定 dubbo.consumer.validation=true,在 消费端校验