您好,登錄后才能下訂單哦!
小編給大家分享一下在Node應用中進行錯誤異常處理的方法,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!
不知道你有沒有遇到這樣一種情況,某天你寫的代碼在線上突然發生錯誤,然后你打開控制臺,卻對著打過包的錯誤信息毫無頭緒?又或者說是代碼在node端出現了問題,你查看錯誤日志的時候,卻發現日志文件中都是雜亂的錯誤堆棧信息。
其實上面這些問題都可以通過在代碼中引入合適的錯誤機制進行解決。大部分時候,由于程序員在開發過程中更加關注需求的實現,反而會忽視一些底層的工作。而錯誤處理機制就相當于我們代碼上的最后一道保險,在程序發生已知或者意外的問題的時候,可以讓開發者在第一時間獲取信息,從而快速定位并解決問題。
常用的錯誤處理機制
首先我們來了解一下目前前端領域到底有哪些錯誤處理機制。
try catch
try...catch這種錯誤處理機制一定是大家最熟悉的,Javascript語言內置的錯誤處理機制可以在檢測到代碼異常的時候直接進行捕獲并處理。
function test() { try { throw new Error("error"); } catch(err) { console.log("some error happened:"); } } test()
node原生錯誤處理機制
大多數Node.js核心API都提供的是利用回調函數處理錯誤,例如:
const fs = require('fs'); function read() { fs.readFile("/some/file/does-not-exist", (err, data) => { if(err) { throw new Error("file not exist"); } console.log(data); }); } read();
通過回調函數的err參數來檢查是否出現錯誤,再進行處理。之所以Node.js采用這種錯誤處理機制,是因為異步方法所產生的方法并不能簡單地通過try...catch機制進行攔截。
promise
Promise是用于處理異步調用的規范,而其提供的錯誤處理機制,是通過catch方法進行捕獲。
fs.mkdir("./temp").then(() => { fs.writeFile("./temp/foobar.txt", "hello"); }).catch(err => { console.log(err) });
async/await + try catch
第三種錯誤處理機制是采用async/await語法糖加上try...catch語句進行的。這樣做的好處是異步和同步調用都能夠使用統一的方式進行處理了。
async function one() { await two(); } async function two() { await "hello"; throw new Error("error"); } async function test() { try { await one(); } catch(error) { console.log(error); } } test();
解決方案
promisify
如果你的代碼中充斥著多種不同的錯誤處理模式,那么維護起來是十分困難的。而且代碼的可讀性也會大大降低。因此,這里推薦采用的統一的解決方案。對于同步代碼來說,直接使用try...catch方式進行捕獲處理即可,而對于異步代碼來說,建議轉換成Promise然后采用async/await + try...catch這種方式進行處理。這樣風格統一,程序的健壯性也大大加強。例如下面這個數據庫請求的代碼:
const database = require("database"); function promiseGet(query) { return new Promise((resolve, reject) => { database.get(query, (err, result) => { if (err) { reject(err); } else { resolve(result); } }) }) } async function main() { await promiseGet("foo"); } main();
自定義錯誤類型
直接使用系統原生的錯誤信息通常會顯得太過單薄,不利于后續進一步的分析和處理。所以為了讓代碼的錯誤處理機制的功能更加強大,我們勢必要多花點精力進行額外的改造。
可以通過擴展基礎的Error類型來達到這一目的。
一般來說,要根據錯誤發生的位置采用不同的錯誤類型。
首先是應用層錯誤,它會保存額外的線索數據:
class ApplicationError extends Error { constructor(message, options = {}) { assert(typeof message === 'string'); assert(typeof options === 'object'); assert(options !== null); super(message); // Attach relevant information to the error instance // (e.g., the username). for (const [key, value] of Object.entries(options)) { this[key] = value; } } get name() { return this.constructor.name; } }
接著,可以再定義用戶接口的錯誤類型,該類型主要用于直接返回給客戶端,比如錯誤狀態碼等等。
class UserFacingError extends ApplicationError { constructor(message, options = {}) { super(message, options); } } class BadRequestError extends UserFacingError { get statusCode() { return 400 } } class NotFoundError extends UserFacingError { get statusCode() { return 404 } }
另外,對于底層的數據庫錯誤來說,可能需要更加細節的錯誤信息。此時也可以根據業務需要進行自定義:
class DatabaseError extends ApplicationError { get toString() { return "Errored happend in query: " + this.query + "\n" + this.message; } } // 使用的話 throw new DatabaseError("Other message", { query: query });
化繁為簡,集中處理
express
有了基礎的錯誤數據類型后,我們可以在代碼里針對不同的錯誤類型采取不同的解決方案。 接下來,以Express應用為例講解一下使用方法。
app.use('/user', (req, res, next) => { const data = await database.getData(req.params.userId); if (!data) { throw new NotFoundError("User not found") } // do other thing }); // 錯誤處理中間件 app.use(async (err, req, res, next) => { if (err instanceof UserFacingError) { res.sendStatus(err.statusCode); // or res.status(err.statusCode).send(err.errorCode) } else { res.sendStatus(500) } // 記錄日志 await logger.logError(err, 'parameter:', req.params, 'User Data:', req.user); // 發送郵件 await sendMailToAdminIfCritical(); })
具體到實際場景中,需要在不同的路由中拋出不同的錯誤類型,然后我們就可以通過在錯誤處理中間件中進行統一的處理。比如根據不同的錯誤類型返回不同的錯誤碼。還可以進行記錄日志,發送郵件等操作。
database
數據庫發生錯誤的時候,除了常規的拋出錯誤,有時候你可能還需要進行額外的重試或回退操作,如:
// 發生網絡錯誤的時候隔200ms,重試3次 function query(queryStr, token, repeatTime = 0, delay = 200) { try { await db.query(queryStr); } catch (err) { if (err instanceof NetworkError && repeatTime < 3) { query(queryStr, token, repeatTime + 1, delay); } throw err; } }
未處理錯誤
對于未處理的錯誤來說,我們可以使用node.js的unhandledRejection事件進行監聽:
process.on('unhandledRejection', error => { console.error('unhandledRejection', error); // To exit with a 'failure' code process.exit(1); });
而且從Node.js 12.0開始,可以使用以下命令啟動程序:
node app.js --unhandled-rejections
這樣也能夠在發現未處理異常的時候進行處理,官方支持了三種對應的處理模式:
strict: Raise the unhandled rejection as an uncaught exception.
warn: Always trigger a warning, no matter if the unhandledRejection hook is set or not but do not print the deprecation warning.
none: Silence all warnings.
總結
最后,總結一下。為了實現可擴展和可維護的錯誤處理機制,我們可以需要注意以下幾個方面:
使用自定義Error類,后續還能根據業務需要進行擴展
將異步代碼轉換成Promise,然后統一使用async/await + try...catch的形式進行錯誤捕獲
盡量采用統一的錯誤處理中間件函數
保持Error信息可理解,返回合適的錯誤狀態和代碼
對于未處理的錯誤,要即使捕獲并記錄
看完了這篇文章,相信你對“在Node應用中進行錯誤異常處理的方法”有了一定的了解,如果想了解更多相關知識,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。