一、安裝
python3 -m pip install webargs
二、基礎特性
# encoding=utf-8
from flask import Flask
from webargs import fields
from webargs.flaskparser import use_args
app = Flask(__name__)
app.route("/")
@use_args({
"name": fields.Str(required=True),
"age": fields.Int(required=True),
}, location='query')
def index(args):
print('args', args)
return "Hello " + args["name"]
if __name__ == "__main__":
app.run(debug=1)
2.1. 使用
2.1.1 通過裝飾器
@use_args({
"name": fields.Str(required=True),
"age": fields.Int(required=True),
}, location='query')
- 第一個引數是需要獲取的欄位名,型別,是否必須等的定義
- location是指從哪裡獲取這些引數,預設是json,可選:
- 'querystring' (same as 'query')
- 'json'
- 'form'
- 'headers'
- 'cookies'
- 'files'
- 解析完後,把所有引數放在字典裡面,傳給下層函式
2.1.2 通過函式
args = parser.parse(user_args, request)
- 引數和裝飾器一樣,多了一傳request
2.2 引數檢查
from webargs import fields, validate
args_1 = {
# 必須引數,字串型別
"username": fields.Str(required=True),
# validate
"password": fields.Str(validate=lambda p: len(p) >= 6),
"password": fields.Str(validate=validate.Length(min=6)),
# Default value when argument is missing
"display_per_page": fields.Int(missing=10),
# Repeated parameter, e.g. "/?nickname=Fred&nickname=Freddie"
"nickname": fields.List(fields.Str()),
# Delimited list, e.g. "/?languages=python,javascript"
"languages": fields.DelimitedList(fields.Str()),
# When value is keyed on a variable-unsafe name
# or you want to rename a key
"user_type": fields.Str(data_key="user-type"),
"start_day": fields.DateTime(required=True, format='%Y-%m-%d %X'),
"bool": fields.Bool(),
"email": fields.Email(),
"ip": fields.IP(),
"type": fields.Constant(constant='COMMON_TYPE'),
"money": fields.Decimal(),
"weight": fields.Float(),
"url": fields.URL(),
"uuid": fields.UUID(),
"raw": fields.Raw(),
}
- fields.Str 表示接收字串引數
- required=True 表示必傳
- validate=lambda p: len(p) >= 6 表示自定義檢查函式。會把引數傳遞給該函式,該函式返回True表示檢查通過,返回False或者丟擲異常表示檢查不通過
- 如果要對多個引數進行聯合檢查,需要在裝飾器層架validate引數:
@use_args(args_1, validate=lambda args: len(args["username"]) < len(args["password"]))
- 異常需要是
from webargs import ValidationError
這個異常,不然會當程式異常處理
- 如果要對多個引數進行聯合檢查,需要在裝飾器層架validate引數:
- 也可以用validate庫裡面的內建檢查函式
- missing=10 表示如果沒有入參,設定為預設值
- fields.List(fields.Str()) 表示列表型引數,列表的元素是字串
- fields.DelimitedList(fields.Str()) 表示逗號型的列表引數
- data_key="user-type" 表示欄位名修改,入參是user-type,在args字典會改為user_type
- fields.DateTime(required=True, format='%Y-%m-%d %X') 表示接收日期型別,並且格式需要符合,引數值會轉換為datetime型別
- "bool": fields.Bool() 表示布林型別,傳1,0,true,false都能識別
- fields.Email() 只接收email,估計裡面會有正則檢查
- fields.IP() 只接收IP
- fields.Constant(constant='COMMON_TYPE') 常量引數,無論入參是什麼值,type永遠等於COMMON_TYPE
- fields.Decimal() 轉換為Decimal型別
- fields.Float() 轉換為float型別
- fields.URL() fields.UUID() 正則檢查url格式或者uuid格式
- fields.Raw 不檢查引數型別
內建引數檢查
- validate=validate.Length(min=1,max=10) 檢查字串長度需要在某個區間
- validate=validate.OneOf(['male', 'female']) 入參需要在列舉裡面
2.3 檢查失敗處理
如果引數檢查失敗,會返回422響應,但是不會提示哪個引數有問題。我們可以通過Flask的異常處理機制,捕獲這個異常,然後構造我們想要的返回
@app.errorhandler(422) # 捕獲422和400的異常碼
@app.errorhandler(400)
def handle_error(err):
headers = err.data.get("headers", None)
messages = err.data.get("messages", ["Invalid request."])
print(headers)
print(messages) # {'json': {'password': ['Shorter than minimum length 6.']}}
return json.dumps({'err_code': 10000, 'err_msg': messages['json']})
- 從err裡面獲取資訊,headers不知道有什麼用的,message會有異常資訊,例如不滿足
validate=validate.Length(min=6)
檢查,就會返回{'json': {'password': ['Shorter than minimum length 6.']}}
- 如果是主動丟擲的ValidationError異常,message會包含ValidationError異常的內容
- 我們可以把這個引數檢查資訊返回給前端,提示前端哪個引數錯誤了。
messages['json']
的json是location的key
2.4 巢狀引數
對於一些複雜的,有多重巢狀的引數
"name": fields.Nested(
{"first": fields.Str(required=True), "last": fields.Str(required=True)}
)
- 表示name是一個巢狀引數,也就是字典
- 然後裡面需要要first key和last key
三、高階特性
3.1 自定義location
上面說了location支援query,json這些,也可以自定義
@parser.location_loader("data")
def load_data(request, schema):
data = {}
data.update({k: request.args.get(k) for k in request.args})
if request.json:
data.update({k: request.json.get(k) for k in request.json})
print(data, 'dataaaaa')
return data
parser.location = 'data' # 設定預設的location為data
- 上面定義了一個data的location,會合並args和json入參
- 把預設的location修改為data
也可以這樣,這個是官方推薦方法:
@parser.location_loader("args_and_json")
def load_data(request, schema):
from webargs.multidictproxy import MultiDictProxy
newdata = request.args.copy()
if request.json:
newdata.update(request.json)
return MultiDictProxy(newdata, schema)
3.2 定義schema
除了可以通過字典定義args,也可以通過類:
from marshmallow import Schema
class UserSchema(Schema):
name = fields.Str(required=True)
age = fields.Int()
@app.route("/")
@use_args(UserSchema())
def index1(args):
print('args', args)
return "Hello "
3.3 未定義的引數處理
如果入參有未定義的引數,webargs預設會丟擲422異常
from webargs.flaskparser import parser
import marshmallow
parser.unknown = marshmallow.EXCLUDE # 如果有未定義引數,不放到args引數,不拋異常
parser.unknown = marshmallow.INCLUDE # 如果有未定義引數,放到args引數,不拋異常
- 可以修改parse.unknown來修改策略。
- 也可以精確設定不同location的unknown策略
3.4 Flask的url引數
@app.route("/<int:id>/")
@use_args(UserSchema())
def index1(args, id):
print('args', args, id)
return "Hello "
- 如果需要用到Flask的url引數,就需要這樣傳遞引數