拒當前端黑鍋俠!用「jsonschema」來驗證後端回傳的 json 資料結構

這篇文章寫於 2021 年,文中的工具與版本可能已經更新,實作時請以官方文件為準。

本文目錄
  1. 一、「jsonschema」是什麼?
  2. 可透過下面指令安裝:
  3. 二、「jsonschema」有哪些優點?
  4. 三、「jsonschema」驗證範例
  5. 簡單範例:
  6. 複雜點的範例:
  7. 常見的陣列範例:
  8. 四、「jsonschema」其他功能

開發網頁前端時,會需要用到後端回傳的 api 數據來渲染頁面,如果雙方依照需求規格書好好實作會天下太平;但在現實生活中總是會有例外發生,如果後端傳給前端時的資料格式不夠穩定,前端在渲染的時候就可能會出錯。遇到這種狀況時,「jsonschema」套件就是你的救星!

圖片來源:寶寶不說貼圖
圖片來源:寶寶不說貼圖
一、「jsonschema」是什麼?
二、「jsonschema」有哪些優點?
三、「jsonschema」驗證範例
  ➤ 簡單範例
  ➤ 複雜點的範例
  ➤ 常見的陣列範例
四、「jsonschema」其他功能

一、「jsonschema」是什麼?

如果你常常被後端回傳的 json 資料整,那這個套件你一定要學習;他是一個專們用來驗證 json 格式是否符合預期的套件。

可透過下面指令安裝:

npm i jsonschema

二、「jsonschema」有哪些優點?

  1. 可以詳細定義要驗證的 json 格式,包含內部所有參數的型態(string/number/array/object)
  2. 驗證結果會用 true/false 回傳
  3. 發生 error 時,會很明確的指出是在哪個位置出錯
  4. 可以客製化回傳錯誤的訊息
  5. 學習及使用非常直覺

三、「jsonschema」驗證範例

簡單範例:

我們要驗證 「4」 這個 instance 的 type 是否為 number。

var Validator = require('jsonschema').Validator;
var v = new Validator();
var instance = 4;
var schema = {"type": "number"};
console.log(v.validate(instance, schema));

原始碼:jsonschema_simple.js(GitHub Gist)

程式執行結果為:

ValidatorResult {
  instance: 4,
  schema: { type: 'number' },
  options: {},
  path: [],
  propertyPath: 'instance',
  errors: [],
  throwError: undefined,
  throwFirst: undefined,
  throwAll: undefined,
  disableFormat: false
}

原始碼:jsonschema_simple_result.js(GitHub Gist)

解說:因為 「4」的確是「number」的type,所以在「errors」參數的結果為 empty array。

複雜點的範例:

  1. 建立「驗證地址格式」的「addressSchema」
  2. 建立「驗證個人格式」的「schema」,並且讓個人格式的「address」關聯到「addressSchema」
  3. 建立一個人的物件(p),填入個人訊息
  4. 使用「jsonschem」提供的「Validator」 function 來驗證物件(p)是否符合個人格式(addressSchema)
var Validator = require('jsonschema').Validator;
var v = new Validator();
 
// Address, to be embedded on Person
var addressSchema = {
  "id": "/SimpleAddress",
  "type": "object",
  "properties": {
    "lines": {
      "type": "array",
      "items": {"type": "string"}
    },
    "zip": {"type": "string"},
    "city": {"type": "string"},
    "country": {"type": "string"}
  },
  "required": ["country"]
};
 
// Person
var schema = {
  "id": "/SimplePerson",
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "address": {"$ref": "/SimpleAddress"},
    "votes": {"type": "integer", "minimum": 1}
  }
};
 
var p = {
  "name": "Barack Obama",
  "address": {
    "lines": [ "1600 Pennsylvania Avenue Northwest" ],
    "zip": "DC 20500",
    "city": "Washington",
    "country": "USA"
  },
  "votes": "lots"
};
 
v.addSchema(addressSchema, '/SimpleAddress');
console.log(v.validate(p, schema));

原始碼:jsonschema_complex.js(GitHub Gist)

程式執行結果為:

ValidatorResult {
  instance: {
    name: 'Barack Obama',
    address: {
      lines: [Array],
      zip: 'DC 20500',
      city: 'Washington',
      country: 'USA'
    },
    votes: 'lots'
  },
  schema: {
    id: '/SimplePerson',
    type: 'object',
    properties: { name: [Object], address: [Object], votes: [Object] }
  },
  options: {},
  path: [],
  propertyPath: 'instance',
  errors: [
    ValidationError {
      path: [Array],
      property: 'instance.votes',
      message: 'is not of a type(s) integer',
      schema: [Object],
      instance: 'lots',
      name: 'type',
      argument: [Array],
      stack: 'instance.votes is not of a type(s) integer'
    }
  ],
  throwError: undefined,
  throwFirst: undefined,
  throwAll: undefined,
  disableFormat: false
}

原始碼:jsonschema_complex_result.js(GitHub Gist)

解說:因為我們在「shemca」這個變數下設定「vote」需要為「integer」的type;但是我們傳入的「vote」內容為「lots」的字串,所以「errors」會回傳一個「is not of a type(s) integer」的錯誤訊息。

常見的陣列範例:

後端 api 其實很常回傳這類陣列的資訊,但因為各種歷史業障;在版本更新時,舊資料在新欄位的資料往往是空的,這時驗證回傳資料的動作就很重要了!

下面是模擬後端回傳「名字」的陣列,用「arraySchema」做格式驗證:

var Validator = require('jsonschema').Validator;
var v = new Validator();

var arraySchema = {
    "type": "array",
    "items": {
        "properties": {
            "name": { "type": "string" },
            "lastname": { "type": "string" }
        },
        "required": ["name", "lastname"]
    }
}
var arrayName = [
    {
        "name": "寶寶",
        "lastname": "不說"
    },
    {
        "name": "鹿尼",
        "lastname": 123
    },
    {
        "name": null,
        "lastname": "好想兔"
    },
    {
        "name": "鹿尼"
    },
]
console.log(v.validate(arrayName, arraySchema));

原始碼:jsonschema_array.js(GitHub Gist)

程式執行結果為:

ValidatorResult {
  instance: [
    { name: '寶寶', lastname: '不說' },
    { name: '鹿尼', lastname: 123 },
    { name: null, lastname: '好想兔' },
    { name: '鹿尼' }
  ],
  schema: { type: 'array', items: { properties: [Object], required: [Array] } },
  options: {},
  path: [],
  propertyPath: 'instance',
  errors: [
    ValidationError {
      path: [Array],
      property: 'instance[1].lastname',
      message: 'is not of a type(s) string',
      schema: [Object],
      instance: 123,
      name: 'type',
      argument: [Array],
      stack: 'instance[1].lastname is not of a type(s) string'
    },
    ValidationError {
      path: [Array],
      property: 'instance[2].name',
      message: 'is not of a type(s) string',
      schema: [Object],
      instance: null,
      name: 'type',
      argument: [Array],
      stack: 'instance[2].name is not of a type(s) string'
    },
    ValidationError {
      path: [Array],
      property: 'instance[3]',
      message: 'requires property "lastname"',
      schema: [Object],
      instance: [Object],
      name: 'required',
      argument: 'lastname',
      stack: 'instance[3] requires property "lastname"'
    }
  ],
  throwError: undefined,
  throwFirst: undefined,
  throwAll: undefined,
  disableFormat: false
}

原始碼:jsonschema_array_result.js(GitHub Gist)

解說:除了第一個「寶寶不說」外,我們每個 instance 其實都故意給他錯誤的資訊,來確認這個格式驗證是否生效,錯誤結果在「errors」裡面呈現的很完美。

四、「jsonschema」其他功能

請參考官方網站,上面提供更多詳實的範例。

Talk

留言討論

留言載入中…