Flask-RESTPlus 中文文档 - 3

450 0

  • 请求解析
    • 基础参数
    • 必要参数
    • 多个数值 & 列表
    • 其他源(Other Destinations)
    • 参数位置
    • 多位置参数
    • 高级类型处理
    • 解析器继承
    • 文件上传
    • 错误处理
    • 错误消息
  • 异常处理
    • Http异常处理
    • Flask终止助手
    • Flask-RESTPlus终止助手
    • @api.errorhandler 装饰器
  • 字段掩码(Fields masks)
    • 语法
    • 使用方法

请求解析

警告:

Flask-RESTful的整个请求解析器部分将被移除,取而代之的是关于如何与其他更好地完成输入/输出的包(如marshmallow)集成的文档。这意味着它将一直保持到2.0,但是认为它已经过时了。不要担心,如果您的代码现在正在使用它,并且您希望继续这样做,那么它不会很快消失。

基于Flask-RESTful的请求解析接口reqparse是在argparse接口之后建模的。它的设计提供了简单和统一的访问flask.request对象上的任何变量。

基础参数

下面是请求解析器的一个简单示例。它在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')
args = parser.parse_args()

提示:

默认的参数类型是unicode字符串。这在python3中是str,在python2中是unicode。

如果您指定了help值,那么在解析类型错误时,它将被呈现为错误消息。如果没有指定帮助消息,则默认行为是从类型错误本身返回消息。有关详细信息,请参见错误消息

提示:

默认情况下, 不需要 参数。另外,请求中提供的不属于RequestParser的参数将被忽略。

在请求解析器中声明但未在请求本身中设置的参数默认为None。

必要参数

要为参数传递值,只需将required=True添加到add_argument()调用即可。

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

多个数值和列表

如果您希望一个键能以列表的形式接收多个值,可以像下面这样传递参数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']

其他源(other destinations)

如果出于某种原因,希望在解析后将参数存储在不同的名称下,那么可以使用dest关键字参数。

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

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

参数位置

默认情况下,RequestParser尝试解析来自flask.Request.valuesflask.Request.json的值。

使用add_argument()的location参数来指定从哪些位置获取值。flask.Request上的任何变量都可以使用。例如

# Look only in the POST body
parser.add_argument('name', type=int, location='form')

# Look only in the querystring
parser.add_argument('PageSize', type=int, location='args')

# From the request headers
parser.add_argument('User-Agent', location='headers')

# From http cookies
parser.add_argument('session_id', location='cookies')

# From file uploads
parser.add_argument('picture', type=werkzeug.datastructures.FileStorage, location='files')

提示:

只有在location='json'时才使用type=list。有关更多细节,请参见这个issue

使用location='form'是验证表单数据和记录表单字段的方法。

多位置参数

可以通过将列表传递给location来指定多个参数位置

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

当指定多个位置时,来自指定的所有位置的参数将合并为单个MultiDict。最后列出的位置优先于结果集。

如果参数位置列表包含headers,则参数名将不再不区分大小写,必须与标题大小写名称匹配(参见str.title())。指定location='headers'(不作为列表)将保留大小写不敏感。

高级类型处理

有时,您需要比基本数据类型更多的类型来处理输入验证。input模块提供一些常见的类型处理

  • 用于更加广泛布尔处理的 boolean()
  • 用于IP地址的 ipv4()ipv6()
  • 用于ISO8601日期和数据处理的date_from_iso8601()datetime_from_iso8601()

你只需要把它们用在 type 参数上

parser.add_argument('flag', type=inputs.boolean)

有关可用 input 的完整列表,请参阅 input文档

您也可以像下面这样编写自己的数据类型

def my_type(value):
    '''Parse my type'''
    if not condition:
        raise ValueError('This is not my type')
    return parse(value)

# Swagger documntation
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 has both 'foo' and 'bar'

parser_copy.replace_argument('foo', required=True, location='json')
# 'foo' is now a required str located in json, not an int as defined
#  by original parser

parser_copy.remove_argument('foo')
# parser_copy no longer has 'foo' argument

文件上传

要使用 RequestParser 处理文件上传,您需要使用 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']  # This is FileStorage instance
        url = do_something_with_file(uploaded_file)
        return {'url': url}, 201

请参阅专用的Flask文档部分。

