作為一個后端開發(fā)人員砍艾,永遠不要相信你的用戶輸入,也不要相信自己~所以巍举,參數校驗是一個非常重要的環(huán)節(jié)脆荷,千萬千萬不要忽視。
最近也涉及到很多需要嚴格參數校驗的接口開發(fā)工作懊悯,之前使用過很多方式進行參數校驗蜓谋,主要有以下兩種:
- 在接口內部直接依次if else校驗,這種只針對比較簡單單一的接口炭分;
- 自己針對不同參數校驗需求桃焕,定制的參數校驗方法,在相應接口內調用捧毛。但是寫多了會發(fā)現(xiàn)重復代碼較多观堂;
- 使用marshmallow的序列化類進行校驗,使用較為方便和清晰呀忧,但是他主要是針對模型類序列化使用的师痕,我大多時候只是校驗json參數而已。
然后了解到了今天寫的主角:jsonschema庫而账,專為json數據校驗而生胰坟,更加的靈活,使用字典配置化的方式定義校驗泞辐。
一笔横、 安裝方式: pip install jsonschema
若不想自己定義schema,可在https://jsonschema.net/home輸入示例json在線生成schema哦铛碑!
二狠裹、官方簡單示例
這里先直接上一個官方的示例代碼,了解下jsonschema的使用方式:
>>> 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(instance={"name" : "Eggs", "price" : 34.99}, schema=schema)
>>> validate(
... instance={"name" : "Eggs", "price" : "Invalid"}, schema=schema,
... ) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
ValidationError: 'Invalid' is not of type 'number'
這里再做個簡單名詞解釋:
- schema變量就是定義的具體參數校驗配置汽烦;
-
type
指定該層級參數類型涛菠,object
為json對象,array
為數組撇吞,number
為數字包含小數俗冻,string
為字符串,integer
為整數... -
properties
指定該層級具體字段名及屬性限制牍颈; -
required
指定該層級必須的字段名迄薄,未在其中的字段可缺失; -
validate
方法即調用參數驗證的方式煮岁,指定instance
待校驗的json對象讥蔽,schema
指定使用的校驗配置涣易;
...
下面將針對兩個實例做使用代碼展示。
三冶伞、需求實例
準備了兩個實例進行學習新症,明白后基本能夠學會該校驗方式的基本使用。
-
需求一:
需要對下列結構的用戶參數進行校驗响禽,user_id
為大于0的整數徒爹;user_name
為字符串;age
為一定范圍內的整數芋类;other
字段為嵌套json的非必須字段隆嗅,但若other字段存在,則hobby
字段為字符串且必須侯繁,height
字段為數值型非必須胖喳。
{
"user_id": 123456,
"user_name": "John",
"age": 12,
"other": {
"hobby": "swimming",
"height": 170.3
}
}
- 需求二:需要對下列結構的公司參數進行校驗,model字段為字符串巫击;count字段為不小于0的整數禀晓;data為數組結構,內嵌json數據坝锰,內嵌字段company_name與cust_uid不可缺失,value值為字符串重付,不為空且小于一定長度顷级。
{
"model": "st",
"count": 4,
"data": [
{"company_name": "測試公司1", "cust_uid": "asd4a676762jjhj"},
{"company_name": "測試公司2", "cust_uid": "andfu58jkskjds3"}
]
}
四、代碼結構
為了增加代碼的復用性和可讀性确垫,我將模擬項目內代碼規(guī)范弓颈,使用分層代碼結構+裝飾器進行參數校驗功能實現(xiàn),代碼結構如下:
- app.py:模擬接口文件删掀,其內包含接收json參數且需要進行參數校驗的接口函數翔冀;
- decorators.py:用于存放項目內的裝飾器函數,這里只有一個參數校驗裝飾器披泪;
- schema.py:定義各類參數校驗的配置字典纤子,也應該算是使用
jsonschema
庫的核心
五、代碼示例
以下開始直接分享針對兩個需求的參數校驗代碼款票,一定注意各個字段名含義及層級關系控硼,可對應上文中的需求介紹來了解每行是在做什么。
schema.py
# 需求1的用戶校驗schema字典定義
schema_user = {
"type": "object",
"required": ["user_id", "user_name", "age"],
"properties": {
"user_id": {
"type": "integer",
"minimum": 1
},
"user_name": {
"type": "string",
"minLength": 1,
"maxLength": 20
},
"age": {
"type": "integer",
"minimum": 1,
"maximum": 120
},
"other": {
"type": "object",
"required": ["hobby"],
"properties": {
"hobby": {"type": "string"},
"height": {"type": "number"}
}
}
}
}
# 需求2的公司校驗schema字典定義
schema_company = {
"type": "object",
"required": ["data", "count"],
"properties": {
"model": {
"type": "string"
},
"count": {
"type": "integer",
"minimum": 1
},
"data": {
"type": "array",
"items": {
"type": "object",
"required": ["company_name", "cust_uid"],
"properties": {
"company_name": {
"type": "string",
"minLength": 1
},
"cust_uid": {
"type": "string",
"minLength": 10,
"maxLength": 100
}
}
}
}
}
}
-
decorators.py
該裝飾器接收一個schema參數艾少,即上面定義的shema校驗字典對象卡乾,可針對接收data參數的接口或方法進行data參數校驗,校驗通過再返回data數據缚够,否則打印或拋出異常參數信息幔妨, 我這里為了演示沒有拋出異常鹦赎,只是做了打印處理。
from jsonschema import validate, ValidationError
# data參數校驗裝飾器误堡,可指定不同的校驗schema
def json_validate(schema):
def wrapper(func):
def inner(data, *args, **kwargs):
try:
validate(data, schema)
except ValidationError as e:
print("參數校驗失敻奇ⅰ:{}!".format(e.message))
else:
print("參數校驗通過!")
return func(data, *args, **kwargs)
return inner
return wrapper
-
app.py
用戶數據處理接口及公司數據處理接口埂伦,需要接收原始用戶或公司的json數據參數煞额,這里導入上面定義的裝飾器和schema字典實現(xiàn)簡潔的參數校驗功能。
from decorators import json_validate
from schema import schema_user, schema_company
@json_validate(schema=schema_company)
def company_api(data):
# 公司數據操作接口示例
print("company_api執(zhí)行入庫操作沾谜!")
return data
@json_validate(schema=schema_user)
def user_api(data):
# 用戶數據操作接口示例
print("use_api執(zhí)行入庫操作膊毁!")
return data
六. 測試校驗
在app.py
下進行測試校驗輸出:
- 正確的參數測試:
if __name__ == '__main__':
company_dict = {
"model": "st",
"count": 4,
"data": [
{"company_name": "測試公司1", "cust_uid": "asd4a676762jjhj"},
{"company_name": "測試公司2", "cust_uid": "andfu58jkskjds3"}
]
}
company_api(company_dict)
user_dict = {
"user_id": 123456,
"user_name": "John",
"age": 12,
"other": {
"hobby": "swimming",
"height": 170.3
}
}
user_api(user_dict)
輸出:
參數校驗通過!
company_api執(zhí)行入庫操作基跑!
參數校驗通過婚温!
use_api執(zhí)行入庫操作!
- 錯誤的用戶參數輸入測試:
- 用戶年齡輸入:0媳否,小于最小值限制
if __name__ == '__main__':
user_dict = {
"user_id": 123456,
"user_name": "John",
"age": 0,
"other": {
"hobby": "swimming",
"height": 170.3
}
}
user_api(user_dict)
輸出:
參數校驗失斦っ:0 is less than the minimum of 1!
- 內嵌字典不傳hobby參數
if __name__ == '__main__':
user_dict = {
"user_id": 123456,
"user_name": "John",
"age": 12,
"other": {
"height": 170.3
}
}
user_api(user_dict)
輸出:
參數校驗失敗:'hobby' is a required property!
- 錯誤的公司參數輸入測試:
- 內嵌字典參數cust_uid輸入空字符串:''
if __name__ == '__main__':
company_dict = {
"model": "st",
"count": 4,
"data": [
{"company_name": "測試公司1", "cust_uid": ""},
{"company_name": "測試公司2", "cust_uid": "andfu58jkskjds3"}
]
}
company_api(company_dict)
輸出:
參數校驗失斃榻摺:'' is too short!
- count輸入小數:3.1
if __name__ == '__main__':
company_dict = {
"model": "st",
"count": 3.1,
"data": [
{"company_name": "測試公司1", "cust_uid": "asd"},
{"company_name": "測試公司2", "cust_uid": "andfu58jkskjds3"}
]
}
company_api(company_dict)
輸出:
參數校驗失斄ν肌:3.1 is not of type 'integer'!
以上,就不再一一測試每種情況了掺逼。
七吃媒、 總結
jsonschema使用類似配置字典的方式來定義參數校驗,使用起來十分的清晰吕喘,理解起來也比較簡單赘那,后期更改也更容易。且報錯信息也比較清晰氯质,一般無需再定制化錯誤信息募舟,個人目前更加喜歡這個方式來進行json參數的校驗。
本篇文章還使用了裝飾器的方式對需要驗參的方法進行裝飾闻察,復用性更強拱礁,且使主體邏輯更加簡潔,還是那句話蜓陌,裝飾器是個好東西觅彰,jsonschema也是好東西,都要盡量用起來才是真的啊~
本文對jsonschema更細節(jié)的內容未做介紹钮热,又更深的需求情參考官方文檔進行對照學習填抬,希望對你有幫助。