答應我危号,別再if/else校驗請求參數(shù)了可以嗎

463e545e5f8d004412a7e2847d802d77.jpg

文:答應我,別再if/else校驗請求參數(shù)了可以嗎
注:中國傳統(tǒng)文化素邪,先仔細看外莲,若有用,再點贊兔朦, 給自己一點思考的時間
注:微信搜索:CodeCow偷线,關注這個非常 SAO 的程序員

?哎!彈指之間

遙想當年沽甥,其實我也特別鐘情于 if/else 連環(huán)寫法声邦,來 校驗請求入?yún)?/strong>,上來就是一頓SAO操作

image

就現(xiàn)在來說摆舟,我們項目都是前后端分離翔忽,前后端約定好請求參數(shù)英融,封裝成一個對象,前段根據(jù)對象來傳參歇式,但傳入的參數(shù)是否為空,怎么判斷胡野!

比如舉個好理解的簡單例子:

請求參數(shù)

@Data
@ApiModel(value = "登錄請求參數(shù)")
public class LoginReqDTO {

    @ApiModelProperty(value = "手機號")
    private String mobile;

    @ApiModelProperty(value = "密碼")
    private String password;

    @ApiModelProperty(value = "驗證碼")
    private String code;
}

響應結果

@Data
public class RespResult<T> {
    private Integer code;
    private String message;
    private T data;
}

當前端調用我們的登錄接口時材失,我們需要 判斷請求參數(shù)是否為空,這時候 SAO代碼 出現(xiàn)了:

@RestController
public class LoginController{

    @ApiOperation(value = "登錄接口")
    @PostMapping(value = "/login")
    public RespResult login(LoginReqDTO loginReqDTO) {
        if (StringUtils.isEmpty(loginReqDTO.getMobile())) {//判斷手機號是否為空
            return new RespResult(400, "手機號不能為空");
        } else if (StringUtils.isEmpty(loginReqDTO.getPassword())) {//判斷密碼是否為空
            return new RespResult(400, "密碼不能為空");
        } else if (StringUtils.isEmpty(loginReqDTO.getCode())) {//判斷驗證碼是否為空
            return new RespResult(400, "驗證碼不能為空");
        } else {
            return new RespResult(200, "成功");//我的嗎硫豆,終于成功了 龙巨!
        }
    }
}

臥CAO!現(xiàn)在請求對象里的參數(shù)只有三個熊响,當參數(shù)有幾十個時旨别,那幾十個 if/else 嵌套,不累嗎汗茄,那可以說是 非常酸爽了……

