0x00 内容概覽
- 請求解析
- 基本參數
- 必需參數
- 多值和清單
- 其他目标
- 參數位置
- 參數多個位置
- 進階類型處理
- 解析器繼承
- 檔案上傳
- 錯誤處理
- 錯誤消息
- 參考連結
0x01 請求解析
注意:Flask-RESTPlus的整個請求解析器部分将被移除,并将替換成關于內建其他更善于處理輸入、輸出的包(例如marshmallow)的說明文檔。但是考慮到已經被廢棄,它将一直維護到2.0版本。如果你現在有代碼使用它,并希望繼續這樣做,那麼也無需擔心,因為它不會很快消失。
Flask-RESTPlus的請求解析接口reqparse是模仿argparse接口實作的。它的設計目的是對Flask中flask.request對象上的任何變量提供簡單和統一的通路方式。
1、基本參數
下面是一個請求解析器的簡單示例。它在flask.Request.values字典中查找兩個參數:一個整數和一個字元串:
from flask_restplus import reqparse
parser = reqparse.RequestParser()
parser.add_argument('rate', type=int, help='Rate cannot be converted')
parser.add_argument('name') # Python3中預設類型為字元串
args = parser.parse_args()
注意:預設參數類型是unicode字元串。在Python3中為str類型,而在Python2中為unicode。
如果指定了help變量的值,那麼在解析請求時,如果出現了類型錯誤,那麼會将它渲染為錯誤資訊。如果未指定help資訊,那麼預設的行為是傳回類型錯誤資訊本身。可以檢視“11、錯誤資訊”部分以了解更多細節。
注意:預設情況下,參數不是必需的。并且,如果請求中提供的某些參數不是RequestParser的部分内容,那麼這些參數将會被忽略。
注意:請求解析器中聲明的參數,如果在請求中并未設定這些參數值,那麼它們将會預設設定為None。
2、必需參數
如果需要確定某個參數必須提供,那麼可以在調用add_argument()時傳入required=True的參數項:
parser.add_argument('name', required=True, help="Name cannot be blank!")
此時,如果請求中未提供該參數,那麼将會傳回錯誤資訊。
3、多值和清單
如果想為某個key接受多個值以構成清單,那麼可以傳入action='append':
parser.add_argument('name', action='append')
此時的查詢格式如下所示:
curl http://api.example.com -d "name=bob" -d "name=sue" -d "name=joe"
而程式中擷取到的參數如下所示:
args = parser.parse_args()
args['name'] # ['bob', 'sue', 'joe']
如果期望一個逗号分隔的清單,那麼可以使用action='split':
parser.add_argument('fruits', action='split')
curl http://api.example.com -d "fruits=apple,lemon,cherry"
args = parser.parse_args()
args['fruits'] # ['apple', 'lemon', 'cherry']
4、其他目标
如果期望參數一旦被解析,就将其存儲為其他名字,那麼可以使用dest參數:
parser.add_argument('name', dest='public_name')
args = parser.parse_args()
args['public_name']
5、參數位置
預設情況下,RequestParser嘗試從flask.Request.values和flask.Request.json中解析值。
在add_argument()中使用location參數來指定擷取值的其他位置。可以使用flask.Request上的任何變量,例如:
# 僅僅在POST body中查找
parser.add_argument('name', type=int, location='form')
# 僅僅在querystring中查找
parser.add_argument('PageSize', type=int, location='args')
# 從請求頭中查找
parser.add_argument('User-Agent', location='headers')
# 從http cookies中查找
parser.add_argument('session_id', location='cookies')
# 從上傳檔案中查找
parser.add_argument('picture', type=werkzeug.datastructures.FileStorage, location='files')
注意:當location='json'時隻能使用type=list,點選此處檢視更多。
注意:使用location='form'既能驗證表單資料,又能為表單字段文檔化。
6、參數多位置
為location參數賦一個清單就能為參數指定多個位置:
parser.add_argument('text', location=['headers', 'values'])
當指定多個參數位置時,那麼所有指定位置的參數将會組成一個MultiDict。其中,清單中最後位置處的參數将會優先存儲在結果集中。
如果參數位置清單中包含headers位置,那麼參數名将變成對大小寫敏感,并且必須比對它們的标題大小寫名稱(見str.title())。
指定location='headers'(而不是作為清單的某個元素)将保留大小寫不敏感的特性。
7、進階類型處理
有時,我們需要其他原始類型來處理輸入驗證問題。為此,inputs子產品中提供了一些常用的類型處理方法,如下:
- boolean()用于廣泛的布爾值處理
- ipv4()和ipv6()用于IP位址
- date_from_iso8601()和datetime_from_iso8601()用于ISO8601 date和datetime處理
隻需要使用它們作為type參數的值即可:
parser.add_argument('flag', type=inputs.boolean)
檢視inputs文檔以了解所有可用的輸入類型。
另外,我們也可以編寫自己的輸入類型:
def my_type(value):
'''解析類型'''
if not condition:
raise ValueError('This is not my type')
return parse(value)
# Swagger文檔化
my_type.__schema__ = {'type': 'string', 'format': 'my-custom-format'}
8、解析器繼承
很多情況下,我們都需要為不同的資源指定不同的解析器。不過,如果這些不同的解析器之間存在大量相同的字段的話,将會存在大量重複編碼的問題。為此,我們可以編寫一個父解析器,父解析器中包含所有共同的參數,然後利用copy()方法來擴充解析器。另外,也可以利用replace_argument()來覆寫父解析器中的任何參數,或者利用remove_argument()完全移除父解析器中的某個參數。例如:
from flask_restplus import reqparse
parser = reqparse.RequestParser()
parser.add_argument('foo', type=int)
parser_copy = parser.copy()
parser_copy.add_argument('bar', type=int)
# 此時,parser_copy中同時包含'foo'和'bar'參數
parser_copy.replace_argument('foo', required=True, location='json')
# 此時,'foo'參數變成了一個必需的str類型的參數,并且查找位置為json;而不再是父解析器中定義的int類型的可選參數
parser_copy.remove_argument('foo')
# 此時,parser_copy中不再包含'foo'參數
9、檔案上傳
為了利用RequestParser處理檔案上傳問題,我們需要将location變量值設定為files,并設定type值為FileStorage。如下所示:
from werkzeug.datastructures import FileStorage
upload_parser = api.parser()
upload_parser.add_argument('file', location='files',
type=FileStorage, required=True)
@api.route('/upload/')
@api.expect(upload_parser)
class Upload(Resource):
def post(self):
uploaded_file = args['file'] # 這是FileStorage執行個體
url = do_something_with_file(uploaded_file)
return {'url': url}, 201
10、錯誤處理
RequestParser處理錯誤的預設方式是在第一個錯誤産生時中斷。當我們擁有需要花費一定時間來處理的參數時,這種方式是有好處的。然而,通常來說,将所有産生的錯誤都綁定在一起,然後同時一次性傳回給用戶端,這種方式則更加友好。這種方式既可以在Flask應用級别指定,也可以在特定的RequestParser執行個體級别指定。為了調用一個包含錯誤綁定選項的RequestParser,需要傳入參數bundle_errors。例如:
from flask_restplus import reqparse
parser = reqparse.RequestParser(bundle_errors=True)
parser.add_argument('foo', type=int, required=True)
parser.add_argument('bar', type=int, required=True)
# 如果某個請求中同時不包含'foo'和'bar',那麼傳回的錯誤将看起來如下所示:
{
"message": {
"foo": "foo error message",
"bar": "bar error message"
}
}
# 預設操作将僅僅傳回第一個錯誤
parser = RequestParser()
parser.add_argument('foo', type=int, required=True)
parser.add_argument('bar', type=int, required=True)
{
"message": {
"foo": "foo error message"
}
}
應用級别的配置key為“BUNDLE_ERRORS”。例如:
from flask import Flask
app = Flask(__name__)
app.config['BUNDLE_ERRORS'] = True
警告:BUNDLE_ERRORS是一個全局設定,它将覆寫每個RequestParser執行個體中的bundle_errors選項值。
11、錯誤消息
每個字段的錯誤消息都可以通過在Argument(也包括RequestParser.add_argument)中使用help參數來自定義。
如果沒有提供help參數,那麼該字段的錯誤消息将會是類型錯誤本身的字元串表示。如果提供了help參數,那麼錯誤消息将會是help參數的值。
help可能包含一個插入的符号{error_msg},它将會替換成類型錯誤的字元串表示。這種方式能夠實作自定義錯誤消息,同時保留原始的錯誤消息。如下所示:
from flask_restplus import reqparse
parser = reqparse.RequestParser()
parser.add_argument(
'foo',
choices=('one', 'two'),
help='Bad choice: {error_msg}'
)
# 如果請求中的'foo'參數值為'three',那麼錯誤資訊将會如下所示:
{
"message": {
"foo": "Bad choice: three is not a valid choice",
}
}
0x02 參考連結
- http://flask-restplus.readthedocs.io/en/stable/parsing.html