Springboot min -Solon 詳解系列文章:
Springboot mini - Solon詳解(一)- 快速入門
Springboot mini - Solon詳解(二)- Solon的核心
Springboot mini - Solon詳解(三)- Solon的web開發
Springboot mini - Solon詳解(四)- Solon的事務傳播機制
Springboot mini - Solon詳解(五)- Solon擴展機制之Solon Pluginhtml
在業務的實現過程當中,尤爲是對外接口開發,咱們須要對請求進行大量的驗證並返回錯誤狀態碼和描述。lombok 框架有不少很讚的註解,可是人家是throw一個異常,這與有些需求不必定能匹配。java
該文將介紹Spring min -Solon的擴展驗證框架:solon.extend.validation
的使用和擴展( org.noear:solon-web
已包含)。效果以下:git
@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, int code, @Pattern("^13\\d{9}$") String mobile){ //... } }
相較於 Spring 的 Validator 是爭對 Bean,Solon 則是爭對 Context(即http參數)。這點區別很是大,Solon 的設計是在 Action 執行以前對 http 參數進行校驗。github
註解 | 做用範圍 | 說明 |
---|---|---|
Date | 參數 | 校驗註解的參數值爲日期格式 |
DecimalMax(value) | 參數 | 校驗註解的參數值小於等於@ DecimalMax指定的value值 |
DecimalMin(value) | 參數 | 校驗註解的參數值大於等於@ DecimalMin指定的value值 |
參數 | 校驗註解的參數值爲電子郵箱格式 | |
Length(min, max) | 參數 | 校驗註解的參數值長度在min和max區間內 |
Max(value) | 參數 | 校驗註解的參數值小於等於@Max指定的value值 |
Min(value) | 參數 | 校驗註解的參數值大於等於@Min指定的value值 |
NoRepeatSubmit | 控制器 或 動做 | 校驗本次請求沒有重複 |
NotBlank | 動做 或 參數 | 校驗註解的參數值不是空白 |
NotEmpty | 動做 或 參數 | 校驗註解的參數值不是空 |
NotNull | 動做 或 參數 | 校驗註解的參數值不是null |
NotZero | 動做 或 參數 | 校驗註解的參數值不是0 |
Null | 動做 或 參數 | 校驗註解的參數值是null |
Numeric | 動做 或 參數 | 校驗註解的參數值爲數字格式 |
Pattern(value) | 參數 | 校驗註解的參數值與指定的正則表達式匹配 |
Whitelist | 控制器 或 動做 | 校驗本次請求在白名單範圍內 |
Valid | 控制器 或 動做 | 爲控制器 或 動做啓用驗證能力 |
可做用在 [動做 或 參數] 上的註解,加在動做上時可支持多個參數的校驗。web
solon.extend.validation 經過 ValidatorManager,提供了一組定製和擴展接口。正則表達式
NoRepeatSubmit 默認使用了本地延時鎖。若是是分佈式環境,須要定製爲分佈式鎖:json
public class NoRepeatLockNew implements NoRepeatLock { @Override public boolean tryLock(String key, int seconds) { //使用分佈式鎖 // return LockUtils.tryLock(XWaterAdapter.global().service_name(), key, seconds); } } ValidatorManager.setNoRepeatLock(new NoRepeatLockNew());
或者 徹底重寫 NoRepeatSubmitValidator,並進行從新註冊app
框架層面沒辦法爲 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.extend.validation 默認輸出 http 400 狀態 + json;嘗試改改去掉 http 400 狀態。
@Configuration public class Config { @Bean //Solon 的 @Bean 也支持空函數,爲其它提運行申明 public void adapter() { ValidatorManager.global().onFailure((ctx, ano, rst, message) -> { ctx.setHandled(true); if (Utils.isEmpty(message)) { message = new StringBuilder(100) .append("@") .append(ano.annotationType().getSimpleName()) .append(" verification failed") .toString(); } ctx.output(message); return true; }); } }
偷懶一下,直接把自帶的扔出來了。只要看着能本身搞就好了:-P
@Target({ElementType.PARAMETER}) //只讓它做用到參數,無論做用在哪,最終都是對Context的校驗 @Retention(RetentionPolicy.RUNTIME) public @interface Date { @Note("日期表達式, 默認爲:ISO格式") //用Note註解,是爲了用時還能看到這個註釋 String value() default ""; String message() default ""; }
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 validate(Context ctx, Date anno, String name, StringBuilder tmp) { String val = ctx.param(name); if (val == null || tryParse(anno, val) == false) { tmp.append(',').append(name); } if (tmp.length() > 1) { return Result.failure(tmp.substring(1)); } else { return Result.succeed(); } } private boolean tryParse(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; } } }
@Configuration public class Config { @Bean public void adapter() { // // 此處爲註冊驗證器。若是有些驗證器重寫了,也是在此處註冊 // ValidatorManager.global().register(Date.class, DateValidator.instance); } }
@Valid @Controller public class UserController extends VerifyController{ @Mapping("/user/add") public void addUser(String name, @Date("yyyy-MM-dd") String birthday){ //... } }