Solon 詳解系列文章:
Solon 框架詳解(一)- 快速入門
Solon 框架詳解(二)- Solon的核心
Solon 框架詳解(三)- Solon的web開發
Solon 框架詳解(四)- Solon的事務傳播機制
Solon 框架詳解(五)- Solon擴展機制之Solon Plugin
Solon 框架詳解(六)- Solon的校驗框架使用、定制與擴展
Solon 框架詳解(七)- Solon Ioc 的注解對比Spring及JSR330
Solon 框架詳解(八)- Solon的緩存框架使用和定制
Solon 框架詳解(九)- 渲染控制之定制統一的接口輸出
Solon 框架詳解(十)- Solon 的常用配置
Solon 框架詳解(十一)- Solon Cloud 的配置說明
在業務的實現過程中,尤其是對外接口開發,我們需要對請求進行大量的驗證並返回錯誤狀態碼和描述。lombok 框架有很多很贊的注解,但是人家是throw一個異常,這與有些需求不一定能匹配。
該文將介紹Solon的擴展驗證框架:solon.validation
的使用和擴展( org.noear:solon-web
已包含)。效果如下:
@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;
}
相較於 Spring 的 Validator 是爭對 Bean,Solon 則是爭對 Context(即http參數)。這點區別非常大,Solon 的設計是在 Action 執行之前對 http 參數進行校驗。
注解 | 作用范圍 | 說明 |
---|---|---|
Date | 參數 或 字段 | 校驗注解的參數值為日期格式 |
DecimalMax(value) | 參數 或 字段 | 校驗注解的參數值小於等於@ DecimalMax指定的value值 |
DecimalMin(value) | 參數 或 字段 | 校驗注解的參數值大於等於@ DecimalMin指定的value值 |
參數 或 字段 | 校驗注解的參數值為電子郵箱格式 | |
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) | 參數 或 字段 | 校驗注解的集合長度在min和max區間內 |
Whitelist | 控制器 或 動作 | 校驗本次請求在白名單范圍內 |
Validated | 參數 或 字段 | 為實體參數 或 實體字段啟用驗證能力 |
Valid | 控制器 或 動作 | 為控制器 或 動作啟用驗證能力 |
可作用在 [動作 或 參數] 上的注解,加在動作上時可支持多個參數的校驗。
一、定制使用
solon.extend.validation 通過 ValidatorManager,提供了一組定制和擴展接口。
1、@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,並進行重新注冊
2、@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,並進行重新注冊
3、改造校驗輸出
solon.extend.validation 默認輸出 http 400 狀態 + json;嘗試改改去掉 http 400 狀態。
@Configuration
public class Config {
@Bean //Solon 的 @Bean 也支持空函數,為其它提運行申明
public void adapter() {
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;
});
}
}
二、添一個擴展注解
1、先定義個校驗注解 @Date
偷懶一下,直接把自帶的扔出來了。只要看着能自己搞就行了:-P
@Target({ElementType.PARAMETER}) //只讓它作用到參數,不管作用在哪,最終都是對Context的校驗
@Retention(RetentionPolicy.RUNTIME)
public @interface Date {
@Note("日期表達式, 默認為:ISO格式") //用Note注解,是為了用時還能看到這個注釋
String value() default "";
String message() default "";
}
2、添加 @Date 的校驗器實現類
public class DateValidator implements Validator<Date> {
@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 instanceof String == false) {
return Result.failure(clz.getSimpleName() + "." + name);
}
String val = (String) val0;
if (val == null || 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 (val == null || verify(anno, val) == false) {
return Result.failure(name);
} else {
return Result.succeed();
}
}
private boolean verify(Date anno, String val) {
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;
}
}
}
3、注冊到校驗管理器
@Configuration
public class Config {
@Bean
public void adapter() {
//
// 此處為注冊驗證器。如果有些驗證器重寫了,也是在此處注冊
//
ValidatorManager.register(Date.class, new DateValidator());
}
}
4、使用一下
@Valid
@Controller
public class UserController extends VerifyController{
@Mapping("/user/add")
public void addUser(String name, @Date("yyyy-MM-dd") String birthday){
//...
}
}
附:Solon項目地址
- gitee: https://gitee.com/noear/solon
- github: https://github.com/noear/solon