Restful规范响应处理(marshal、marshal_with、fields、reqparse、representation)

1、Restful规范返回值

        对于一个类视图,可以指定好一些字段做标准化用于返回。以后使用ORM模型或者自定义模型的时候,他会自动的获取模型中的相应的字段,生成json格式数据,然后再返回给客户端。

使用方法

  • 导入 flask_restful.marshal_with 装饰器
  • 定义一个字典变量来指定需要返回的标准化字段,以及该字段的数据类型

        在请求方法中,返回自定义对象的时候,flask_restful会自动的读取对象模型上的所有属性。组装成一个符合标准化参数的json格式字符串返回给客户端

示例代码:

from flask import Flask
from flask_restful import Api, Resource, fields, marshal_with

app = Flask(__name__)
api = Api(app)


class News:
    def __init__(self, code, msg, state, content):
        self.code = code
        self.msg = msg
        self.state = state
        self.content = content


class NewsView(Resource):
    resouce_fields = {
        'code': fields.Integer,
        'msg': fields.String,
        'state': fields.String
    }

    @marshal_with(resouce_fields)
    def get(self):
        return {'code': 200, 'msg': '访问成功!', 'state': 'OK'}

    @marshal_with(resouce_fields)
    def post(self):
        return {'msg': '注册成功!'}

    @marshal_with(resouce_fields)
    def put(self):
        # 在返回对象时,会自动在对象中获取与约定好的字段,并获取封装成json。
        news = News(404, '注册成功', 'OK', 'dgw')
        return news


api.add_resource(NewsView, '/news/')

if __name__ == '__main__':
    app.run(debug=True)

2、序列化数据

        Flask-RESTful 提供了marshal工具,用来帮助我们将数据序列化为特定格式的字典数据,以便作为视图的返回值。

from flask_restful import Resource, fields, marshal_with

resource_fields = {
    'name': fields.String,
    'address': fields.String,
    'user_id': fields.Integer
}

class Todo(Resource):
    @marshal_with(resource_fields, envelope='resource')
    def get(self, **kwargs):
        return db_get_todo()

也可以不使用装饰器的方式

class Todo(Resource):
    def get(self, **kwargs):
        data = db_get_todo()
        return marshal(data, resource_fields)

示例代码1:

from flask import Flask
from flask_restful import fields, Resource, marshal, marshal_with, Api

app = Flask(__name__)
api = Api(app)


# 用来模拟要返回的数据对象的类
class User(object):
    def __init__(self, user_id, name, age):
        self.user_id = user_id
        self.name = name
        self.age = age


resoure_fields = {
    'user_id': fields.Integer,
    'name': fields.String
}


class Demo1Resource(Resource):
    @marshal_with(resoure_fields, envelope='data1')
    def get(self):
        user = User(1, 'dgw', 12)
        return user


class Demo2Resource(Resource):
    def get(self):
        user = User(1, 'dgw', 12)
        return marshal(user, resoure_fields, envelope='data2')


api.add_resource(Demo1Resource, '/demo1')
api.add_resource(Demo2Resource, '/demo2')

if __name__ == '__main__':
    app.run(debug=True)

运行效果:

 测试代码2:

运行结果: 

 

示例代码3:

from flask import Flask
from flask_restful import reqparse, Api, Resource, fields, marshal_with

app = Flask(__name__)
api = Api(app)

# 1. 关于参数解析的部分
# 一组虚拟的数据
TODOS = {
    'todo1': {'task': 1},
    'todo2': {'task': 2},
    'todo3': {'task': 3},
}

# 定义允许的参数为task,类型为int,以及错误时的提示
parser = reqparse.RequestParser()
parser.add_argument('task', type=int, help='Please set a int task content!')


# 真正处理请求的地方
class TodoList(Resource):
    def get(self):
        return TODOS, 200, {'Etag': 'some-opaque-string'}

    def post(self):
        args = parser.parse_args()
        print(max(TODOS.keys()), type(max(TODOS.keys())))
        todo_id = int(max(TODOS.keys()).lstrip('todo')) + 1
        todo_id = 'todo%i' % todo_id
        TODOS[todo_id] = {'task': args['task']}
        return TODOS[todo_id], 201


# 实际定义路由的地方
api.add_resource(TodoList, '/todos', '/all_tasks')


# 2. 关于响应域的部分
# ORM的数据模型
class TodoDao(object):
    def __init__(self, todo_id, task):
        self.todo_id = todo_id
        self.task = task

        # 这个域不会被返回
        self.status = 'active'


# marshal-蒙板,
resource_fields = {
    'task': fields.String,
    'url': fields.Url('todo_ep')
}


