服務端json引數校驗神器Json Schema
目錄
json簡介
JSON(JavaScript Object Notation) 是一種輕量級的資料交換格式。 易於人閱讀和編寫。同時也易於機器解析和生成。 它基於JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一個子集。 JSON採用完全獨立於語言的文字格式,但是也使用了類似於C語言家族的習慣(包括C, C++, C#, Java, JavaScript, Perl, Python等)。 這些特性使JSON成為理想的資料交換語言。 舉個栗子:
{ "an": [ "arbitrarily" , "nested" ], "data": "structure" }
服務端校驗引數需求分析
引數檢測是伺服器端拿到引數的第一步操作,後續的一系列操作都完全信任資料的可靠安全性。可以準確的說,前端的引數校驗是為了適當的保護服務端,為了更好的使用者體驗,但服務端萬萬不可信任前端傳遞的引數,所有的引數一定要進行校驗,確保輸入資料是合法正確性,這是服務端程式設計師的基本準則。常見的引數檢驗包括非空,最大長度檢測,大小檢測等等。應用在執行業務邏輯之前,必須通過校驗保證接受到的輸入資料是合法正確的,但很多時候同樣的校驗出現了多次,在不同的層,不同的方法上,導致程式碼冗餘,浪費時間。
json引數檢驗簡單而繁瑣方式
可以針對每個欄位,進行一系列的引數校驗,然後完成該json引數的檢驗。 舉個栗子:
dict = { "an": [ "arbitrarily", "nested" ], "data": "structure" }
if dict.get(data, '')=='':
return 'key data is no null'
if len(dict.get(data, ''))>30:
return "data is too long"
other verify
就是這樣不斷的針對每一個欄位進行校驗,確保輸入的資料的可靠、安全性。 缺點:很多時候同樣的校驗出現了多次,在不同的層,不同的方法上,導致程式碼冗餘,浪費時間。比如校驗是否為空,檢驗長度對大等等大量重複程式碼,效率很低。
Json Schema
相信用過flask-wtf的同學都會很喜歡這樣的校驗工具,自動對每個欄位進行,而且效率還很高,不用寫大量的重複程式碼,開發效率會得到大大的提升。反正我在用html的表單提交時都會使用flask-wtf,只能說,你用過一次就會愛上它。 那麼當前端提交的資料不是form而是json時怎麼辦呢?如果不想像上面一樣每個欄位或者每個資料都一一檢測,這時候你可以嘗試使用json schema,很像flask-wtf的一個開源框架,專門用來校驗json或者可以轉換成json的dict。
Json Schema 入門
先舉個栗子,大致說一下json schema怎麼用
>>> from jsonschema import validate
>>> # A sample schema, like what we'd get from json.load()
>>> schema = {
... "type" : "object",
... "properties" : {
... "price" : {"type" : "number"},
... "name" : {"type" : "string"},
... },
... }
>>> # If no exception is raised by validate(), the instance is valid.
>>> validate({"name" : "Eggs", "price" : 34.99}, schema)
>>> validate(
... {"name" : "Eggs", "price" : "Invalid"}, schema
... ) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...i
ValidationError: 'Invalid' is not of type 'number'
你可以使用validate來驗證輸入的json是否符合你需要的模式,如果不符合要求會丟擲ValidationError異常,ValidationError.message 會提示不符合哪個要求或哪個要求出錯,ValidationError.instance會提示是不符合該要求的欄位的內容或者例項是什麼。具體的schema 的表示式該怎麼寫後面會介紹到。
Json Schema 表示式
json schema的表示式在這裡就講點基本的好了,其實基本的已經夠用了,如果要想更深入學習,可以到官網json schema進行學習。 json schema能支援的型別有string,Numeric types,object,array,boolean,null。接下來會一一解釋。
string
舉個例子來說明:
{
"type": "string",
"minLength": 2,
"maxLength": 3
}
'A' is wrong, 'aa' is ok, 'aaa' is ok ,'aaaasf' is wrong
這表明它需要一個string欄位,其中string 有的屬性有:
- minLength
- maxLength
- pattern (正則表示式驗證)
- format (目前支援:”date-time”,”email”,”hostname”,”ipv4”,”ipv6”,”uri”, 如果沒有你需要的格式,你可以用pattern 的正則表示式來完成)
Numeric types
老慣例,上來舉個例子:
{
"type": "number",
"minimum": 0,
"maximum": 100,
"exclusiveMaximum": true
}
ok:
0
10
99
wrong:
-1
100
101
這表明它需要一個Numeric types欄位,其中number有的屬性有:
- type(integer,number)
- multipleOf(要求該數字是這個數的整數倍)
- minimum 、exclusiveMinimum 、maximum 、exclusiveMaximum (最大值、最小值以及是否包含該最大最小值)
object
在Python中,object類似於dict型別,”type”:”object”就是宣告需要dict類。舉個例子,就能大致理解了:
{
"type": "object",
"properties": {
"number": { "type": "number" },
"street_name": { "type": "string" },
"street_type": { "type": "string",
"enum": ["Street", "Avenue", "Boulevard"]
}
}
}
ok:
{ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue" }
{ "number": 1600, "street_name": "Pennsylvania" }
{ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue", "direction": "NW" }
wrong:
{ "number": "1600", "street_name": "Pennsylvania", "street_type": "Avenue" }
這表明它需要dict的型別。object具有的屬性有:
- additionalProperties: 是否可以需要多其它的字典關鍵字。例如”additionalProperties”: { “type”: “string” }表明允許多的關鍵字必須是string型別,additionalProperties:false則表明不允許多其它的關鍵字。
- required:表明必須要有該關鍵字,例如 “required”: [“number”, “street_name”] 必須要有這number和street_name這兩個key。
- minProperties,maxProperties 表明這個字典最少、最多擁有多少個keys。
- dependencies:依賴屬性,例如 “dependencies”: { “credit_card”:[“billing_address”]}表明有credit_card是必須有billing_address這個key。
- patternProperties:key為正則表示式。例如”patternProperties”: {“^S_”: { “type”: “string” }},需要S_開頭key
array
array對應python裡的[],可以是列表也可以是原組,舉個例子:
{
"type": "array",
"items": [
{
"type": "number"
},
{
"type": "string"
},
{
"type": "string",
"enum": ["Street", "Avenue", "Boulevard"]
},
{
"type": "string",
"enum": ["NW", "NE", "SW", "SE"]
}
]
}
則
[1600, "Pennsylvania", "Avenue", "NW"] is ok
“Drive” is not one of the acceptable street types:
[24, "Sussex", "Drive"] is wrong
This address is missing a street number
["Palais de l'Élysée"] is wrong
It’s okay to not provide all of the items:
[10, "Downing", "Street"] is ok
And, by default, it’s also okay to add additional items to end:
[1600, "Pennsylvania", "Avenue", "NW", "Washington"] is ok
item具有的屬性是: - additionalItems :該additionalItems關鍵字控制是否有效有超出了所定義的陣列中的其他專案items。在這裡,我們將重用上面的示例模式,但設定 additionalItems為false,這會導致不允許陣列中的額外項。 - minItems、maxItems:minItems和 maxItems關鍵字指定陣列的長度。每個關鍵字的值必須是非負數。 - Uniqueness:模式可以確保陣列中的每個項都是唯一的。只需將uniqueItems關鍵字設定為true。
boolean
在Python中,“boolean”類似於bool。請注意,在JSON中, true它false是小寫的,而在Python中它們是大寫的(True和False)。 舉個例子:{ "type": "boolean" }
OK:
true
false
wrong:
"true"
0
布林型別只匹配兩個特殊值:true和 false。請注意,架構不接受評估為true或的值false,例如1和0
null
在Python中,null類似於None。{ "type": "null" }
ok:
null
wrong:
false
0
""
null型別通常用於表示缺失值。當模式指定a type時null,它只有一個可接受的值:null
實戰演習
USERS_SCHEMA = {
"type": "object",
"required": ["users"],
"properties": {
"users": {
"type": "array",
"items": {
"type": "object",
"required": ["name", "email"],
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 30,
},
"primary_sector": {
"type": "string",
"maxLength": 60,
},
"second_sector": {
"type": "string",
"maxLength": 60,
},
"tertiary_sector": {
"type": "string",
"maxLength": 60,
},
"email": {
"type": "string",
"minLength": 1,
"maxLength": 60,
"pattern": '^([a-zA-Z0-9]+[-_.]?)*[a-zA-Z0-9][email protected]([a-zA-Z0-9]+[-_.]?)*'\
'[a-zA-Z0-9]+\.[a-zA-Z]{2,6}$',
}
},
},
},
},
}
用來對應的符合要求的json是
{
"users":{
[{
"name": "hello",
"primary_sector": "一級部門",
"second_sector": "二級部門",
"tertiary_sector": "三級部門",
"email" : "郵箱",
}],
[{
"name": "hello1",
"primary_sector": "一級部門",
"second_sector": "二級部門",
"tertiary_sector": "三級部門",
"email" : "郵箱",
}],
}
}
可以一次性驗證json裡含有多有輸入使用者資料的json。
總結
Json Schema給人感覺比較像在寫json正則表示式,按著基本的幫助文件和知道,通過組合基本就能寫出你需要的json驗證格式,然後驗證的時候只需要一句validate()就可以了,如果驗證失敗會丟擲異常。建議以後驗證json格式還是習慣使用這種驗證方式,會大大提高開發效率。