當前位置:
首頁 > 最新 > 關注程序異常流

關注程序異常流

想分享這個主題很久了,前些天和好朋友聊天的時候也提到過,我覺得新手最像新手的地方就在於新手考慮事情總是不夠全面,這體現在很多方面,比如設計上、技術選型上等等。今天我想分享給大家的是其中一個方面:異常處理

正好前些時間在手機上看到這麼一則小故事:

故事很好笑,可能大家看完都會覺得這群程序員也太蠢了,連這種狀況都沒有考慮到。其實在我們工作中,也常常會出現只關注於正常流程處理的開發,而忽略一些非正常的流程的情況。

其實討論程序異常處理的話題比較大,我大概分成三個方面來說,一個是程序上的容錯,一個是業務上的容錯,另一個是安全方面

程序的容錯

程序的容錯是我們寫代碼過程中應該首要考慮的地方,比如當你要編寫一個模塊給別人使用時,如果沒有考慮到一些特殊情況的輸入,可能模塊就無法正常使用了。

需要考慮容錯其實是相當常見的場景,比如這裡我寫了一個好玩的「懶排序」,假設要給別人使用。

代碼比較簡單,大家可能也能看出裡面常見的幾個可能拋異常的地方。

第二個可能有問題的地方是 done 函數的調用,done 很可能外部沒有傳遞。這裡我們常常使用短路運算的方式避免:

最後還有一個可能比較隱晦的地方,這裡是對數字進行排序,所以我們應該考慮數組中每一項的類型是否合法,也就是類型校驗。類型校驗又是另外一個話題,這裡只提一下對數字的校驗方法。

大家用的比較多的應該是 isNaN 方法了,這是掛載在 window 上的方法。其實 ES6 也提供了一個 isNaN 方法,並掛載在了 Number 上,也就是 Number.isNaN。這兩者的區別在於 window.isNaN 傳入 undefined、非空字元串等其實並不是 NaN 的參數時也返回 true,而 Number.isNaN 會首先判斷參數是否為 number,所以我們更推薦使用 Number.isNaN。

以上是一個比簡單的例子,我們針對性地提出了三點改進。有了這些改進,我們的程序不會報錯,但在我們平常的開發過程中其實有些異常是不可避免的,我們必須要針對可能出現的異常進行異常捕獲,說到這裡,大家可能最先想到的就是 try...catch 語句了。

try...catch

try catch 是大家用的比較多的,這裡就不詳細展開說了,只提一下大家可能忽略的地方,就是 try catch 其實有三種形式:

try...catch

try...finally

try...catch...finally

finally 塊是不管異常與否都會進入的地方,我們常常在這裡面做一些清理工作,在前端可能是把一些變數置為 null 避免內存泄露,後端可能是關閉資料庫連接等等。

另外需要注意的是在 finally 中 return 的值將作為 try catch 語句的整體返回值,不管在 try 或者 catch 中是否已經 return 了。

Promise

Promise 的異常捕獲是我在面試中比較常問的問題。Promise 是非同步的,所以對其 try catch 是沒有作用的。

我們一般使用 catch 方法去處理 Promise 的 reject 狀態,這裡需要知道的是 Promise 的 catch 方法只是一種特殊的 then 方法,catch 方法等同於調用 then 方法但把第一個參數置為 undefined。

另外提一個新手可能會犯的錯誤,在 then 方法中的 onResolved 回調中拋出的異常只會讓返回的新 Promise 的狀態置為 reject,而不會讓同一 then 方法中的 onRejected 方法執行。

async/await

async/await 是比 Promise 使用起來更方便也更容易理解的語法,它讓非同步的執行有了同步的寫法。對於 async/await 的異常捕獲,我覺得只要理解兩個地方就行:

async 函數的返回值是 Promise 對象

await 命令就是該 Promise 內部 then 命令的語法糖

async 函數可以當做普通函數調用,也可以使用 await 表達式調用。當作為普通函數調用時,如上所述,該函數返回一個 Promise,我們使用 Promise 的異常捕獲即可:

當使用 await 表達式調用時,會使 async 函數暫停執行,等待表達式中的 Promise 解析完成後繼續執行 async 函數並返回解決結果。所以我們可以使用 try catch 進行捕獲:

window.onerror