那么秸弛,問題來了:

  • 第一點:你是爽了,別人一閱讀(怕是上來就是一 Jao洪碳!

  • 第二點:現(xiàn)在是一個接口递览,那幾十個接口時,那全屏可能只有 if/else了瞳腌。(喲呵绞铃!膩害

  • 第三點:則是以后如果再復雜一點,或者想要再加條件的話嫂侍,是不是還的整個if/else儿捧,極其不好擴展。

  • 第四點:最后代碼若一改挑宠,以前的老功能肯定還得重測菲盾,豈不瘋了……

所以,如果在不看下文的情況下痹栖,你一般會如何去對付這些令人頭痛的if/else語句呢亿汞?

當然有人會說用 循環(huán)語句switch/case 來判斷是否會優(yōu)雅一些呢?答案是:有錘子區(qū)別揪阿,毛區(qū)別都沒有疗我!

image

接下來簡單講幾種改進方式,別再 if/else走天下了

有Boot自帶的參數(shù)驗證為啥不用

大家肯定學過 boot吧(沒學過也不打緊南捂,沒吃過豬肉吴裤,還沒見過豬跑嗎!D缃 )麦牺,因此,為啥不用boot自帶的 spring validation,也就是@Validated注解剖膳,為啥不用魏颓?

首先我們在請求參數(shù)上加上 @NotBlank注解,并定義為空時的 message

@Data
@ApiModel(value = "登錄請求參數(shù)")
public class LoginReqDTO {

    @NotBlank(message = "手機號不能為空")
    @ApiModelProperty(value = "手機號")
    private String mobile;

    @NotBlank(message = "密碼不能為空")
    @ApiModelProperty(value = "密碼")
    private String password;

    @NotBlank(message = "驗證碼不能為空")
    @ApiModelProperty(value = "驗證碼")
    private String code;
}

接下來我們將 請求參數(shù)是否為空 交給 spring validation 來判斷吱晒,只需要定義一個 全局異常處理器來處理異常:
GlobalExceptionHandler :

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(value = BindException.class)
    public RespResult violationException(BindException exception) {
        // 不帶任何參數(shù)訪問接口,會拋出 BindException
        // 因此甸饱,我們只需捕獲這個異常,并返回我們設置的 message 即可
        String message = exception.getAllErrors().get(0).getDefaultMessage();
        return new RespResult(400, message);
    }
}

接下來接口調用就變得非常簡單了仑濒,加個@Validated注解就行了叹话, if/else也灰飛煙滅了:

@RestController
public class LoginController {

    @ApiOperation(value = "登錄接口")
    @PostMapping(value = "/login", consumes = "application/json", produces = "application/json")
    public RespResult login(@Validated LoginReqDTO loginReqDTO) {
        return new RespResult(200, "成功"); 
    }
}

最后,我們用 空的請求參數(shù) 使用Postman訪問下登錄接口:localhost:8080/login墩瞳;結果為:

{
    "code": 400,
    "message": "手機號不能為空",
    "data": null
}
是不是感覺很爽驼壶,SAO代碼( if/else)也沒了。

而且喉酌,這樣一來热凹,假如以后我想擴充條件,只需要去“請求參數(shù)對象中添加一個@NotBlank(message = “XXX”)注解”即可瞭吃,而不是去改以前的代碼碌嘀,這豈不很穩(wěn)!
example:

    @NotBlank(message = "XXX不能為空")
    private String XXX;
image

穩(wěn)是穩(wěn)了歪架,但是股冗,假如請求參數(shù)不是對象怎么辦?

比如和蚪,稍微老一點的項目請求參數(shù)可能是非實體止状,那此時的 if/else 是怎么樣被 KO 的呢 !

別慌!此時我們只需要簡單改造下GlobalExceptionHandler——全局異常處理器

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler({ConstraintViolationException.class})
    public RespResult violationException(Exception exception) {
        if (exception instanceof ConstraintViolationException) { //使用關鍵字instanceof 判斷 ConstraintViolationException 是否為 Exception 直接或間接子類
            return constraintViolationException((ConstraintViolationException) exception); //調用下面方法攒霹,返回結果
        }
        return new RespResult(500, "server error"); // 否則跑出 server error
    }

    // 當我們沒有此方法怯疤,空參訪問localhost:8080/login 會拋出ConstraintViolationException 異常
    public RespResult constraintViolationException(ConstraintViolationException ex) {
        Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
        if (!CollectionUtils.isEmpty(constraintViolations)) { //判斷是否為空
            StringBuilder sb = new StringBuilder();
            for (ConstraintViolation constraintViolation : constraintViolations) { //遍歷 ConstraintViolation
                sb.append(constraintViolation.getMessage()).append(","); // 吧錯誤信息循環(huán)放到sb中, 并以逗號隔開
            }
            String errorMessage = sb.toString(); // 獲得異常信息字符串
            return new RespResult(400, errorMessage);
        }
        return new RespResult(500, "server error"); // 否則跑出 server error
    }
}

接下來,我們只需要在請求參數(shù) 前加上“** @NotBlank** ”注解催束,并且在接口所在類加上“ @Validated ” 即可集峦,if/else 同樣被 KO 了

@Validated  // 此注解別忘了
@RestController
public class LoginController {
    
    @ApiOperation(value = "登錄接口")
    @PostMapping(value = "/login", consumes = "application/json", produces = "application/json")
    public RespResult login(@NotBlank(message = "手機號不能為空") String mobile,
                            @NotBlank(message = "密碼不能為空") String password,
                            @NotBlank(message = "驗證碼不能為空") String code) {
        return new RespResult(200, "成功");
    }
}

我們再次用 空的請求參數(shù) 使用Postman訪問下登錄接口:localhost:8080/login;結果為:

{
    "code": 400,
    "message": "驗證碼不能為空,密碼不能為空,手機號不能為空,",
    "data": null
}

不難發(fā)現(xiàn)抠刺,把請求參數(shù)中所有為空的參數(shù)塔淤,都驗證出來了,它不香嗎速妖!

image

然并卵高蜂,實際開發(fā)中,并非 3 + 2 - 5 * 0 這么簡單

假如有需求罕容,校驗請求“手機號格式是否正確”备恤,怎么辦稿饰!別慌,自定義注解 登場

有自定義注解為啥不用

首先露泊,我們要自定義注解喉镰,肯定的先了解注解的本質。

「java.lang.annotation.Annotation」接口中有這么一句話滤淳,用來描述『注解』梧喷。

The common interface extended by all annotation types
譯:所有的注解類型都繼承自這個普通的接口(Annotation)

這句話有點抽象,但卻說出了注解的本質脖咐。

來來來,我們隨便看一個JDK內置注解的定義汇歹,比如咋們常用的:@Override

@Target(ElementType.METHOD) //注解放置的目標位置
@Retention(RetentionPolicy.SOURCE) //注解在哪個階段執(zhí)行
public @interface Override {    //注解
}

不難看出屁擅,這是注解 @Override 的定義,其實它本質上就是:

public interface Override extends Annotation{ //繼承 Annotation
}  

你沒有看錯产弹,注解的本質就是一個繼承了 Annotation 接口的接口

小編有幸在書上看到這樣一端描述:

  • 一個注解準確意義上來說派歌,只不過是一種特殊的注釋而已,如果沒有解析它的代碼痰哨,它可能連注釋都不如

