摘要:否則非法請求參數(shù)小則影響用戶體驗或者產(chǎn)生垃圾數(shù)據(jù),大則會拖跨整個系統(tǒng)其次,手工對所有的參數(shù)進行校驗相當繁瑣,容易出錯,而且最后,通過工具來完成其實是比較好的方式,但是必須讓工具變得優(yōu)雅一些。
聲明:本文屬原創(chuàng)文章,始發(fā)于公號:程序員自學之道,同步發(fā)布到 sf,轉(zhuǎn)載請注明出處。
不夠好的方案在 Web 開發(fā)中, 我們經(jīng)常需要校驗各種參數(shù),這是一件繁瑣又重要的事情,對于很多人來說,在做參數(shù)校驗的時候,會有以下幾種類型的處理方式:
甩鍋型校驗太麻煩了,讓客戶端去負責校驗就行了,調(diào)用方傳錯了是調(diào)用方的問題,不是服務的問題,甩個 500 錯誤讓他們好好反?。?br>
勞模型有多少參數(shù),我就寫多少個 if 語句做判斷,校驗不通過的都寫一句友好的提示,如:
自己寫個參數(shù)校驗的通用工具,然后每個請求接收到的參數(shù)都調(diào)用工具方法來校驗,校驗不通過就把校驗結(jié)果返回給調(diào)用方。這樣確實能減少很多冗余的代碼:
對 SpringMVC 了解比較全面的朋友都知道,它支持 Bean Validation,因此可以通過使用 javax.validation.constraints 包下的注解,如 @NotNull @Max @Min 等,來實現(xiàn)由框架處理數(shù)據(jù)校驗:
首先,添加 hibernate-validator 依賴(SpringBoot 項目為我們自動添加了):
org.hibernate.validator hibernate-validator 6.0.10.Final
然后,在參數(shù)對象的字段上打注解:
最后,在 Controller 中給參數(shù)對象添加 @Valid 注解,并處理校驗結(jié)果:
tip:如果你的參數(shù)不是對象,一定要在 Controller 上打 @Validate 注解!
這樣做,每個Controller方法進來都要處理結(jié)果,也都是冗余的代碼。
方案分析以上這些處理方式都有不足之處:
首先,參數(shù)校驗是一件非常重要的事,客戶端要把住第一道防線,而服務方要采取不信任的態(tài)度,做好參數(shù)校驗。否則非法請求參數(shù)小則影響用戶體驗或者產(chǎn)生垃圾數(shù)據(jù),大則會拖跨整個系統(tǒng)!
其次,手工對所有的參數(shù)進行校驗相當繁瑣,容易出錯,而且 So boring~
最后,通過工具來完成其實是比較好的方式,但是必須讓工具變得優(yōu)雅一些。
那么,有沒有更好的解決方案呢?答案是:有的!
最佳實踐其實,上面的半自動型的解決方式,只要再進一步,就可以實現(xiàn)全自動了!
想想,如果上面的半自動型例子中,我們不在 Controller 方法中處理校驗結(jié)果,會怎么樣呢?答案是,會拋出異常:
那么,如果我們做了全局統(tǒng)一異常處理,不就可以實現(xiàn)自動校驗并返回我們想要的結(jié)果了嗎?所以我們可以這樣做:
@ControllerAdvice public class GlobalExceptionHandler { /** 統(tǒng)一處理參數(shù)校驗異常 */ @ExceptionHandler @ResponseBody public ResultBean> handleValidationException(BindException e) { // 獲取 String msg = e.getBindingResult().getAllErrors().stream() .map(DefaultMessageSourceResolvable::getDefaultMessage) .collect(Collectors.joining(",")); log.warn("參數(shù)校驗不通過, msg: {}", msg); return ResultBean.fail(msg); } }
然而,如果你只統(tǒng)一處理 BindException 這個異常的話,你會發(fā)現(xiàn)這個方案有時候好用,有時候卻會“失靈”。為什么呢?因為對于不同的參數(shù)解析方式,Spring做參數(shù)校驗時會拋出不同的異常,而且這些異常沒有繼承關(guān)系,通過異常獲取校驗結(jié)果的方式也各不相同(好坑爹~)。
總結(jié)起來有以下幾種異常需要處理:
對象參數(shù)接收請求體: MethodArgumentNotValidException
請求參數(shù)綁定到對象參數(shù)上: BindException
普通參數(shù): ConstraintViolationException
必填參數(shù)沒傳: ServletRequestBindingException
必填請求參數(shù)缺失:MissingServletRequestParameterException
路徑參數(shù)缺失:MissingPathVariableException
所以完整的處理方法應該是這樣:
@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ù)校驗不通過,msg: {}", msg); return ResultBean.fail(msg); }
添加了這個全局異常處理器之后,就可以自動參數(shù)校驗了!體驗飛升的感覺~~
完整實例已經(jīng)上傳到 GitHub,請查看:https://github.com/dadiyang/s...
如果我是在瀏覽器上訪問的,如發(fā)一個訪問某個頁面,結(jié)果參數(shù)校驗不通過,這時這個統(tǒng)一異常處理器會返回一個 json 格式的文本。普通用戶看到這樣的文本,估計要懵圈了。
那么問題來了,怎么能讓打開頁面的請求返回錯誤頁面,而 ajax 請求返回 json 呢?
我的實例代碼已經(jīng)展示了,有興趣可以了解一下,敬請期待下一篇的講解。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/74782.html
摘要:比如和指令,鏡像中的文件內(nèi)容被檢查并且為每個文件計算校驗和。這些文件的最終修改和訪問時間將不被考慮到校驗和內(nèi)。在查找緩存期間,校驗和將被用于與已存在的鏡像校驗和進行對比。 Docker 可以從 Dockerfile 中讀取指令自動構(gòu)建鏡像,Dockerfile是一個包含構(gòu)建指定鏡像所有命令的文本文件。Docker堅持使用特定的格式并且使用特定的命令。你可以在 Dockerfile參考 ...
摘要:作為面試官,我是如何甄別應聘者的包裝程度語言和等其他語言的對比分析和主從復制的原理詳解和持久化的原理是什么面試中經(jīng)常被問到的持久化與恢復實現(xiàn)故障恢復自動化詳解哨兵技術(shù)查漏補缺最易錯過的技術(shù)要點大掃盲意外宕機不難解決,但你真的懂數(shù)據(jù)恢復嗎每秒 作為面試官,我是如何甄別應聘者的包裝程度Go語言和Java、python等其他語言的對比分析 Redis和MySQL Redis:主從復制的原理詳...
摘要:作為面試官,我是如何甄別應聘者的包裝程度語言和等其他語言的對比分析和主從復制的原理詳解和持久化的原理是什么面試中經(jīng)常被問到的持久化與恢復實現(xiàn)故障恢復自動化詳解哨兵技術(shù)查漏補缺最易錯過的技術(shù)要點大掃盲意外宕機不難解決,但你真的懂數(shù)據(jù)恢復嗎每秒 作為面試官,我是如何甄別應聘者的包裝程度Go語言和Java、python等其他語言的對比分析 Redis和MySQL Redis:主從復制的原理詳...
閱讀 2132·2021-11-19 09:58
閱讀 1719·2021-11-15 11:36
閱讀 2879·2019-08-30 15:54
閱讀 3399·2019-08-29 15:07
閱讀 2771·2019-08-26 11:47
閱讀 2825·2019-08-26 10:11
閱讀 2511·2019-08-23 18:22
閱讀 2759·2019-08-23 17:58