其實以上說到的都算針對性的異常捕獲,但在實際開發中,我們總會碰到我們沒考慮到的程序異常,這裡可以使用一個全局的異常捕獲方法進行處理。

在瀏覽器里,提供了 window.onerror 事件,它會捕獲程序中出現的未被捕獲到的同步或非同步的錯誤。

在事件處理函數中,能獲取到錯誤信息、當前 URL、代碼行數、列數等,非常詳細。

nodejs

篇幅原因,nodejs 的異常捕獲就不展開說了,在 process 上有三個事件:

uncaughtException

捕捉全局未捕獲異常,一般用作使進程優雅退出

unhandledRejection

當有 Promise rejected 但沒有 onRejected 函數進行處理時觸發

rejectionHandled

當有 Promise rejected 但被 onRejected 函數進行處理時觸發

業務上的容錯

說完程序里的容錯,我們接著說一下業務上的容錯。其實在本文開頭提到的例子就是一個典型的業務上沒有進行容錯的例子。其實業務上的容錯更多的是產品需要考慮的問題,但作為開發,我們也需要去理解業務,並能敏銳地發現一些業務中可能碰到的問題,避免開發到一半需要推翻重來。

代碼上拋出異常只會讓你的程序不可用,說白了用戶體驗就不會好。但一旦業務上發生問題,那麼影響的可能就是產品的業務線了。

舉個例子,我們之前做了個項目,因為訂單的狀態太多,用戶會有很多不同的操作來使訂單轉移到不同的狀態,不同的狀態又會反應出不同的操作。最後就在不同狀態的切換中亂套了,導致上線之後收到了很多用戶狀態錯誤的反饋。

這跟開發往往沒有什麼太大的聯繫,而是對業務的理解。

安全

最後跟大家聊一下安全。我們說了這麼多,其實側重的都是正常用戶的使用,但在實際生產環境中,我們必須還要考慮一些惡意的輸入。

篇幅原因也不展開說了。。簡單提一下這幾種攻擊。

CSRF 攻擊

面試的時候我也會嘗試性問一下對方在安全方面考慮的問題。一般會從跨域開始問,然後自然而然提到 jsonp,jsonp 可以讓我們進行跨域請求,但其中是否會有安全問題?畢竟別人也能通過 jsonp 訪問你的網站了。

另外,當用戶登錄了 a.com 之後,打開了一個 evil.com,在 evil.com 里向 a.com 發送請求,是會攜帶 a.com 的 cookie 的,包括 jsonp 請求,這裡的安全問題又要如何避免?

一般我們會使用添加 token 的方式來防止 CSRF 攻擊,token 可以隱藏在表單中,或者在請求頭裡攜帶,作為一個合法請求的校驗。

XSS 攻擊

這個可能是前端接觸比較多的攻擊,一般分為反射型的 XSS 攻擊和存儲型的 XSS 攻擊。區別在於存儲型的 XSS 攻擊會使惡意代碼存放在服務端,導致所有能看到這段代碼的用戶都受影響。

XSS 攻擊的誘導常見於一些惡意鏈接,當用於訪問一些奇怪的網址(比如郵箱里的垃圾郵件、廣告鏈接),可能會跳轉到具有 XSS 漏洞的網站,從而引發安全問題。

一般我們需要考慮對用戶的輸入進行合法校驗,包括前端和後端。

SQL 注入

SQL 注入是後端同學需要著重考慮的問題,對於一些小白代碼,特別常見於一些學校的管理後台,很可能有 SQL 注入的漏洞,舉個最簡單的例子:

如果後端對用戶輸入沒有進行任何過濾,直接是這麼校驗用戶登錄的話,那麼只要用戶猜出用戶名,如常見的 admin,然後使用 " or "1" = "1,就能正常登錄了。

總結

說了這麼多,其實今天的主題就一個,那就是希望大家在以後的開發中,一定要多考慮異常的情況,不要想當然地以為用戶一定會按照正常的流程走進來。當然,這也需要產品同學考慮各種可能出現的業務情況、測試同學進行各種 case 的測試。在大家共同的努力下,才能保證整個產品不會出現大的漏洞。

如果發現以上內容有任何不正確的地方,或者想一起探討的,歡迎在評論區留言~

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 全球大搜羅 的精彩文章:

王者峽谷不需要愛情
菜刀科普——關於廚刀你不知道的那些事

TAG:全球大搜羅 |