在业务的实现过程中,尤其是对外接口开发,我们需要对请求进行大量的验证并返回错误状态码和描述。在前面的内容中也已经使用过验证机制。

该文将介绍 solon.validation 框架的使用和扩展。效果如下:

//这个注解一定要加类上(或者基类上)
@Valid
@Controller
public class UserController {
@NoRepeatSubmit //重复提交验证
@Whitelist //白名单验证
@NotNull({"name", "mobile", "icon", "code"}) //非NULL验证
@Numeric({"code"})
@Mapping("/user/add")
public void addUser(String name, @Pattern("^http") String icon, @Validated User user){
//...
}
} @Data
public class User {
@NotNull
private String nickname; @Email
private String email;
}

Solon 的校验框架,可支持Context的参数较验(即请求传入的参数),也可支持实体字段较验。

注解 作用范围 说明
Date 参数 或 字段 校验注解的值为日期格式
DecimalMax(value) 参数 或 字段 校验注解的值小于等于@ DecimalMax指定的value值
DecimalMin(value) 参数 或 字段 校验注解的值大于等于@ DecimalMin指定的value值
Email 参数 或 字段 校验注解的值为电子邮箱格式
Length(min, max) 参数 或 字段 校验注解的值长度在min和max区间内(对字符串有效)
Logined 控制器 或 动作 校验本次请求主体已登录
Max(value) 参数 或 字段 校验注解的值小于等于@Max指定的value值
Min(value) 参数 或 字段 校验注解的值大于等于@Min指定的value值
NoRepeatSubmit 控制器 或 动作 校验本次请求没有重复提交
NotBlacklist 控制器 或 动作 校验本次请求主体不在黑名单
NotBlank 动作 或 参数 或 字段 校验注解的值不是空白
NotEmpty 动作 或 参数 或 字段 校验注解的值不是空
NotNull 动作 或 参数 或 字段 校验注解的值不是null
NotZero 动作 或 参数 或 字段 校验注解的值不是0
Null 动作 或 参数 或 字段 校验注解的值是null
Numeric 动作 或 参数 或 字段 校验注解的值为数字格式
Pattern(value) 参数 或 字段 校验注解的值与指定的正则表达式匹配
Size 参数 或 字段 校验注解的集合大小在min和max区间内(对集合有效)
Whitelist 控制器 或 动作 校验本次请求主体在白名单范围内

可作用在 [动作 或 参数] 上的注解,加在动作上时可支持多个参数的校验。

1、开始定制使用

solon.validation 通过 ValidatorManager,提供了一组定制和扩展接口。

@NoRepeatSubmit 改为分布式锁验证

NoRepeatSubmit 默认使用了本地延时锁。如果是分布式环境,需要定制为分布式锁:

public class NoRepeatSubmitCheckerNew implements NoRepeatSubmitChecker {
@Override
public boolean check(String key, int seconds) {
//使用分布式锁
//
return LockUtils.tryLock(XWaterAdapter.global().service_name(), key, seconds);
}
} ValidatorManager.setNoRepeatSubmitChecker(new NoRepeatSubmitCheckerNew());

或者 完全重写 NoRepeatSubmitValidator,并进行重新注册

@Whitelist 实现验证

框架层面没办法为 Whitelist 提供一个名单库,所以需要通过一个接口实现完成对接。

public class WhitelistCheckerNew implements WhitelistChecker {
@Override
public boolean check(Whitelist anno, Context ctx) {
String ip = IPUtils.getIP(ctx); return WaterClient.Whitelist.existsOfServerIp(ip);
}
} ValidatorManager.setWhitelistChecker(new WhitelistCheckerNew());

或者 完全重写 WhitelistValidator,并进行重新注册

改造校验输出

solon.validation 默认输出 http 400 状态 + json;尝试改改去掉 http 400 状态。

