亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

Spring 參數(shù)校驗(yàn)最佳實(shí)踐(附完整實(shí)例)

tomlingtm / 1830人閱讀

摘要:否則非法請(qǐng)求參數(shù)小則影響用戶體驗(yàn)或者產(chǎn)生垃圾數(shù)據(jù),大則會(huì)拖跨整個(gè)系統(tǒng)其次,手工對(duì)所有的參數(shù)進(jìn)行校驗(yàn)相當(dāng)繁瑣,容易出錯(cuò),而且最后,通過工具來完成其實(shí)是比較好的方式,但是必須讓工具變得優(yōu)雅一些。

聲明:本文屬原創(chuàng)文章,始發(fā)于公號(hào):程序員自學(xué)之道,同步發(fā)布到 sf,轉(zhuǎn)載請(qǐng)注明出處。

不夠好的方案

在 Web 開發(fā)中, 我們經(jīng)常需要校驗(yàn)各種參數(shù),這是一件繁瑣又重要的事情,對(duì)于很多人來說,在做參數(shù)校驗(yàn)的時(shí)候,會(huì)有以下幾種類型的處理方式:

甩鍋型

校驗(yàn)太麻煩了,讓客戶端去負(fù)責(zé)校驗(yàn)就行了,調(diào)用方傳錯(cuò)了是調(diào)用方的問題,不是服務(wù)的問題,甩個(gè) 500 錯(cuò)誤讓他們好好反?。?br>

勞模型

有多少參數(shù),我就寫多少個(gè) if 語句做判斷,校驗(yàn)不通過的都寫一句友好的提示,如:

工具型

自己寫個(gè)參數(shù)校驗(yàn)的通用工具,然后每個(gè)請(qǐng)求接收到的參數(shù)都調(diào)用工具方法來校驗(yàn),校驗(yàn)不通過就把校驗(yàn)結(jié)果返回給調(diào)用方。這樣確實(shí)能減少很多冗余的代碼:

半自動(dòng)型

對(duì) SpringMVC 了解比較全面的朋友都知道,它支持 Bean Validation,因此可以通過使用 javax.validation.constraints 包下的注解,如 @NotNull @Max @Min 等,來實(shí)現(xiàn)由框架處理數(shù)據(jù)校驗(yàn):
首先,添加 hibernate-validator 依賴(SpringBoot 項(xiàng)目為我們自動(dòng)添加了):


    org.hibernate.validator
    hibernate-validator
    6.0.10.Final

然后,在參數(shù)對(duì)象的字段上打注解:

最后,在 Controller 中給參數(shù)對(duì)象添加 @Valid 注解,并處理校驗(yàn)結(jié)果:

tip:如果你的參數(shù)不是對(duì)象,一定要在 Controller 上打 @Validate 注解!

這樣做,每個(gè)Controller方法進(jìn)來都要處理結(jié)果,也都是冗余的代碼。

方案分析

以上這些處理方式都有不足之處:

首先,參數(shù)校驗(yàn)是一件非常重要的事,客戶端要把住第一道防線,而服務(wù)方要采取不信任的態(tài)度,做好參數(shù)校驗(yàn)。否則非法請(qǐng)求參數(shù)小則影響用戶體驗(yàn)或者產(chǎn)生垃圾數(shù)據(jù),大則會(huì)拖跨整個(gè)系統(tǒng)

其次,手工對(duì)所有的參數(shù)進(jìn)行校驗(yàn)相當(dāng)繁瑣,容易出錯(cuò),而且 So boring~

最后,通過工具來完成其實(shí)是比較好的方式,但是必須讓工具變得優(yōu)雅一些。

那么,有沒有更好的解決方案呢?答案是:有的

最佳實(shí)踐

其實(shí),上面的半自動(dòng)型的解決方式,只要再進(jìn)一步,就可以實(shí)現(xiàn)全自動(dòng)了!

想想,如果上面的半自動(dòng)型例子中,我們不在 Controller 方法中處理校驗(yàn)結(jié)果,會(huì)怎么樣呢?答案是,會(huì)拋出異常:

那么,如果我們做了全局統(tǒng)一異常處理,不就可以實(shí)現(xiàn)自動(dòng)校驗(yàn)并返回我們想要的結(jié)果了嗎?所以我們可以這樣做:

@ControllerAdvice
public class GlobalExceptionHandler {
    /** 統(tǒng)一處理參數(shù)校驗(yàn)異常 */
    @ExceptionHandler
    @ResponseBody
    public ResultBean handleValidationException(BindException e) {
        // 獲取
        String msg = e.getBindingResult().getAllErrors().stream()
                .map(DefaultMessageSourceResolvable::getDefaultMessage)
                .collect(Collectors.joining(","));
        log.warn("參數(shù)校驗(yàn)不通過, msg: {}", msg);
        return ResultBean.fail(msg);
    }
}

然而,如果你只統(tǒng)一處理 BindException 這個(gè)異常的話,你會(huì)發(fā)現(xiàn)這個(gè)方案有時(shí)候好用,有時(shí)候卻會(huì)“失靈”。為什么呢?因?yàn)?strong>對(duì)于不同的參數(shù)解析方式,Spring做參數(shù)校驗(yàn)時(shí)會(huì)拋出不同的異常,而且這些異常沒有繼承關(guān)系,通過異常獲取校驗(yàn)結(jié)果的方式也各不相同(好坑爹~)。

總結(jié)起來有以下幾種異常需要處理:

