Python Web參數校驗庫之webargs
用Python做Web后端開發的同學想必都知道,如何快速解析和校驗前端傳遞過來的請求參數是代碼中必不可少的任務。
以flask為例
@app.route("/api/login", methods=["POST"])
def login():
data = request.get_json()
# 郵箱格式校驗
email = data.get("email")
if not email or re.match(r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)", email):
return jsonify({"code": 400, "msg": "參數有誤"}), 400
# 密碼長度校驗
password = data.get("password")
if not password or len(password) < 6:
return jsonify({"code": 400, "msg": "參數有誤"}), 400
# 數據庫查詢
return jsonify({"code": 200, "msg": "ok"})
對於一個簡單的登錄接口,要寫一堆又臭又長的參數校驗邏輯,而這些本是和業務邏輯沒什么關系的代碼。
只要是和業務無關的代碼,我們總能找到一些框架或者庫來解決問題,而針對web請求參數的解析和校驗庫, webargs 就是一款強大的工具。它直接站在巨人的肩膀上,基於marshmallow做了字段規則校驗工作,如果熟悉 marshmallow 的同學上手這個庫也就半小時。
另一個特點是它針對市面上常見的web框架都寫了一個單獨的解析器parser,對開發者來說無疑是真香,不管你用的是哪個web框架,都能無縫接入webargs到項目中,無需我們自己做過多封裝。
安裝
$ pip install -U webargs
使用
from webargs import fields
from webargs.flaskparser import use_args
app = Flask("hello")
@app.route("/api/login", methods=["POST"])
@use_args({"username": fields.Str(required=True),
"password": fields.Str(required=True, validate=lambda x: len(x) >= 6)})
def login(args):
name = args['username']
password = args['password']
return jsonify({"code": 200, "msg": "ok"})
use_args 是 webargs 提供的裝飾器,正是有了這個裝飾器,它能處理前端傳過來的請求參數,我們只要定義好schema,剩下的交給webargs。
所謂 schema 就是你希望前端傳過來的參數有哪些,每個參數的類型是什么,應該滿足什么規則等等。
這里我們定義的schema要求usename字段必須是字符串,而且是必填項, password 的長度必須大於6.
如果你用的是django,只需要導入djangoparser下的use_args 即可
from webargs.djangoparser import use_args
如果用戶沒有按照要求發送數據,代碼壓根就不會執行到我們的業務代碼中去,直接在裝飾器就把錯誤返回給上層了,所以代碼整潔了不少。

如果傳的參數不符合要求,默認返回的錯誤碼是422。但是返回的錯誤信息似乎看不懂,api的調用者看了並不知道參數錯在哪里。
我們可以專門針對錯誤重新處理一下,同樣返回一個格式可讀的json體給前端, 這是flask的特性,如果是django,請自行查找相應的方法
@app.errorhandler(422)
@app.errorhandler(400)
def handle_error(err):
headers = err.data.get("headers", None)
messages = err.data.get("messages", ["Invalid request."])
if headers:
return jsonify({"errors": messages}), err.code, headers
else:
return jsonify({"errors": messages}), 400

location
webargs 默認情況下是從請求body中以json格式來讀取和解析參數的,如果要通過其他方式獲取,就需要指定在use_args中指定location
@app.route("/", methods=["GET"])
@use_args({"name": fields.Str(missing="Friend")}, location="querystring")
def index(args):
"""A welcome page."""
return jsonify({"message": "Welcome, {}!".format(args["name"])})

location可支持的類型有:
- 'querystring':通過url后面的查詢參數獲取
- 'json' # 把請求body當作json來獲取參數
- 'form' # 通過form表單獲取參數
- 'headers' # 通過請求頭獲取參數
- 'cookies' # 通過cookie獲取
- 'files' # 通過文件獲取
驗證器
webargs 最強大的地方其實還在Field提供的校驗規則,依賴於marshmallow,我們可以指定每個字段的數據類型,支持的類型非常多。大部分都是marshmallow提供了,webargs自己也提供了部分字段類型。

除了可以指定具體的類型外,我們還可以指定驗證器來要求字段符合某種規則, validate 接收的參數是函數,意味着我們可以自定義任何規則。
def must_exist_in_db(val):
if val != 1:
raise ValidationError("id not exist")
hello_args = {"name": fields.Str(missing="Friend"),
"id": fields.Integer(required=True, validate=must_exist_in_db)}
@app.route("/", methods=["GET"])
@use_args(hello_args, location="query")
def hello(args):
"""A welcome page."""
return jsonify({"message": "Welcome, {}!".format(args["name"])})

先介紹這么多,更多高級功能參考:https://webargs.readthedocs.io/en/latest/quickstart.html
趕緊在你的項目中試試吧!