注解小編就淺聊到這里了胶果,咋們言歸正傳,回到 **使用自定義注解 KO if/else ** 斤斧,也別讓 if/else 久等了早抠。

首先,自定一個參數(shù)驗證注解:@ValidParam

/**
* Create By CodeCow on 2020/7/21.
* 自定義注解
* @Target:注解放置的目標位置
* @Retention:注解在哪個階段執(zhí)行
* @Constraint:指定此注解的實現(xiàn)撬讽, 即:驗證器(就是下面的:ParamValidator 類)
*/
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = ParamValidator.class)
public @interface ValidParam {

   String message() default "手機號格式不正確";  // 校驗的失敗的時候返回的信息,可以指定默認值

   Class<?>[] groups() default {};

   Class<? extends Payload>[] payload() default {};
}

接下來我們 編寫一個校驗器 蕊连,驗證該注解

/**
 * Create By CodeCow on 2020/7/21.
 * 編寫校驗器 驗證該上面我們自定義的注解:@ValidParam
 * 我們需要 實現(xiàn) ConstraintValidator 接口 并且重寫 isValid 方法
 */
public class ParamValidator implements ConstraintValidator<ValidParam, String> {

    @Override
    public void initialize(ValidParam constraintAnnotation) {
        // 初始化
    }

    @Override
    public boolean isValid(String param, ConstraintValidatorContext constraintValidatorContext) {
        // 開始驗證
        // 手機號格式(正則語法)
        String mobileFormat = "^((13[0-9])|(14[5,7])|(15[^4,\\D])|(17[0,1,3,6-8])|" +  
                "(18[0-9])|(19[8,9])|(166))[0-9]{8}$";
        Pattern pattern = Pattern.compile(mobileFormat);
        return pattern.matcher(param).matches(); // 手機號格式正確返回 true,否則 false
    }
}

最后游昼,我們修改登錄接口甘苍,在 請求參數(shù) 前,加上我們自定義的注解(@ValidParam)即可:

同樣烘豌,請求參數(shù)的校驗载庭,只需一個注解, if/else就被 KO 了
@RestController
public class LoginController {

    @ApiOperation(value = "登錄接口")
    @PostMapping(value = "/login", consumes = "application/json", produces = "application/json")
    public RespResult login(@ValidParam String mobile) {
        return new RespResult(200, "成功");
    }
}

最后廊佩,我們再次以 空參數(shù) 使用Postman訪問下登錄接口:localhost:8080/login囚聚;結果為:

{
    "code": 400,
    "message": "手機號格式不正確,",
    "data": null
}

后記

好啦,今就先聊到這里吧罐寨,本文僅僅是 拋磚引玉 而已靡挥,使用了現(xiàn)階段大家比較“鐘情的 if/else” 打了個樣,不是說用 if/else 不好鸯绿,只是希望大家在以后的編碼中跋破,不要濫用簸淀,代碼不要太過“冗余”。

其次毒返,在真實項目中租幕,業(yè)務場景不可能像上面那么簡單,也就像小編前文所提(實際開發(fā)拧簸,并非 3 + 2 - 5 * 0 這么簡單)劲绪,所以在您握住鼠標的那一刻,還是的多思考一番盆赤,考慮這樣寫是否合理贾富,是否具有擴展性。

好文推薦

image

小聲BB

  • 中國傳統(tǒng)文化牺六,先仔細看颤枪,若有用,再點贊淑际, 給自己一點思考的時間
  • 更多幽默畏纲、風趣好文,盡在“ CodeCow ” 程序牛公眾號
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末春缕,一起剝皮案震驚了整個濱河市盗胀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌锄贼,老刑警劉巖票灰,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異咱娶,居然都是意外死亡米间,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門膘侮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屈糊,“玉大人,你說我怎么就攤上這事琼了÷呷瘢” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵雕薪,是天一觀的道長昧诱。 經(jīng)常有香客問我,道長所袁,這世上最難降的妖魔是什么盏档? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮燥爷,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己昼伴,他們只是感情好誊册,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布新思。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上午衰,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機與錄音冒萄,去河邊找鬼臊岸。 笑死,一個胖子當著我的面吹牛尊流,可吹牛的內容都是我干的扇单。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼奠旺,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了施流?” 一聲冷哼從身側響起响疚,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瞪醋,沒想到半個月后忿晕,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡银受,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年践盼,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宾巍。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡咕幻,死狀恐怖,靈堂內的尸體忽然破棺而出顶霞,到底是詐尸還是另有隱情肄程,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布选浑,位于F島的核電站蓝厌,受9級特大地震影響,放射性物質發(fā)生泄漏古徒。R本人自食惡果不足惜拓提,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望隧膘。 院中可真熱鬧代态,春花似錦寺惫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至必尼,卻和暖如春蒋搜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背判莉。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工豆挽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人券盅。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓帮哈,卻偏偏與公主長得像,于是被迫代替她去往敵國和親锰镀。 傳聞我的和親對象是個殘疾皇子娘侍,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353