使用 voluptuous 校驗數據
在 Python 中,我們經常需要對參數進行校驗,這是我們有好多種方法,例如寫很多 if 啊,或者寫正則表達式啊等等,技巧高的人可以寫得很巧妙,而技巧一般的人呢,可能會寫得很冗長,例如我,經常就不能很好得處理參數校驗的代碼。 所以我就不斷得尋找,終于最近發現了一個不錯的 python 參數校驗 lib,叫做 voluptuous。名字可能有點難記,而且英文翻譯過來的中文還不怎么好聽,但是非常好用。下面就逐漸帶大家嘗試一下 voluptuous 庫的妙用。
安裝 voluptuous 要用之前,肯定要先安裝的啦,安裝這一步很簡單,還是按照往常一般使用 pip 搞定。我使用的是當前的最新版 0.8.8: pip install voluptuous==0.8.8 嘗試 voluptuous 首先,想來一個最為常見的,就是校驗參數的類型,我這里假設參數都是以 json 格式進行傳遞的,json 格式在 python 中又可以表現為字典(dict),所以這里就不做區分了。 以官網的日志為例,使用分頁的參數為例子,校驗參數的類型,首先,我們要求參數的類型,查詢的字段q必須為字符串,分頁大小per_page必須為數字,頁碼page必須為數字。 那么,使用 voluptuous 后,可以這樣寫: from voluptuous import Schema s = Schema({ 'q': str, 'per_page': int, 'page': int }) 這就表示了我們剛才的需求:查詢的字段q必須為字符串,分頁大小per_page必須為數字,頁碼page必須為數字。 那么,現在,假設客戶端傳過來了一組參數: {"q": "hello", "page": 10, "per_page": 20 } 那么,我們要怎么來校驗呢?也很簡單: from voluptuous import Schema s = Schema({ 'q': str, 'per_page': int, 'page': int }) print s({"q": "hello", "page": 10, "per_page": 20 }) 這里可以看到,我們使用客戶端傳遞過來的參數作為參數,調用了我們使用既定模板創建的對象,然后直接把他打印出來,如果你跑過這段代碼,你會發現結果如下: {"q": "hello", "page": 10, "per_page": 20 } 沒錯, voluptuous 就是將校驗通過的參數返回了。就是這么簡單。 那這時,你可能要問了,如果校驗不通過的參數怎么辦,我要怎么處理,下面就演示一下如果傳遞的參數是: {"q": "hello", "page": "world", "per_page": 20 } 這樣的話,要怎么來處理校驗不通過的問題,其實,當校驗不通過的時候,Schema 會拋出一個異常,然后我們可以通過捕獲這個異常來確定校驗失敗的問題,例如: try: print s({"q": "hello", "page": "world","per_page": 20}) except MultipleInvalid as e: print "error: {} occur while parse args".format(e.errors) 當你執行這段代碼之后,你會發現打印出來的內容是: error: [TypeInvalid('expected int',)] occur while parse args 我們就知道參數是有問題的,需要客戶端確認。 更進一步 ok,這就是一個簡單的校驗,下面我們進行更復雜的校驗,我們現在不僅要保證參數的類型正確,我們還需要保證查詢參數一定要有,另外兩個參數可有可無。那么我們可以怎么做呢? 其實,也很簡單,就是給必須的參數加上一個 Required 關鍵字,例如這樣: from voluptuous import Schema, Required required_s = Schema({ Required('q'): str, 'per_page': int, 'page': int }) 然后,我們可以嘗試一下加入什么都不傳,會發生什么事: try: print required_s({}) except MultipleInvalid as e: print "error: {} occur while parse with required args".format(e.errors) 很明顯,你跑一遍就知道了,這段代碼會拋出這個異常: error: [RequiredFieldInvalid('required key not provided',)] occur while parse with required args 這個時候,我們再貪心一點,我們覺得參數必須還不夠,還不能有多余的參數,什么參數都塞過來,浪費我內存啊,過濾這個應該和剛才的參數必須有一樣簡單吧? 是的,不過這時因為不是對單獨一個參數起作用了,所以需要放在 Schema 的參數里面,這個參數叫做 extras,例如下面這段就是表示不要多余的參數: not_allow_extra_s = Schema({ 'q': str, 'per_page': int, 'page': int }, extra=False) 然后我們嘗試一下: try: print not_allow_extra_s({"q": "hello", "unknown": "key"}) except MultipleInvalid as e: print "error: {} occur while parse with no extras args".format(e.errors) 結果也很簡單: error: [Invalid('extra keys not allowed',)] occur while parse with no extras args 其實,Schema 默認就是不允許有多余參數的,如果我們想要多余的參數,則需要顯式得將這個參數設置為 True,表示允許有多余的參數。 現在我們已經知道一些用法了,但是,我們覺得只控制類型不夠,我們還想控制一下參數的長度,例如查詢的參數不能超過 10 個字符,那么樣怎么做了? 下面這段代碼就自定義了一個校驗值不能超過 10 個字符的 str 類型: def less_than_10(value): if isinstance(value, str) and len(value) 校驗函數很簡單,接受一個參數,也就是要校驗的值,然后,如果校驗成功就返回這個值,否則就拋出一個 Invalid 的異常。這樣 Schema 就知道是否校驗通過了。 其實,你可能會想,如果這里校驗通過后我返回的不是參數的值會怎樣?如果你有這個想法,我很佩服。 確實,如果這里不返回參數的值,Schema 也會認為是校驗通過的,而且 Schema 會將你的返回值返回。那么想到這里,你是不是有一些更加強大的想法?好吧,不知道你有沒有,我這里告知一下,可以通過這個方法來進行數據轉換。 假如,我們的查詢只支持大寫字母,但是我們認為客戶端傳過來是小寫字母的時候也是有效的,那么,我們就需要將客戶端傳遞過來的參數進行轉換,轉成大寫字母,那么,我們可以這樣寫: def convert_letter(value): if isinstance(value, str): return value.upper() raise Invalid("not valid string") transformation_s = Schema({ Required('q'): convert_letter, 'per_page': int, 'page': int }) print transformation_s({'q': 'hello'}) 然后,我們看一下輸出: {'q': 'HELLO'} 已經變成了大寫了。 好吧,到此已經將 voluptuous 的一些比較常用和重要的功能介紹完了,如果大家有用心去體會的話,相信可以寫出一些非常精妙的校驗器出來。
技術改變世界!
--狂詩絕劍

浙公網安備 33010602011771號