# 真正处理请求的地方
class Todo(Resource):
    # 蒙板 把多余的参数蒙住,只返回resource_fields中声明的键值对
    @marshal_with(resource_fields)
    def get(self, todo_id):
        return TodoDao(todo_id=todo_id, task='Remember the milk'), 200


# 实际定义路由的地方
api.add_resource(Todo, '/todos/<todo_id>', endpoint='todo_ep')  # 类Todo中只写了get方法

if __name__ == '__main__':
    app.run(debug=True)

运行结果:

get功能:

 post功能:

3、Restful规范返回值-参数设置

3.1 设置重命名属性和默认值

问题:规范给出的属性名和模型内部的属性名不相同?

答:使用 attribute 配置这种映射,比如: fields.String(attribute='username')

问题:某些字段,没有值,但想给一个值做为默认值?

答:使用 default 指定默认值,比如: fields.String(default='sxt')

示例代码:

from flask import Flask
from flask_restful import Api, Resource, fields, marshal_with

app = Flask(__name__)
api = Api(app)


class News:
    def __init__(self, code, msg, info):
        self.code = code
        self.msg = msg
        self.info = info
        self.state = 1000


class NewsView(Resource):
    resouce_fields = {
        'code': fields.Integer(default=200),  # 通过参数default来设置默认值
        'msg': fields.String,
        'content': fields.String(attribute='info'),  # 通过参数attribute来设置提取数据的字段
        'state': fields.Integer(default=2000)  # 优先级不如真实数据里面的高
    }

    @marshal_with(resouce_fields)
    def get(self):
        news = News(200, '访问成功!', '移动')
        return news

    @marshal_with(resouce_fields)
    def post(self):
        return {'msg': '增加数据成功!', 'info': '联通'}

    @marshal_with(resouce_fields)
    def put(self):
        news = News(200, '访问成功!', '移动')
        return news


api.add_resource(NewsView, '/news/')

if __name__ == '__main__':
    app.run(debug=True)

运行结果:

4、Restful规范返回值-类型设置

大型的互联网项目中,返回的数据格式,有时是比较复杂的结构。

如:豆瓣电影

返回的值里有json或者列表数据,这时可以通过以字段来实现

  • fields.List 放置一个列表
  • fields.Nested放置一个字典

示例代码:

from flask import Flask
from flask_restful import Api, Resource, fields, marshal_with

app = Flask(__name__)
api = Api(app)


class User:
    def __init__(self, uname):
        self.uname = uname

    def __repr__(self):
        return f'<User uname:{self.uname}>'


class NewsType:
    def __init__(self, _type):
        self._type = _type

    def __repr__(self):
        return f'<User type:{self._type}>'


class News:
    def __init__(self, code, msg):
        self.code = code
        self.msg = msg
        self.user = None
        self._type = []

    def __repr__(self):
        return f'<News code:{self.code} msg:{self.msg} user:{self.user} type:{self._type}>'


def create_data():
    user = User('dgw')
    _type1 = NewsType('IT')
    _type2 = NewsType('Python')
    news = News(200, 'Python update!')
    news.user = user
    news._type.append(_type1)
    news._type.append(_type2)

    return news


class NewsView(Resource):
    resouce_fields = {
        'code': fields.Integer,
        'msg': fields.String,
        'user': fields.Nested({
            'uname': fields.String
        }),
        '_type': fields.List(fields.Nested({
            '_type': fields.String
        }))
    }

    @marshal_with(resouce_fields)
    def get(self):
        news = create_data()
        print(news)
        print(type(news))
        return news


api.add_resource(NewsView, '/news/')

if __name__ == '__main__':
    app.run(debug=True)

运行结果:

5、定制返回的JSON格式

5.1 需求

想要接口返回的JSON数据具有如下统一的格式

{"message": "描述信息", "data": {要返回的具体数据}}

在接口处理正常的情况下, message返回ok即可,但是若想每个接口正确返回时省略message字段

class DemoResource(Resource):
    def get(self):
        return {'user_id':1, 'name': 'itcast'}

对于诸如此类的接口,能否在某处统一格式化成上述需求格式?

{"message": "OK", "data": {'user_id':1, 'name': 'itcast'}}

5.2 解决

Flask-RESTful的Api对象提供了一个representation的装饰器,允许定制返回数据的呈现格式

api = Api(app)

@api.representation('application/json')
def handle_json(data, code, headers):
    # TODO 此处添加自定义处理
    return resp

Flask-RESTful原始对于json的格式处理方式如下:

代码出处:flask_restful.representations.json

