【Flask-RESTPlus系列】Part3:請求解析

 

0x00 內容概覽

  1. 請求解析
    1. 基本參數
    2. 必需參數
    3. 多值和列表
    4. 其餘目標
    5. 參數位置
    6. 參數多個位置
    7. 高級類型處理
    8. 解析器繼承
    9. 文件上傳
    10. 錯誤處理
    11. 錯誤消息
  2. 參考連接

0x01 請求解析

注意:Flask-RESTPlus的整個請求解析器部分將被移除,並將替換成關於集成其餘更善於處理輸入、輸出的包(例如marshmallow)的說明文檔。可是考慮到已經被廢棄,它將一直維護到2.0版本。若是你如今有代碼使用它,並但願繼續這樣作,那麼也無需擔憂,由於它不會很快消失。html

Flask-RESTPlus的請求解析接口reqparse是模仿argparse接口實現的。它的設計目的是對Flask中flask.request對象上的任何變量提供簡單和統一的訪問方式。git

一、基本參數

下面是一個請求解析器的簡單示例。它在flask.Request.values字典中查找兩個參數:一個整數和一個字符串:github

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。json

若是指定了help變量的值,那麼在解析請求時,若是出現了類型錯誤,那麼會將它渲染爲錯誤信息。若是未指定help信息,那麼默認的行爲是返回類型錯誤信息自己。能夠查看「十一、錯誤信息」部分以瞭解更多細節。flask

注意:默認狀況下,參數不是必需的。而且,若是請求中提供的某些參數不是RequestParser的部份內容,那麼這些參數將會被忽略。api

注意:請求解析器中聲明的參數,若是在請求中並未設置這些參數值,那麼它們將會默認設置爲None。restful

二、必需參數

若是須要確保某個參數必須提供,那麼能夠在調用add_argument()時傳入required=True的參數項:cookie

parser.add_argument('name', required=True, help="Name cannot be blank!")

此時,若是請求中未提供該參數,那麼將會返回錯誤信息。session

三、多值和列表

若是想爲某個key接受多個值以構成列表,那麼能夠傳入action='append':app

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']

四、其餘目標

 若是指望參數一旦被解析,就將其存儲爲其餘名字,那麼可使用dest參數:

parser.add_argument('name', dest='public_name')

args = parser.parse_args()
args['public_name']

五、參數位置

默認狀況下,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'既能驗證表單數據,又能爲表單字段文檔化。

六、參數多位置

爲location參數賦一個列表就能爲參數指定多個位置:

parser.add_argument('text', location=['headers', 'values'])

當指定多個參數位置時,那麼全部指定位置的參數將會組成一個MultiDict。其中,列表中最後位置處的參數將會優先存儲在結果集中。

若是參數位置列表中包含headers位置,那麼參數名將變成對大小寫敏感,而且必須匹配它們的標題大小寫名稱(見str.title())。

指定location='headers'(而不是做爲列表的某個元素)將保留大小寫不敏感的特性。

七、高級類型處理

 有時,咱們須要其餘原始類型來處理輸入驗證問題。爲此,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'}

八、解析器繼承

 不少狀況下,咱們都須要爲不一樣的資源指定不一樣的解析器。不過,若是這些不一樣的解析器之間存在大量相同的字段的話,將會存在大量重複編碼的問題。爲此,咱們能夠編寫一個父解析器,父解析器中包含全部共同的參數,而後利用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'參數

九、文件上傳

 爲了利用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

十、錯誤處理

 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選項值。

十一、錯誤消息

每一個字段的錯誤消息均可以經過在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 參考連接

相關文章
相關標籤/搜索