@Configuration
public class Config {
@Bean //Solon 的 @Bean 也支持空函数,用于一些初始化动作
public void validInit() {
ValidatorManager.setFailureHandler((ctx, ano, rst, message) -> {
ctx.setHandled(true);
ctx.setRendered(true); if (Utils.isEmpty(message)) {
message = new StringBuilder(100)
.append("@")
.append(ano.annotationType().getSimpleName())
.append(" verification failed")
.toString();
} ctx.output(message); return true;
});
}
}

2、尝试添一个扩展注解

先定义个校验注解 @Date

偷懒一下,直接把自带的扔出来了。只要看这过程后,能自己搞就行了:-P

@Target({ElementType.PARAMETER})   //只让它作用到参数,不管作用在哪,最终都是对Context的校验
@Retention(RetentionPolicy.RUNTIME)
public @interface Date {
@Note("日期表达式, 默认为:ISO格式") //用Note注解,是为了用时还能看到这个注释
String value() default ""; String message() default "";
}

添加 @Date 的校验器实现类

public class DateValidator implements Validator<Date> {
public static final DateValidator instance = new DateValidator(); @Override
public String message(Date anno) {
return anno.message();
} /**
* 校验实体的字段
* */
@Override
public Result validateOfEntity(Class<?> clz, Date anno, String name, Object val0, StringBuilder tmp) {
if (val0 != null && val0 instanceof String == false) {
return Result.failure(clz.getSimpleName() + "." + name);
} String val = (String) val0; if (verify(anno, val) == false) {
return Result.failure(clz.getSimpleName() + "." + name);
} else {
return Result.succeed();
}
} /**
* 校验上下文的参数
* */
@Override
public Result validateOfContext(Context ctx, Date anno, String name, StringBuilder tmp) {
String val = ctx.param(name); if (verify(anno, val) == false) {
return Result.failure(name);
} else {
return Result.succeed();
}
} private boolean verify(Date anno, String val) {
//如果为空,算通过(交由@NotEmpty之类,进一步控制)
if (Utils.isEmpty(val)) {
return true;
} try {
if (Utils.isEmpty(anno.value())) {
DateTimeFormatter.ISO_LOCAL_DATE_TIME.parse(val);
} else {
DateTimeFormatter.ofPattern(anno.value()).parse(val);
} return true;
} catch (Exception ex) {
return false;
}
}
}

注册到校验管理器

@Configuration
public class Config {
@Bean
public void adapter() {
//
// 此处为注册验证器。如果有些验证器重写了,也是在此处注册
//
ValidatorManager.register(Date.class, new DateValidator());
}
}

可以使用它了

@Valid
@Controller
public class UserController extends VerifyController{
@Mapping("/user/add")
public void addUser(String name, @Date("yyyy-MM-dd") String birthday){
//...
}
}