對(duì)象參數(shù)接收請(qǐng)求體: MethodArgumentNotValidException

請(qǐng)求參數(shù)綁定到對(duì)象參數(shù)上: BindException

普通參數(shù)ConstraintViolationException

必填參數(shù)沒傳: ServletRequestBindingException

必填請(qǐng)求參數(shù)缺失:MissingServletRequestParameterException

路徑參數(shù)缺失:MissingPathVariableException

所以完整的處理方法應(yīng)該是這樣:

@ExceptionHandler({ConstraintViolationException.class,
            MethodArgumentNotValidException.class,
            ServletRequestBindingException.class,
            BindException.class})
@ResponseBody
public ResultBean handleValidationException(Exception e) {
    String msg = "";
    if (e instanceof MethodArgumentNotValidException) {
        MethodArgumentNotValidException t = (MethodArgumentNotValidException) e;
        msg = getBindingResultMsg(t.getBindingResult());
    } else if (e instanceof BindException) {
        BindException t = (BindException) e;
        msg = getBindingResultMsg(t.getBindingResult());
    } else if (e instanceof ConstraintViolationException) {
        ConstraintViolationException t = (ConstraintViolationException) e;
        msg = t.getConstraintViolations().stream()
                .map(ConstraintViolation::getMessage)
                .collect(Collectors.joining(","));
    } else if (e instanceof MissingServletRequestParameterException) {
        MissingServletRequestParameterException t = (MissingServletRequestParameterException) e;
        msg = t.getParameterName() + " 不能為空";
    } else if (e instanceof MissingPathVariableException) {
        MissingPathVariableException t = (MissingPathVariableException) e;
        msg = t.getVariableName() + " 不能為空";
    } else {
        msg = "必填參數(shù)缺失";
    }
    log.warn("參數(shù)校驗(yàn)不通過,msg: {}", msg);
    return ResultBean.fail(msg);
}

添加了這個(gè)全局異常處理器之后,就可以自動(dòng)參數(shù)校驗(yàn)了!體驗(yàn)飛升的感覺~~

完整實(shí)例已經(jīng)上傳到 GitHub,請(qǐng)查看:https://github.com/dadiyang/s...

如果我是在瀏覽器上訪問的,如發(fā)一個(gè)訪問某個(gè)頁面,結(jié)果參數(shù)校驗(yàn)不通過,這時(shí)這個(gè)統(tǒng)一異常處理器會(huì)返回一個(gè) json 格式的文本。普通用戶看到這樣的文本,估計(jì)要懵圈了。

那么問題來了,怎么能讓打開頁面的請(qǐng)求返回錯(cuò)誤頁面,而 ajax 請(qǐng)求返回 json 呢

我的實(shí)例代碼已經(jīng)展示了,有興趣可以了解一下,敬請(qǐng)期待下一篇的講解。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/74782.html

相關(guān)文章

  • Dockerfile最佳實(shí)踐

    摘要:比如和指令,鏡像中的文件內(nèi)容被檢查并且為每個(gè)文件計(jì)算校驗(yàn)和。這些文件的最終修改和訪問時(shí)間將不被考慮到校驗(yàn)和內(nèi)。在查找緩存期間,校驗(yàn)和將被用于與已存在的鏡像校驗(yàn)和進(jìn)行對(duì)比。 Docker 可以從 Dockerfile 中讀取指令自動(dòng)構(gòu)建鏡像,Dockerfile是一個(gè)包含構(gòu)建指定鏡像所有命令的文本文件。Docker堅(jiān)持使用特定的格式并且使用特定的命令。你可以在 Dockerfile參考 ...

    張金寶 評(píng)論0 收藏0
  • 【推薦】最新200篇:技術(shù)文章整理

    摘要:作為面試官,我是如何甄別應(yīng)聘者的包裝程度語言和等其他語言的對(duì)比分析和主從復(fù)制的原理詳解和持久化的原理是什么面試中經(jīng)常被問到的持久化與恢復(fù)實(shí)現(xiàn)故障恢復(fù)自動(dòng)化詳解哨兵技術(shù)查漏補(bǔ)缺最易錯(cuò)過的技術(shù)要點(diǎn)大掃盲意外宕機(jī)不難解決,但你真的懂?dāng)?shù)據(jù)恢復(fù)嗎每秒 作為面試官,我是如何甄別應(yīng)聘者的包裝程度Go語言和Java、python等其他語言的對(duì)比分析 Redis和MySQL Redis:主從復(fù)制的原理詳...

    BicycleWarrior 評(píng)論0 收藏0
  • 【推薦】最新200篇:技術(shù)文章整理

    摘要:作為面試官,我是如何甄別應(yīng)聘者的包裝程度語言和等其他語言的對(duì)比分析和主從復(fù)制的原理詳解和持久化的原理是什么面試中經(jīng)常被問到的持久化與恢復(fù)實(shí)現(xiàn)故障恢復(fù)自動(dòng)化詳解哨兵技術(shù)查漏補(bǔ)缺最易錯(cuò)過的技術(shù)要點(diǎn)大掃盲意外宕機(jī)不難解決,但你真的懂?dāng)?shù)據(jù)恢復(fù)嗎每秒 作為面試官,我是如何甄別應(yīng)聘者的包裝程度Go語言和Java、python等其他語言的對(duì)比分析 Redis和MySQL Redis:主從復(fù)制的原理詳...

    tommego 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<