错误处理

RequestParser 处理错误的默认方式是在第一个错误出现的时候终止。当您可能需要一些时间来处理某些参数的时候,这将是很有好处的。然而,将错误捆绑一起并且一次性发送回客户端通常是较好的处理。这种方式可以在Flask应用程序级别(Flask application level)或在特定的 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)

# If a request comes in not containing both 'foo' and 'bar', the error that
# will come back will look something like this.

{
    "message":  {
        "foo": "foo error message",
        "bar": "bar error message"
    }
}

# The default behavior would only return the first error

parser = RequestParser()
parser.add_argument('foo', type=int, required=True)
parser.add_argument('bar', type=int, required=True)

{
    "message":  {
        "foo": "foo error message"
    }
}

程序的配置键是 "BUNDLE_ERRORS",下面是例子

from flask import Flask

app = Flask(__name__)
app.config['BUNDLE_ERRORS'] = True

警告:

BUNDLE_ERRORS 是一个重载了每个 RequestParser 实例里的 bundle_errors 选项的全局设定

错误消息

每个域的错误消息可以通过 help 参数来进行定制(也是在 RequestParser.add_argument当中)。

如果不提供 help 参数,那么这个域的错误消息会是错误类型本身的字符串表示。否则,错误消息就是 help 参数的值

help 参数可能包含一个插值标记( interpolation token),就像 {error_msg} 这样,这个标记将会被错误类型的字符串表示替换。这允许您在保留原本的错误消息的同时定制消息,就像下面的例子这样

from flask_restplus import reqparse


parser = reqparse.RequestParser()
parser.add_argument(
    'foo',
    choices=('one', 'two'),
    help='Bad choice: {error_msg}'
)

# If a request comes in with a value of "three" for `foo`:

{
    "message":  {
        "foo": "Bad choice: three is not a valid choice",
    }
}

字段掩码(Fields masks)

Flask-RESTPlus通过一个自定义请求头支持部分对象获取(partial object fetching)也就是字段掩码(fields mask)。

默认情况下头名为 X-Fields ,但是它可以被 RESTPLUS_MASK_HEADER 参数修改。

语法

语法非常简单。你只要提供一个包含字段名并用逗号分隔的列表,可以选择性的用括号包裹。

# These two mask are equivalents
mask = '{name,age}'
# or
mask = 'name,age'
data = requests.get('/some/url/', headers={'X-Fields': mask})
assert len(data) == 2
assert 'name' in data
assert 'age' in data

实现嵌套字段只需要将内容用大括号包裹即可。

mask = '{name, age, pet{name}}'

嵌套规范适用于嵌套对象或对象列表:

# Will apply the mask {name} to each pet
# in the pets list.
mask = '{name, age, pets{name}}'

特殊字符米字星(*)代表“所有剩余字段(all remaining fields)”。它仅允许指定嵌套过滤:

# Will apply the mask {name} to each pet
# in the pets list and take all other root fields
# without filtering.
mask = '{pets{name},*}'

# Will not filter anything
mask = '*'

使用方法

默认情况下,每次使用 api.marshal@api.marshal_with 时,如果存在标头,掩码将自动应用。

当你每次使用 @api.marshal_with 修饰器时,标头将作为Swagger参数展示。

由于Swagger并不允许展示全局标头,这将会使你的Swagger文档更加冗长。你可以修改 RESTPLUS_MASK_SWAGGERFalse 来禁用这个功能。

您也可以指定默认的掩码,如果找不到掩码,则将应用默认掩码。

class MyResource(Resource):
    @api.marshal_with(my_model, mask='name,age')
    def get(self):
        pass

默认掩码也可以在模型级别处理:

model = api.model('Person', {
    'name': fields.String,
    'age': fields.Integer,
    'boolean': fields.Boolean,
}, mask='{name,age}')

它将被导出到 x-mask 字段:

{"definitions": {
    "Test": {
        "properties": {
            "age": {"type": "integer"},
            "boolean": {"type": "boolean"},
            "name": {"type": "string"}
        },
        "x-mask": "{name,age}"
    }
}}

要覆盖默认掩码,您需要提供另一个掩码或将*用作掩码。

技术文章 Flask Flask Extension

TOPIC

Flask

关于Flask


Comments 0