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


 

0x00 內容概覽

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

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 參考鏈接

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM