跳到主要內容

資料驗證好幫手 - JSON Schema Validator

資料驗證好幫手 - JSON Schema Validator

文件大綱:

JSON schema 簡介及作用

直接先看一下JSON schema的範例,暖個身
{
   "title":"產品資訊",
   "description":"產品包含商品標號、商品名稱及價錢",
   "type":"object",
   "properties":{
      "pid":{
         "description":"產品的id,是product id的縮寫",
         "type":"integer"
      },
      "name":{
         "description":"產品名稱",
         "type":"string",
         "maxLength": 10
      },
      "price":{
         "type":"number",
         "exclusiveMinimum":0
      }
   },
   "required":[
      "pid",
      "name",
      "price"
   ]
}
JSON Schema就是JSON資料結構的規範,規定一個JSON資料該有哪些資料,包含規定型態、大小範圍等。JSON Schema有官方規範,可以在http://json-schema.org上查看,目前已出到draft-07。
這份文件要介紹的 Ajv (Another JSON Schema Validator) 套件會支援 draft-04/06/07 這幾個版本。
JSON Schema 功用如下:
  • 用於構建人機可讀的文件:
    • JSON Schema 可以讓系統讀取,同時也是一個讓人一目瞭然的文件檔,一舉兩得。
  • 用於生成模擬資料:
    • 有了JSON Schema,可以自動生成符合規定的資料讓測試程式使用。
  • 用於資料驗證:
    • 不需要再寫程式來判斷資料長度跟內容了,所有的邏輯都可以移植到JSON Schema中維護,最後我們會使用ajv這個工具來使用JSON schema進行驗證。

JSON schema 詳細介紹

網路上已經有文件把9成的功能介紹完了,廢話不多說直接看JSON Schema 辭典吧!
其他教學文件:

JSON Schema範例

比起看文件,大家更愛範例,所以在這邊提供幾個範例,剩下的可以去上面的文件看。
資料是數字(在這邊成為n), 3 <= n < 9 且 n要是3的倍數
{ "type": "number", "minimum": 3, "exclusiveMaximum": 9, "multipleOf": 3 }
  • exclusiveMinimum與exclusiveMaximum在draft-04以前是boolean,但在draft-06以後則變成數字。

字串長度為5~10
{ "type": "string", "minLength": 5, "maxLength": 10, }

符合正規表示式的字串,輸入的字串必須符合身分證字號的格式
{ "title": "ID card number", "type": "string", "pattern": "^[A-Z]{1}[0-9]{9}$" }

符合ipv4的的字串,還有email跟date-time等format
{ "type": "string", "format":"ipv4" }

陣列長度2~5,而且必須都是數字,並不得有重複的數字
{ "type": "array", "items": { "type": "number" }, "minItems": 2, "maxItems": 5, "uniqueItems": true }

陣列內只能有指定的[ 整數 , DC或Marvel ],不可以有其他東西
{ "type": "array", "items": [ { "description": "粉絲的年紀", "type": "integer" }, { "type": "string", "enum": ["DC", "Marvel"] } ], "additionalItems": false }
  • 在沒有additionalItems的情況下,[ 整數 , DC或Marvel , 這邊要放多少東西都可以 ],additionalItems可以指定type等相關資訊,代表多出來的陣列內容必須符合additionalItems的限制。

Object物件中,最多只能有5個property,一定要有nickname,當有carMileage時,就一定要有carBrand,反之則不需要,carBrand只能填bmw或benz,除了原本定義的property之外,其他的property都得是string型態。
{ "type":"object", "maxProperties":5, "properties":{ "nickname":{ "type": "string" }, "carBrand":{ "enum": ["benz", "bmw"] }, "carMileage":{ "type":"number" } }, "required": ["nickname"], "dependencies":{ "carMileage": ["carBrand"] }, "additionalProperties":{ "type":"string" } }
oneOf代表 只能 符合其中一項,也就是這個數字必須是5或3的倍數,但不能是15的倍數,另外還有anyOf跟allOf以及not可以使用
{ "type": "number", "oneOf": [ { "multipleOf": 5 }, { "multipleOf": 3 } ] }

使用refs引用定義好的definitions,先定義好正整數後直接引用
{ "type": "array", "items": { "$ref": "#/definitions/positiveInteger" }, "definitions": { "positiveInteger": { "type": "integer", "exclusiveMinimum": 0 } } }
definitions相當於引用自己建立的變數的感覺,厲害的是$refs可以引用外部檔案的schema,像是json-schema.org提供的官方範例geo.json
{ "$ref": "http://json-schema.org/geo.json#" }

  • contains - { "contains": { "type": "integer" } } means that any array with at least one integer, any non-array is matched
  • propertyNames - property名稱必須符合規範
  • const - 比對不同property的值是否相同
if-else的範例
{ "type": "integer", "minimum": 1, "maximum": 1000, "if": { "minimum": 100 }, "then": { "multipleOf": 100 }, "else": { "if": { "minimum": 10 }, "then": { "multipleOf": 10 } } }
符合規定的資料: 1, 5, 10, 20, 50, 100, 200, 500, 1000
不符合規定的資料:
  • -1, 0 (<1 li="">
  • 2000 (>1000)
  • 11, 57, 123 (any number with more than one non-zero digit)
  • non-integers

$schema與$id到底是什麼?

The “$schema” keyword
  • $schema代表你寫的JSON Schema文件遵循的規範是哪一個版本。最新版的draft的schema是http://json-schema.org/schema#,目前如果你打開來看的話會是draft-07,如果想指定成draft-06版的話可以寫成http://json-schema.org/draft-06/schema#
The “$id” keyword - schemaId
  • 官方文件提到的schemaId
  • $id 是拿來定義這個schema的獨立編號,必須是uri。
    http://json-schema.org/example/geo.json#中的$id就是"http://json-schema.org/geo",別人使用$ref想遠端過來取用這個schema就是對應這個$id,object中的property也可以有$id,有如path的概念
  • 來看以下範例,如果想取用內部的定義則用#/definitions/B,取用外部的話則用http://example.com/other.json,詳細教學看這邊
{ "$id": "http://example.com/root.json", "definitions": { "A": { "$id": "#foo" }, "B": { "$id": "other.json", "definitions": { "X": { "$id": "#bar" }, "Y": { "$id": "t/inner.json" } } }, "C": { "$id": "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f" } } }

JSON Schema Validator

JSON Schema Validator就是專門來驗證JSON的工具,將要比對的資料拿去跟schema比對,馬上就知道資料符不符合規定,並且能夠得知是哪個資料的哪個環節不符合規定,相當方便,目前星星數最多,而且還有在維護的就屬Ajv(Another JSON Schema Validator)莫屬了,以前我用過的JSON Schema Validator是tv4。
另外有一個react-jsonschema-form,只要在
物件中放入schema跟data,它就能自動生成Bootstrap表單,它的介紹語是這樣寫的"A simple React component capable of building HTML forms out of a JSON schema and using Bootstrap semantics by default."
react-jsonschema-form 程式範例
import React, { Component } from "react"; import { render } from "react-dom"; import Form from "react-jsonschema-form"; const schema = { title: "Todo", type: "object", required: ["title"], properties: { title: {type: "string", title: "Title", default: "A new task"}, done: {type: "boolean", title: "Done?", default: false} } }; const log = (type) => console.log.bind(console, type); render(( <Form schema={schema} onChange={log("changed")} onSubmit={log("submitted")} onError={log("errors")} /> ), document.getElementById("app"));
但因為綁定Bootstrap有一點死,所以這邊還是選用比較有彈性的ajv。

Ajv: Another JSON Schema Validator

注意:完整的文件可以直接去github上看,這邊會提到比較常用到的內容,或是我有用過的內容。[npm ajv 連結]
這邊有模擬在Nodejs上面執行ajv的連結,可以進去試用看看。
我們已經學會了JSON Schema了,現在直接使用Ajv來比對schema跟data就能驗證資料了,看看下面的範例吧!符合條件的話ajv.validate就會回傳true,反之則false
var ajv = Ajv(); var schema = { "type": "number", "oneOf": [ { "multipleOf": 5 }, { "multipleOf": 3 } ] }; var data10 = 10; var data15 = 15; console.log(ajv.validate(schema, data10)); // true console.log(ajv.validate(schema, data15)); // false

如果想知道錯在哪裡,我們就輸出ajv.errors,也是直接看下面範例。
我們在字串長度應該是5的schema中輸入長度是4的字串
var ajv = Ajv(); var schema = { "type": "string", "minLength": 5 }; var data = "four"; var valid = ajv.validate(schema, data); if (!valid) console.log(ajv.errors);
資料長度不符合規定時Ajv回傳的錯誤訊息格式如下,大致上看得懂,我們去看下個範例後再解說這些參數是幹嘛的。
[ { "keyword":"minLength", "dataPath":"", "schemaPath":"#/minLength", "params":{ "limit":5 }, "message":"should NOT be shorter than 5 characters" } ]

這次的範例有兩個錯誤,分別是2016-12-99不符合date格式,以及fruit欄位應該要是字串卻填入了數字,如果要顯示超過一個以上的error就必須在Options欄位加上{ allErrors: true },否則它只會回傳第一個error。
var ajv = new Ajv({ allErrors: true }); var schema = { "type": "object", "properties": { "purchaseDate": { "format": "date" }, "foods": { "type": "object", "properties": { "fruit": { "type": "string" } }, }, } }; var data = { "purchaseDate": "2016-12-99", "foods": { "fruit": 5566 } }; var valid = ajv.validate(schema, data); if (!valid) console.log(ajv.errors);
回傳了兩個錯誤
[ { "keyword":"format", "dataPath":".purchaseDate", "schemaPath":"#/properties/purchaseDate/format", "params":{ "format":"date" }, "message":"should match format 'date'" }, { "keyword":"type", "dataPath":".foods.fruit", "schemaPath":"#/properties/foods/properties/fruit/type", "params":{ "type":"string" }, "message":"should be string" } ]
想要知道error object的架構,可以看Validation errors,我把裡面的部分內容節錄在下方。
  • keyword: validation keyword.
  • dataPath: the path to the part of the data that was validated. By default dataPath uses JavaScript property access notation (e.g., “.prop[1].subProp”). When the option jsonPointers is true (see Options) dataPath will be set using JSON pointer standard (e.g., “/prop/1/subProp”).
  • schemaPath: the path (JSON-pointer as a URI fragment) to the schema of the keyword that failed validation.
  • params: the object with the additional information about error that can be used to create custom error messages. [params文件]
  • message: the standard error message (can be excluded with option messages set to false).

API列表 - 接在ajv後面的api都寫在這。
.compile(Object schema)
validating function and cache the compiled schema for future use,比起每次都用ajv.validate()的執行速度還快,官方文件也說這是The fastest validation call

.errorsText([Array errors [, Object options]])
ajv.errorsText(ajv.errors)這樣的寫法可以把error object轉換成一個錯誤訊息字串,
remote schemas have to be added with addSchema or compiled to be available

.addFormat(String name, String|RegExp|Function|Object format)
使用addFormat定義客製化的format,可以參考下面的範例
var ajv = new Ajv().addFormat('cellphone', '^09[0-9]{2}-[0-9]{3}-[0-9]{3}$'); var schema = { "format": "cellphone" }; var data = '0912-345-678'; // format have to be 09XX-XXX-XXX var validate = ajv.compile(schema); console.log(validate(data)); // true

.addKeyword(String keyword, Object definition)
自訂keyword
ajv.addKeyword('range', { type: 'number', compile: function (sch, parentSchema) { var min = sch[0]; var max = sch[1]; return parentSchema.exclusiveRange === true ? function (data) { return data > min && data < max; } : function (data) { return data >= min && data <= max; } } }); var schema = { "range": [2, 4], "exclusiveRange": true }; var validate = ajv.compile(schema); console.log(validate(2.01)); // true console.log(validate(3.99)); // true console.log(validate(2)); // false console.log(validate(4)); // false

.addSchema(Array|Object schema [, String key])
compile跟addSchema非常像,其實我也分不太出來為啥要分兩個,
直接傳schema
var validate = jv.addSchema(schema)
傳schema並給予一個key值
var valid = ajv.addSchema(schema, 'mySchema').validate('mySchema', data);
除了addSchema的話,也可以在宣告Ajv時直接assignschemas
var ajv = new Ajv({schemas: [schema1, schema2]});
schemas: an array or object of schemas that will be added to the instance. In case you pass the array the schemas must have IDs in them

.addMetaSchema(Array|Object schema [, String key])
  • 這裡有官方對於Meta Schema的介紹,meta-schema是就是JSON Schema的範本,目前最新的meta-schema是draft-07。
至於為何要addMetaSchema我還不是非常清楚

.getSchema(String key)
只透過uri來取得Schema
ajv.getSchema('http://example.com/schemas/schema.json')

Jquery-Validation - 直得參考的的套件

我認為JSON-validator必須要有良好的message回傳機制才實用,所以我找到了一個我個人認為設計蠻好的套件jquery-validation,因為是jquery所以不太適合套用到reactjs專案,但是可以試著引用它的設計模式來打造一個適合自己專案的validator。可以看看Youtube上的jQuery Validation Plugin 播放清單來了解如何使用。
基本上它就是將驗證的rule與要回傳的message分成兩個property互相對應

留言

這個網誌中的熱門文章

2017通訊大賽「聯發科技物聯網開發競賽」決賽團隊29強出爐!作品都在11月24日頒獎典禮進行展示

2017通訊大賽「聯發科技物聯網開發競賽」決賽團隊29強出爐!作品都在11月24日頒獎典禮進行展示 LIS   發表於 2017年11月16日 10:31   收藏此文 2017通訊大賽「聯發科技物聯網開發競賽」決賽於11月4日在台北文創大樓舉行,共有29個隊伍進入決賽,角逐最後的大獎,並於11月24日進行頒獎,現場會有全部進入決賽團隊的展示攤位,總計約為100個,各種創意作品琳琅滿目,非常值得一看,這次錯過就要等一年。 「聯發科技物聯網開發競賽」決賽持續一整天,每個團隊都有15分鐘面對評審團做簡報與展示,並接受評審們的詢問。在所有團隊完成簡報與展示後,主辦單位便統計所有評審的分數,並由評審們進行審慎的討論,決定冠亞季軍及其他各獎項得主,結果將於11月24日的「2017通訊大賽頒獎典禮暨成果展」現場公佈並頒獎。 在「2017通訊大賽頒獎典禮暨成果展」現場,所有入圍決賽的團隊會設置攤位,總計約為100個,展示他們辛苦研發並實作的作品,無論是想觀摩別人的成品、了解物聯網應用有那些新的創意、尋找投資標的、尋找人才、尋求合作機會或是單純有興趣,都很適合花點時間到現場看看。 頒獎典禮暨成果展資訊如下: 日期:2017年11月24日(星期五) 地點:中油大樓國光廳(台北市信義區松仁路3號) 我要報名參加「2017通訊大賽頒獎典禮暨成果展」>>> 在參加「2017通訊大賽頒獎典禮暨成果展」之前,可以先在本文觀看各團隊的作品介紹。 決賽29強團隊如下: 長者安全救星 可隨意描繪或書寫之電子筆記系統 微觀天下 體適能訓練管理裝置 肌少症之行走速率檢測系統 Sugar Robot 賽亞人的飛機維修輔助器 iTemp你的溫度個人化管家 語音行動冰箱 MR模擬飛行 智慧防盜自行車 跨平台X-Y視覺馬達控制 Ironmet 菸消雲散 無人小艇 (Mini-USV) 救OK-緊急救援小幫手 穿戴式長照輔助系統 應用於教育之模組機器人教具 這味兒很台味 Aquarium Hub 發展遲緩兒童之擴增實境學習系統 蚊房四寶 車輛相控陣列聲納環境偵測系統 戶外團隊運動管理裝置 懷舊治療數位桌曆 SeeM智能眼罩 觸...
你掛65號,到醫院發現才看到7號怎麼辦?這家公司想出好方法,現值50億美金 創新拿鐵   2018-12-19 17:00 209801   人氣       現正熱映中 熱門文章 你掛65號,到醫院發現才看到7號怎麼辦?這家公司想出好方法,現值50億美金 創新拿鐵 2018-12-19 17:00 防堵非洲豬瘟的大功臣!超萌檢疫犬敬業值班「精彩故事多」,奇葩陸客最令人哭笑不得… 中央社 2018-12-17 11:41 機票錢根本花的冤枉⋯又貴、又擠、又騙!世界「六大最雷景點」,不要再輕信網路「照騙」了! 周佳萱 2018-12-14 13:58 活活燒死193人!列車駕駛叫大家「坐好別動」卻拔鑰匙逃跑…韓「大邱地鐵縱火案」離譜內幕 黃瑜敏 2018-12-18 12:34 火鍋的靈魂:湯底的秘密⋯國宴名廚:只要「這樣做」,清湯白水也能煮出好滋味! 食力foodNEXT 2018-12-16 10:00 駭人實驗!失散19年三胞胎感人重逢,意外揭穿「失散真相」是一樁慘無人道的心理實驗… 黃瑜敏 2018-12-17 15:04 誰理你們!從SARS爆發冷血嗆聲,到非洲豬瘟疫情不通報…看見「兩岸一家親」不過是場笑話 潘渝霈   蔡佳妘 2018-12-18 17:47 向老闆提升遷遭拒!3年後他當上主管才看透真相:太認真、太專業、太忠誠都是升遷阻礙... 洪雪珍 2018-12-19 14:34 醫院藥師月薪5萬起跳、只要包藥發藥很好賺?過來人痛揭醫院「領藥得來速」的黑暗真相 時報出版 2018-12-17 14:19 男孩與美洲豹玩耍照片竄紅網絡引發深思 BBC中文網 2018-12-17 12:24 更多文章   熱門分享 你掛65號,到醫院發現才看到7號怎麼辦?這家公司想出好方法,現值50億美金 創新拿鐵 2018-12-19 17:00 ...

HTTP headers設置,讓網站更安全避免淪為挖礦機

https://meet.bnext.com.tw/articles/view/42595?utm_campaign=5741400&utm_source=SendPulse&utm_medium=push HTTP headers設置,讓網站更安全避免淪為挖礦機 梅干 2018/04/17 最近一位工程師好友,突然丟一個檢測平台的分數,起初梅干以為是SSL檢測,就進入測試一下,卻發現到,怎分數相當的低,最後研究了一下,原來網站不是有加入SSL憑證就代表安全無誤,就在今年的六月KeyCDN在官方部落格,發表了一篇關於站點HTTP headers配置,分別為X-XSS-Protection、X-Frame-Options、X-Content-Type-Options Content-Security-Policy、Strict-Transport-Security、Public-Key-Pins這六項進行安全的設定,讓網站更加的安全,不會淪為挖礦主機。 而要修正這個HTTP headers的配置,一點也不難,只需將設定參數加入.htaccess文件後,立即就可讓網站更加的安全,並且也可遠離挖礦惡意程式入侵主機,因此身為網站管理員,這個絕不容輕乎,因此也趕快來檢測看看,你的網站是否安全,以及要如何將網站進行修正。 HTTP headers檢測網站 網站名稱: securityheaders.io  網站連結: https://securityheaders.io Step1 首先,開啟.htaccess檔案後,並加入下方的參數設定。 # HTTP security settings start Header set Strict-Transport-Security: max-age=2592000; Header set X-Frame-Options: SAMEORIGIN Header set Referrer-Policy: no-referrer Header set X-XSS-Protection: "1; mode=block" Header set X-Content-Type-Options: nosniff # HTTP security settings end ...