from flask import make_response, current_app
from flask_restful.utils import PY3
from json import dumps


def output_json(data, code, headers=None):
    """Makes a Flask response with a JSON encoded body"""

    settings = current_app.config.get('RESTFUL_JSON', {})

    # If we're in debug mode, and the indent is not set, we set it to a
    # reasonable value here.  Note that this won't override any existing value
    # that was set.  We also set the "sort_keys" value.
    if current_app.debug:
        settings.setdefault('indent', 4)
        settings.setdefault('sort_keys', not PY3)

    # always end the json dumps with a new line
    # see https://github.com/mitsuhiko/flask/pull/1262
    dumped = dumps(data, **settings) + "\n"

    resp = make_response(dumped, code)
    resp.headers.extend(headers or {})
    return resp

为满足需求,做如下改动即可

@api.representation('application/json')
def output_json(data, code, headers=None):
    """Makes a Flask response with a JSON encoded body"""

    # 此处为自己添加***************
    if 'message' not in data:
        data = {
            'message': 'OK',
            'data': data
        }
    # **************************

    settings = current_app.config.get('RESTFUL_JSON', {})

    # If we're in debug mode, and the indent is not set, we set it to a
    # reasonable value here.  Note that this won't override any existing value
    # that was set.  We also set the "sort_keys" value.
    if current_app.debug:
        settings.setdefault('indent', 4)
        settings.setdefault('sort_keys', not PY3)

    # always end the json dumps with a new line
    # see https://github.com/mitsuhiko/flask/pull/1262
    dumped = dumps(data, **settings) + "\n"

    resp = make_response(dumped, code)
    resp.headers.extend(headers or {})
    return resp

示例代码:

from flask import Flask
from flask_restful import Api, Resource, fields, marshal_with
from flask import make_response, current_app
from flask_restful.utils import PY3
from json import dumps

app = Flask(__name__)
api = Api(app)


@api.representation('application/json')
def output_json(data, code, headers=None):
    """Makes a Flask response with a JSON encoded body"""

    # 此处为自己添加***************
    if 'message' not in data:
        data = {
            'message': 'OK',
            'data': data
        }
    # **************************

    settings = current_app.config.get('RESTFUL_JSON', {})

    # If we're in debug mode, and the indent is not set, we set it to a
    # reasonable value here.  Note that this won't override any existing value
    # that was set.  We also set the "sort_keys" value.
    if current_app.debug:
        settings.setdefault('indent', 4)
        settings.setdefault('sort_keys', not PY3)

    # always end the json dumps with a new line
    # see https://github.com/mitsuhiko/flask/pull/1262
    dumped = dumps(data, **settings) + "\n"

    resp = make_response(dumped, code)
    resp.headers.extend(headers or {})
    return resp


class NewsView(Resource):
    resouce_fields = {
        'code': fields.Integer,
        'msg': fields.String,
        'state': fields.String
    }

    @marshal_with(resouce_fields)
    def get(self):
        return {'code': 200.1, 'msg': '访问成功!', 'state': 'OK'}


api.add_resource(NewsView, '/news')

if __name__ == '__main__':
    app.run(debug=True)

运行结果:

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Flask-RESTful 中,可以使用 request_parser 来预处理 POST 请求中的 JSON 数据。使用 request_parser 可以在处理 JSON 数据之前对数据进行验证和预处理,以确保数据的有效性。以下是一个使用 request_parser 预处理 JSON 数据的示例: ``` from flask import Flask, request from flask_restful import Api, Resource, reqparse app = Flask(__name__) api = Api(app) parser = reqparse.RequestParser() parser.add_argument('name', type=str, required=True, help='Name is required') parser.add_argument('age', type=int, required=True, help='Age is required') class User(Resource): def post(self): args = parser.parse_args() name = args['name'] age = args['age'] # perform validation and pre-processing on the data here # ... # return the response return {'name': name, 'age': age} api.add_resource(User, '/user') if __name__ == '__main__': app.run(debug=True) ``` 在上面的示例中,我们首先创建了一个 RequestParser 对象,然后使用 add_argument 方法添加需要解析的参数。接下来,在 User 类的 post 方法中,我们使用 parser.parse_args() 方法解析 POST 请求中的 JSON 数据,并从解析结果中获取 name 和 age 参数。我们可以在获取参数之后对数据进行验证和预处理,完成后再返回响应。 需要注意的是,Flask-RESTful 会自动将返回值序列化为 JSON 格式,并设置 Content-Type 头为 application/json。因此,在返回响应时,只需要返回一个 Python 字典即可,无需手动进行 JSON 序列化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值