1. 程式人生 > 實用技巧 >服務端程式設計——異常+校驗器+環境變數

服務端程式設計——異常+校驗器+環境變數

一、異常處理

  • 在服務端程式設計的時候注意要進行異常的處理,當某處出現錯誤的時候要及時捕捉
  • 通常異常處理分為兩步:
    • 監聽錯誤
    • 輸出給客戶端一段有意義的資訊(錯誤提示)
  • 通常在服務端捕捉到的錯誤不會直接返回給客戶端,捕捉到的錯誤包含了 堆疊呼叫資訊,而要返回的是簡介明瞭的錯誤提示資訊
    • message:錯誤資訊文字
    • error_code:後端自己設定的在不同情況下發出的錯誤碼,前端會通過error_code的不同數字來判斷不同錯誤
    • request_url:當前請求的url
  • 通常錯誤可以分為:
    • 已知錯誤:如輸入url的引數校驗出現錯誤(即可以預知的錯誤)
    • 未知錯誤:程式潛在錯誤,如使用者輸入資料庫密碼時輸錯了(很難預知的錯誤)

捕獲錯誤

  • 肯定是要用try..catch語句來捕獲錯誤的,但錯誤的捕獲一定要先保證程式是在同步的順序下執行,非同步的時候錯誤捕捉不到

  • 那如何保證在一系列函式鏈式呼叫的時候讓每個函式都是同步的方式執行?答案顯示是利用promise、async、await來實現

  • 這裡可以使用面向切面程式設計(AOP),也就是在整個函式呼叫鏈最上面再寫一個函式來起始 捕捉整個鏈上出現的錯誤,並且在另一個檔案裡寫上對該錯誤的處理邏輯

  • 這裡是在中介軟體函式裡出現錯誤時丟擲異常,然後在 middlewares/exceptions.js 檔案裡統一寫處理錯誤邏輯,注意這個檔案裡的函式要:

    • 在最先被註冊,並且呼叫next
    • 一定要是async函式,且呼叫next時要寫await,都要保證程式是同步執行的
const catchError = async (ctx, next) => {
  try {
    // 確定下一個中介軟體函式要繼續執行下去
    await next()
  } catch (error) {
    if (error.error_code) {
    // 判斷一下是否是已知錯誤
      error.requestUrl = `${ctx.method} ${ctx.path}`
      ctx.body = {
        msg: error.msg,
        code: error.code,
        requestUrl: error.requestUrl
      }
      ctx.code = error.code
    }
  }
}

定義HttpException異常基類

  • 由於是要throw出一個錯誤,裡面要包含所有錯誤資訊,因此是要new一個Error類然後在裡面新增資訊的,現在將這個過程封裝成一個HttpException異常類,裡面專門存放有關的異常資訊
  • 這個類繼承js的原生Error類
class HttpException extends Error {
  constructor (msg="啊哦,出現了一個錯誤", error_code=10000, code=200) {
    // Class中的 super(),它在這裡表示父類的建構函式,用來新建父類的 this 物件
    super()
    this.msg = msg
    this.error_code = error_code
    this.code = code
  }
}
module.exports = HttpException
  • 後續在中介軟體函式裡要丟擲異常的時候,就引入這個檔案,並且丟擲HttpException型別的錯誤就好了,並傳遞引數

  • 可能有人會發現,欸這HttpException怎麼還是基類?因為後續各種特定的錯誤還要定製屬於自己的特殊類(很多哦),這樣在建立丟擲錯誤的時候都不太需要傳參了,例如引數錯誤類

class ParameterException extends HttpException {
  constructor (msg, error_code, code) {
    super()
    this.msg = msg || '引數錯誤'
    this.error_code = error_code || 10000
    this.code = 400 || code
  }
}

二、校驗器(Lin-Validator)

  • 由於koa真的非常精簡,所以很多功能都需要自己封裝檔案去實現,現在要用的校驗功能也一樣,這裡使用給定的校驗器Lin-Validator(七月老師自己寫的)
    • 起始這裡說到的校驗功能,就是類似於 傳參的時候要傳正整數 等情況,不做約束的話拿到所有引數沒法處理,傳參錯誤則報錯
  • 將lin-validator.js和util.js放到core資料夾下,在app下新增validator資料夾,裡面專門放和校驗有關的程式碼

建立校驗器

  • 在validator資料夾下寫validator.js檔案,先拿限制正整數的校驗器為例:
    • 建立PositiveIntegerValidator類,繼承LinValidator類(要引入LinValidator和Rule兩個類)
    • 建構函式裡寫上要校驗的規則和校驗失敗時的錯誤資訊
class PositiveIntegerValidator extends LinValidator {
  constructor () {
    super()
    this.id = [
      // 規則可以疊加,是 且 的關係
      new Rule('isInt', '需要正整數', {min: 1})
    ]
  }
}

使用校驗器

  • 在要使用的檔案裡,像這樣建立一個物件 呼叫validate函式將上下文資訊傳遞進去
const v =  await new PositiveIntegerValidator().validate(ctx)
  • 這裡的await和async的錯誤找了好久... 控制檯總是報錯說Promise的reject未進行處理,但明明已經寫了try...catch邏輯,原來還是因為try...catch只能捕獲同步的錯誤,這裡用await和async來讓程式碼同步執行
  • 注意在校驗的時候,如果要拿到請求body裡的資料,一定要在掛載中介軟體之前就引入koa-bodyparse,不然拿不到body(body為undefined)
  • 校驗器這裡返回的v物件,不加await的話返回的是Promise物件,加上了就能將promise的值求出來,這裡通過v可以直接獲取當前請求中的資訊,比如:
    • 要獲取body中傳過來的email,v.get('body.email')
    • 要獲取header中傳過來的token,v.get('header.token')

三、環境配置

  • 由於在伺服器程式設計時,錯誤資訊一旦被catch了就看不到了,也就沒辦法進行處理,所以除錯的時候還是要將error給throw出來
  • 但真正專案上線之後,錯誤是不能直接展現給使用者的,因此要區分生產環境和開發環境,根據不同的環境來選擇某些輸出操作進不進行
  • 這裡在根目錄下建立config資料夾裡面放著環境變數的相關檔案,dev是開發環境,prod是生產環境
  • 在init裡將環境變數匯入到全域性(global),要注意路徑的問題(這裡又再一次用到了process.cwd()函式來獲取當前的絕對路徑)
  • 從此在每一個輸出除錯資訊的地方都先判斷當前是在什麼環境下執行