iT邦幫忙

2023 iThome 鐵人賽

DAY 21
0
Security

從自建漏洞中學習 - 一起填坑吧系列 第 21

Auth 應用程式 - Error 處理篇

  • 分享至 

  • xImage
  •  

Error 處理

在 Express 中要處理的 Error 有哪幾種?

https://ithelp.ithome.com.tw/upload/images/20231006/20107197UKTd2OreSa.png

主要分為兩種 Error:

  • Operational Errors 運算錯誤

    運算錯誤通常是在執行階段發生的錯誤,而非因為系統設計上的邏輯錯誤而產生的 BUG。

    舉例來說:

    像是伺服器無法連線、資料庫無法連線或是請求超時,等等。

  • Programming Errors 開發者錯誤

    開發者錯誤是開發者在撰寫程式時,應注意而未注意而產生的 BUG,這種情況有可能是因為該 BUG 比較難被察覺或是處理而產生。

    舉例來說:

    像是讀取了某個 undefined 的屬性、用 "String" 或 "Number" 類型傳入某個應該為 "Object" 類型的參數,等等。

在 Express 如何處理在同步或是非同步程式碼中的錯誤 ?

  • 錯誤若發生在同步程式碼中:

    根據 Express 官方文件:

    Errors that occur in synchronous code inside route handlers and middleware require no extra work. If synchronous code throws an error, then Express will catch and process it.

    解說:
    不需要特別對 middleware 做設計,Express 將捕獲並處理它。

    範例:

    app.get('/', (req, res) => {
        throw new Error('It is broken.') // Express 會自己 catch 它.
    });
    
  • 錯誤若發生在非同步程式碼中:

    根據 Express 官方文件:

    For errors returned from asynchronous functions invoked by route handlers and middleware, you must pass them to the next() function, where Express will catch and process them.

    解說:
    對於非同步函數傳回的錯誤,必須將它們傳遞給 next() 函數,Express 將在其中擷取並處理它們。

    範例:

    app.get('/', (req, res, next) => {
        fs.readFile('/file-does-not-exist', (err, data) => {
          if (err) {
            next(err); // 傳遞 errors 給 Express.
          } else {
            res.send(data);
          }
        })
    })
    

    不過,從 Express 5 開始,傳回 Promise 的 route 處理程序和 middleware 會在拒絕或拋出錯誤時自動呼叫 next(value)。

    如以下範例:

    app.get('/post/:id', async (req, res, next) => {
        const user = await getPostById(req.params.id);
        res.send(user);
    });
    

    解說:

    • 如果 getPostById 有處理錯誤:

      如 throw Error 或 reject,則將使用拋出的錯誤或拒絕的值來呼叫 next 。

    • 如果未提供拒絕值:

      會使用 Express 路由器提供的預設錯誤物件來呼叫 next。

回到正題,我們如何管控 Error ?

  • Step 1: 建立一個 Custom 的 Error 繼承 Error

    藉由建立一個 Custom 的 Error,處理 statusCode 跟 Error message

    此處建立一個 Custom 的 Error - AppError

    class AppError extends Error {
        constructor(message, statusCode){
            super(message);
    
            this.statusCode = statusCode;
            // status code 如果是 4 開頭,就是跟 Client 端互動相關的 Error,也就是 Operational Error 
            this.status = `${statusCode}`.startWith('4') ? 'fail' : 'error';
            this.isOperational = true;
    
            // 返回調用 Stack Trace
            Error.captureStackTrace(this, this.constructor);
        }
    }
    
    module.exports = AppError; 
    
  • Step 2: 建立一個 ErrorController,集中處裡 Errors

    • 首先,我們先建立一個 ErrorController

      ErrorController:

      module.exports = (err, req, res, next) => {
      
          err.statusCode = err.statusCode || 500;
          err.status = err.status || 'error';
      
          res.status(err.statusCode).json({
              status: err.status,
              message: err.message
          });
      }
      
    • 在管理 route 的地方 - app.js 加入 ErrorController:

      const errorHandler = require('./controllers/errorController');
      app.use(errorHandler);
      
  • Step 3 - 建立一個控管非同步 Error 的 function

    透過 return 調用傳入的 async function,並且針對其 function 做 catch,最後把 error 直接傳給 next。

    建立一個 catchAsyncError.js 檔案:

    catchAsyncError.js

    module.exports = fun => {
        return (req, res, next) => {
            fun(req, res, next).catch(next);
        }
    }
    

今日小心得

今天大致上介紹了幾個 Error handling 會使用到的 function,在後續篇章會舉例說明幾個需要處理的 Error,並實作它 ~ 讓我們明天繼續看下去吧 !


Reference:


上一篇
Auth 應用程式 - Authentication 認證篇
下一篇
Auth 應用程式 - Error 處理篇之 2
系列文
從自建漏洞中學習 - 一起填坑吧30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言