Solon詳解(六)- Solon的校驗擴展框架使用與擴展

Solon詳解系列文章:
Solon詳解(一)- 快速入門
Solon詳解(二)- Solon的核心
Solon詳解(三)- Solon的web開發
Solon詳解(四)- Solon的事務傳播機制
Solon詳解(五)- Solon擴展機制之Solon Plugin
Solon詳解(六)- Solon的校驗擴展框架使用與擴展 html

在業務的實現過程當中,尤爲是對外接口開發,咱們須要對請求進行大量的驗證並返回錯誤狀態碼和描述。lombok 框架有不少很讚的註解,可是人家是throw一個異常,這與有些需求不必定能匹配。java

該文將介紹Solon的擴展驗證框架:solon.extend.validation 的使用和擴展( org.noear:solon-web 已包含)。效果以下:web

@XValid
@XController
public class UserController {
    @NoRepeatSubmit  //重複提交驗證
    @Whitelist     //白名單驗證
    @NotNull({"name", "mobile", "icon", "code"})  //非NULL驗證
    @Numeric({"code"})
    @XMapping("/user/add")
    public void addUser(String name, @Pattern("^http") String icon, int code, @Pattern("^13\\d{9}$") String mobile){
        //...
    }
}

相較於 Spring 的 Validator 是爭對 Bean,Solon 則是爭對 XContext(即http參數)。這點區別很是大,Solon 是在 XAction 執行以前對 http 參數進行校驗。正則表達式

註解 做用範圍 說明
Date 參數 校驗註解的參數值爲日期格式
DecimalMax(value) 參數 校驗註解的參數值小於等於@ DecimalMax指定的value值
DecimalMin(value) 參數 校驗註解的參數值大於等於@ DecimalMin指定的value值
Email 參數 校驗註解的參數值爲電子郵箱格式
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 控制器 或 動做 校驗本次請求在白名單範圍內
XValid 控制器 或 動做 爲控制器 或 動做啓用驗證能力

可做用在 [動做 或 參數] 上的註解,加在動做上時可支持多個參數的校驗。json

1、定製使用

solon.extend.validation 經過 ValidatorManager,提供了一組定製和擴展接口。app

一、@NoRepeatSubmit 改成分佈式鎖

NoRepeatSubmit 默認使用了本地延時鎖。若是是分佈式環境,須要定製爲分佈式鎖:框架

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());

二、@Whitelist 實現驗證

框架層面沒辦法爲 Whitelist 提供一個名單庫,因此須要經過一個接口實現完成對接。分佈式

public class WhitelistCheckerNew implements WhitelistChecker {
    @Override
    public boolean check(Whitelist anno, XContext ctx) {
        String ip = IPUtils.getIP(ctx);

        return WaterClient.Whitelist.existsOfServerIp(ip);
    }
}

ValidatorManager.setWhitelistChecker(new WhitelistCheckerNew());

三、改造校驗輸出

solon.extend.validation 默認輸出 http 400 狀態 + json;嘗試改改去掉 http 400 狀態。ide

@XConfiguration
public class Config {
    @XBean  //Solon 的 @XBean 也支持空函數,爲其它提運行申明
    public void adapter() {
        ValidatorManager.globalSet(new ValidatorManager((ctx, ano, rst, message) -> {
            ctx.setHandled(true);

            if (XUtil.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})   //只讓它做用到參數,無論做用在哪,最終都是對XContext的校驗
@Retention(RetentionPolicy.RUNTIME)
public @interface Date {
    @XNote("日期表達式, 默認爲:ISO格式")  //用XNote註解,是爲了用時還能看到這個註釋
    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 XResult validate(XContext 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 XResult.failure(tmp.substring(1));
        } else {
            return XResult.succeed();
        }
    }

    private boolean tryParse(Date anno, String val) {
        try {
            if (XUtil.isEmpty(anno.value())) {
                DateTimeFormatter.ISO_LOCAL_DATE_TIME.parse(val);
            } else {
                DateTimeFormatter.ofPattern(anno.value()).parse(val);
            }

            return true;
        } catch (Exception ex) {
            return false;
        }
    }
}

三、註冊到校驗管理器

@XConfiguration
public class Config {
    @XBean
    public void adapter() {
        ValidatorManager.global().register(Date.class, DateValidator.instance);
    }
}

四、使用一下

@XValid
@XController
public class UserController extends VerifyController{
    @XMapping("/user/add")
    public void addUser(String name, @Date("yyyy-MM-dd") String birthday){
        //...
    }
}
相關文章
相關標籤/搜索