Solon Web 开发,八、校验、及定制与扩展的更多相关文章

  1. Solon Web 开发

    Solon Web 开发 一.开始 二.开发知识准备 三.打包与运行 四.请求上下文 五.数据访问.事务与缓存应用 六.过滤器.处理.拦截器 七.视图模板与Mvc注解 八.校验.及定制与扩展 九.跨域 ...

  2. Solon Web 开发,一、开始

    Solon Web 开发 一.开始 二.开发知识准备 三.打包与运行 四.请求上下文 五.数据访问.事务与缓存应用 六.过滤器.处理.拦截器 七.视图模板与Mvc注解 八.校验.及定制与扩展 九.跨域 ...

  3. Solon Web 开发,二、开发知识准备

    Solon Web 开发 一.开始 二.开发知识准备 三.打包与运行 四.请求上下文 五.数据访问.事务与缓存应用 六.过滤器.处理.拦截器 七.视图模板与Mvc注解 八.校验.及定制与扩展 九.跨域 ...

  4. Solon Web 开发,四、请求上下文

    Solon Web 开发 一.开始 二.开发知识准备 三.打包与运行 四.请求上下文 五.数据访问.事务与缓存应用 六.过滤器.处理.拦截器 七.视图模板与Mvc注解 八.校验.及定制与扩展 九.跨域 ...

  5. Solon Web 开发,五、数据访问、事务与缓存应用

    Solon Web 开发 一.开始 二.开发知识准备 三.打包与运行 四.请求上下文 五.数据访问.事务与缓存应用 六.过滤器.处理.拦截器 七.视图模板与Mvc注解 八.校验.及定制与扩展 九.跨域 ...

  6. Solon Web 开发,六、过滤器、处理、拦截器

    Solon Web 开发 一.开始 二.开发知识准备 三.打包与运行 四.请求上下文 五.数据访问.事务与缓存应用 六.过滤器.处理.拦截器 七.视图模板与Mvc注解 八.校验.及定制与扩展 九.跨域 ...

  7. Solon Web 开发,七、视图模板与Mvc注解

    Solon Web 开发 一.开始 二.开发知识准备 三.打包与运行 四.请求上下文 五.数据访问.事务与缓存应用 六.过滤器.处理.拦截器 七.视图模板与Mvc注解 八.校验.及定制与扩展 九.跨域 ...

  8. Solon Web 开发,九、跨域处理

    Solon Web 开发 一.开始 二.开发知识准备 三.打包与运行 四.请求上下文 五.数据访问.事务与缓存应用 六.过滤器.处理.拦截器 七.视图模板与Mvc注解 八.校验.及定制与扩展 九.跨域 ...

  9. Solon Web 开发,十一、国际化

    Solon Web 开发 一.开始 二.开发知识准备 三.打包与运行 四.请求上下文 五.数据访问.事务与缓存应用 六.过滤器.处理.拦截器 七.视图模板与Mvc注解 八.校验.及定制与扩展 九.跨域 ...

随机推荐

  1. mobx在react的使用

    ​ 创建项目第六步 mobx 1.安装 yarn add mobx yarn add mobx-react 2.新建/src/store/store.js import {observable, co ...

  2. ACwing1211. 蚂蚁感冒

    题目: 长 100 厘米的细长直杆子上有 n 只蚂蚁. 它们的头有的朝左,有的朝右. 每只蚂蚁都只能沿着杆子向前爬,速度是 1 厘米/秒. 当两只蚂蚁碰面时,它们会同时掉头往相反的方向爬行. 这些蚂蚁 ...

  3. IDEA常用设置及插件

    设置 1.设置打开后不直接进入项目 IDEA默认打开时会直接进入上次打开的目录,有的时候加载很长时间,但这个时候可能我们并不是要打开这个项目,这里有一个设置,在如图的位置,去掉勾选,即可设置打开后不直 ...

  4. JAVA地址通过百度地图API转化为经纬度

    public static Map getLngAndLat(String address) { Map map = new HashMap(); String url = "http:// ...

  5. 【LeetCode】1437. 是否所有 1 都至少相隔 k 个元素 Check If All 1s Are at Least Length K Places Away

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 指针 日期 题目地址:https://leetcode ...

  6. 【LeetCode】1436. 旅行终点站 Destination City (Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 set 日期 题目地址:https://leetcod ...

  7. 【剑指Offer】矩阵覆盖 解题报告(Python)

    [剑指Offer]矩阵覆盖 解题报告(Python) 标签(空格分隔): LeetCode 题目地址:https://www.nowcoder.com/ta/coding-interviews 题目描 ...

  8. Centos/Docker/Nginx/Node/Jenkins 操作

    Centos Centos 是一个基于 Linux 的开源免费操作系统 # 本地拷贝文件到远程服务器scp output.txt root@47.93.242.155:/data/ output.tx ...

  9. CS5211替代PS8625|设计EDP转LVDS转接屏方案|替代PS8625

    PS8625将作为DP或eDP接收器设备出现在视频源中,并将作为LVDS显示面板的LVDS源设备.该设备是一个完全集成的解决方案,不需要外部CPU.内存.时钟基准或电压调节器.PS8625可配置为从显 ...

  10. VMware客户端vSphere Web Client新建虚拟机

    1.说明 vSphere Web Client是为管理员提供的一款通用的. 基于浏览器的VMware管理工具, 能够监控并管理VMware基础设施. 由于需要登录的宿主机安装的是ESXi-6.5.0, ...