Flask-Restful

1 Flask restful

1.1 简介

flask-restful 是flask扩展包 提供快速工具化构建restful风格api的支持 兼容已有的orm框架
1.  视图类方法映射请求方式(继承Resource视图类 可对多个url进行匹配)
2.  自带参数解析器(数据类型、数据值、其他扩展选项(必选、提示信息、参数类型{query_string|form|json|header}等))
3.  响应数据自定义扩展输出(直接和实体类pv关联、可多重嵌套输出)

1.2 安装

1. 在线
pip install flask-restful

2.离线
git clone https://github.com/twilio/flask-restful.git
cd flask-restful
python setup.py develop

1.3 实例

# -*- coding:utf-8 -*-
from flask import Flask
from flask_restful import Api, Resource


# 视图类
class Test(Resource):
    def get(self):
        return {'data': "data"}


if __name__ == '__main__':
    # flask实例
    app = Flask(__name__)
    # 通过api封装flask
    api = Api(app)
    # 添加路由映射并关联到视图类
    api.add_resource(Test, '/')
    # 运行服务
    app.run(debug=True)

'''
curl http://localhost:5000/
>>>{"data": "data"}
'''

# flask-restful-api实例
from flask_cors import CORS
from flask_restful import Api, Resource, reqparse, fields, marshal_with
from flask import Flask

# flask实例
app = Flask(__name__)
# 跨域问题
CORS(app)
# 通过api封装flask 提供restful风格
api = Api(app)

# 测试数据
todos = {
    'todo1': {'task': 1},
    'todo2': {'task': 2},
    'todo3': {'task': 3},
}

# 请求参数解析
parser = reqparse.RequestParser()
parser.add_argument('task', type=int, help='task-pargrams must be int')


# 请求处理类
class TodoList(Resource):
    def get(self):
        return todos, 200, {'Etag': 'some-opaque-string'}
    
    def post(self):
        args = parser.parse_args()
        todo_id = int(max(todos.keys()).lstrip('todo'))+1
        todo_id = 'todo%i' % todo_id
        todos[todo_id] = {'task': args['task']}
        print(todos[todo_id])
        return todos[todo_id], 201
    
    
# 添加路由
api.add_resource(TodoList, '/todos', '/tasks')


# 响应域问题
# 定义orm数据模型
class ToDos(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):
    @marshal_with(resource_fields)
    def get(self, todo_id):
        return ToDos(todo_id=todo_id, task='task'+ str(todo_id)), 200
    

# 路由配置
api.add_resource(Todo, '/todos/<todo_id>', endpoint='todo_ep')

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


2 输入(参数解析)

2.1 参数解析器

from flask_restful import reqparse
# 1. 一般参数解析器
parser = reqparse.RequestParser()
parser.add_argument('name', type=str, required=True, help='name is not empty', dest='uName', location='')  
parser.add_argument('score', type=int, help='score is type:int', action='append') 


# 2. 复用参数解析器
parser_copy = parser.copy()


PS: 
    1.实际获取通过校验解析的参数
    	args = parser.parse_args()
    2. 自带参数校验异常响应处理
    3. type可以指定特定函数 扩展入参类型(基本数据结构+复杂数据结构)
    def  num_select(v):
        if v % 2 == 0:
            return v
        else:
            raise ValueError(f"value({v}) if invalid")
    parser.add_argument("val", type=num_select)

2.2 实例

# -*- coding:utf-8 -*-
from flask import Flask, request
from flask_restful import reqparse, Resource, Api
from werkzeug.datastructures import FileStorage

# 1.请求参数解析器对象(可每个视图类一个或者复用)
parser = reqparse.RequestParser(trim=True, bundle_errors=True)
'''
-trim: 入参是否去除空格
-bundle_errors: 捆绑输出参数名和参数异常信息
PS: 可全局配置bundle_errors 设置且覆盖所有参数解析器的配置
Flask("myFlask").config['BUNDLE_ERRORS']=True
'''

# 2.添加参数并进行解析
parser.add_argument('name', type=str, required=True, help='name is not empty', dest='uName', location='args')  # 必选参数
parser.add_argument('score', type=int, help='score is type:int', action='append')  # 多值参数
parser.add_argument('User-Agent', type=str, location='headers')  # 请求头信息
parser.add_argument('myCookie', type=str, location='cookies')  # 请求cookies信息
parser.add_argument('jsonStr', type=str, location='json')  # json
parser.add_argument('formVal', type=str, location='form')  # form
parser.add_argument('portrait', type=FileStorage, location='files')  # 文件上传
'''
add_argument 参数解析
    -name: 参数名
    -default: 参数默认值
    -type: 数据类型
    -required: 是否必选
    -help: 提示信息
    -dest: 入参别名(parse_args()实际存储的参数名)
    -ignore: 是否忽略参数类型转换失败(默认为False)
    -choices: 参数枚举值(且参数值只能是这些值)
    -location: 入参来源(位置)
        -form 表单(content-type的值是application/x-www-form-urlencoded 或者 multipart/form-data)
        -json json字符串(content-type的值是application/json)
        -headers 请求头
        -files 文本
        -cookies 请求cookies
        -args 查询字符串
        -values  等价于[form, args]
    -action: 参数值操作形式
        append 参数值追加
        store: 存储值(默认形式)
    -case_sensitive: 参数值是否区分大小写(默认True)
    -trim: 是否剔除参数名空格(默认False)
    -nullable: 是否允许参数值为null(默认为True)
    -store_missing: 当获取不到指定参数是否存储默认值(默认为True)
    PS: location 可设置多个且优先级最高的是最后一个值(location=[form, json] json优先级高)
'''


# 3.解析器继承(复用参数解析器)-在源基础上添加新参数验证
parser_copy = parser.copy()  # 本质为深拷贝
parser_copy.add_argument('bar', type=int)
parser_copy.replace_argument('bar', type=str, required=True)  # 替换特定参数的解析规则
parser_copy.remove_argument('bar')  # 移除特定参数的解析


# 4.测试资源视图类
class ReqParseTest(Resource):
    def get(self):
        """get方法"""
        # 获取参数
        args = parser.parse_args(req=None, strict=False, http_error_code=400)
        '''
            req: 可根据flask请求上下文覆盖req(默认None)
            strict: 实际入参不在参数解析器内部抛出400异常(默认False)
            http_error_code: 默认400
        '''
        print(f"GET-{args}")
        print(f"ReqHeaders:\n{request.headers}\n")
        return {"data": args, "method": "GET"}, 200
    
    def post(self):
        """post方法"""
        args = parser.parse_args()
        print(f"POST-{args}")
        '''
        POST-{'uName': 'fff', 'score': [2, 3], 'User-Agent': 'myHeader', 'myCookie': 'myCookies', 'jsonStr': None, 'formVal': 'formVal', 'portrait': <FileStorage: "'1.jpg'" ('image/jpeg')>}
        '''
        print(f"ReqHeaders:\n{request.headers}\n")
        portrait = args.get("portrait")  # 上传图片
        if portrait and isinstance(portrait, FileStorage):
            args["portrait"] = f"fileSize={len(args.get('portrait').read()) / (1024*1024)}MB"
        return {"data": args, "method": "POST"}, 200


if __name__ == '__main__':
    app = Flask(__name__)
    api = Api(app)
    api.add_resource(ReqParseTest, '/')
    app.run(debug=True)
    

# 1. GET请求
curl  "http://localhost:5000/?name=fff&score=2&score=3&formVal=formVal&jsonStr=jsonStr&User-Agent:xxx&myCookie:yyy" -H "User-Agent:myHeader"  --cookie "myCookie=myCookies"

# 2. POST请求-表单
curl  "http://localhost:5000/?name=fff&score=2&score=3" -H "User-Agent:myHeader"  --cookie "myCookie=myCookies" -F "portrait=@F:/test/data/1.jpg;filename='1.jpg'"  -F "formVal=formVal" -X POST 

# 3.POST请求-JSON
curl  "http://localhost:5000/?name=fff&score=2&score=3" -H "User-Agent:myHeader"  --cookie "myCookie=myCookies"  -H "Content-Type:application/json;charset=utf-8;" -d "{\"json_1\": \"json_1\", \"jsonStr\": \"json_2\"}" -X POST



3 输出(响应域)

3.1 响应域

flask-restful 使用fileds模块 对ORM模型类和基本实体类进行映射关联后 达到实际的数据自定义展示渲染输出能够自主控制的目的
# 简单示例
# -*- coding:utf-8 -*-
import datetime

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


# 实体类
class StuInfo:
    def __init__(self, name, class_no, age, admission_time):
        self.other_name = name
        self.class_no = class_no
        self.age = age
        self.admission_time = admission_time
     


# 响应域(实际输出的数据结构)
resource_fields = {
    'name': fields.String(attribute='other_name'),  # 别名 attribute指定实际属性名
    'cno': fields.String(attribute=lambda x: x.class_no),  # lambda表达式__call__对象属性
    'age': fields.Integer(default=12),  # default 默认值指定
    'admission_time': fields.DateTime(dt_format='iso8601'),
}

# 实体
StuInfo1 = StuInfo("aa", "c1", None, datetime.datetime.now())


# 方式一 marshal_with装饰器
class Test1(Resource):
    @marshal_with(resource_fields, envelope="result")
    def get(self):
        return StuInfo1
    '''
    {
    "result": {
        "name": "aa",
        "cno": "c1",
        "age": 12,
        "admission_time": "2023-04-03T10:24:55.526729"
        }
    }
    '''
    
    
# 方式二 marshal方法
class Test2(Resource):
    def get(self):
        return marshal(StuInfo1, resource_fields), 200
    '''
    {
    "name": "aa",
    "cno": "c1",
    "age": 12,
    "admission_time": "2023-04-03T10:25:24.180457"
    }
    '''
    
    
if __name__ == '__main__':
    app = Flask(__name__)
    api = Api(app)
    api.add_resource(Test1, '/test1')
    api.add_resource(Test2, '/test2')
    app.run(debug=True)

3.2 常规用法

# 1.常规用法
# 1.1 输出响应域格式
resource_fields = {
    'name': fields.String(attribute='other_name'),  # 别名 attribute指定实际属性名
    'cno': fields.String(attribute=lambda x: x.cno),  # lambda表达式__call__对象属性
    'age': fields.Integer(default=12),  # default 默认值指定
    'admission_time': fields.DateTime(dt_format='iso8601'),  # 日期输出格式
    'score': fields.List(fields.Integer, attribute="score_list"),  # 单属性对应多个值
    'evaluate': Evaluate(attribute="evaluate"),  # 自定义响应字段
    'school_info': fields.Nested(school_info_resource),  # 嵌套响应字段1
    'score_detail': fields.List(fields.Nested(score_detail_resource)),  # 嵌套字段2
    'api_absolute': fields.Url('hello', absolute=True, scheme='http'),  # 其他(absolute全API信息|scheme匹配特征协议)
    'api_simple': fields.Url('hello',)
}

# 1.2 嵌套响应域格式
school_info_resource = {
    "sName": fields.String,
    "addr": fields.String
}
resource_fields = {
    'school_info': fields.Nested(school_info_resource),  # 嵌套响应字段
}

# 1.3 自定义响应字段
class Evaluate(fields.Raw):
    def format(self, value):
        return "良好" if not value else "优秀"
    
# 2. 全局指定


3.3 实例

# -*- coding:utf-8 -*-
import datetime

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


# 自定义响应字段
class Evaluate(fields.Raw):
    def format(self, value):
        return "良好" if not value else "优秀"
    
    
# 嵌套响应字段1
school_info_resource = {
    "sName": fields.String,
    "addr": fields.String
}

# 嵌套响应字段2
score_detail_resource = {
    "subject": fields.String(attribute=lambda x: x.subject),
    "score": fields.Integer
}

# 响应域(实际输出的数据结构)
resource_fields = {
    'name': fields.String(attribute='other_name'),  # 别名 attribute指定实际属性名
    'cno': fields.String(attribute=lambda x: x.cno),  # lambda表达式__call__对象属性
    'age': fields.Integer(default=12),  # default 默认值指定
    'admission_time': fields.DateTime(dt_format='iso8601'),  # 日期输出格式
    'score': fields.List(fields.Integer, attribute="score_list"),  # 单属性对应多个值
    'evaluate': Evaluate(attribute="evaluate"),  # 自定义响应字段
    'school_info': fields.Nested(school_info_resource),  # 嵌套响应字段1
    'score_detail': fields.List(fields.Nested(score_detail_resource)),  # 嵌套字段2
    'api_absolute': fields.Url('hello', absolute=True, scheme='http'),  # 其他(absolute全API信息|scheme匹配特征协议)
    'api_simple': fields.Url('hello',)
}


# ORM实体类
class SchoolInfo:
    def __init__(self):
        self.sName = "学校名"
        self.addr = "学校地址"
        
        
class ScoreInfo:
    def __init__(self):
        self.subject = "语文"
        self.score = 85


class StuInfo:
    def __init__(self, **stu_info):
        self.other_name = stu_info.get("name")
        self.cno = stu_info.get("cno")
        self.age = stu_info.get("age")
        self.admission_time = stu_info.get("admission_time")
        self.score_list = stu_info.get("score_list")
        self.evaluate = stu_info.get("evaluate")
        self.school_info = stu_info.get("school_info")
        self.score_detail = stu_info.get("score_detail")


# pv访问资源类
class StuResource(Resource):
    @marshal_with(resource_fields)
    def get(self):
        # 查询输出特定实体类
        stu_info = {
            'name': "n1",  # 别名 attribute指定实际属性名
            'cno': "c1",  # lambda表达式__call__对象属性
            'age': None,  # default 默认值指定
            'admission_time': datetime.datetime.now(),  # 日期输出格式
            'score_list': [88, 85],  # 单属性对应多个值
            'evaluate': 0,  # 自定义响应字段
            'school_info': SchoolInfo(),
            'score_detail': ScoreInfo()
        }
        s1 = StuInfo(**stu_info)
        return s1
    
    
if __name__ == '__main__':
    app = Flask(__name__)
    api = Api(app)
    api.add_resource(StuResource, "/", endpoint='hello')
    app.run(debug=True)


4 扩展

4.1 全局响应内容自定义

# -*- coding:utf-8 -*-
'''
flask-restful 扩展知识
'''
import json

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


# 响应域(实际输出的数据结构)
resource_fields = {
    'name': fields.String(attribute='other_name'),  
    'cno': fields.String(attribute=lambda x: x.cno),  
    'age': fields.Integer(default=12), 
}


class StuInfo:
    def __init__(self, **stu_info):
        self.other_name = stu_info.get("name")
        self.cno = stu_info.get("cno")
        self.age = stu_info.get("age")


# pv访问资源类
class StuResource(Resource):
    @marshal_with(resource_fields)
    def get(self):
        # 查询输出特定实体类
        stu_info = {
            'name': "n1",  
            'cno': "c1",  
            'age': None,  
        }
        s1 = StuInfo(**stu_info)
        return s1
    
    
# 方式一
# 1.1 json数据自定义格式输出
def out_json(data, code, headers=None):
    resp = make_response(json.dumps({"code": 0, "msg": "", "data": data}), code)
    resp.headers.extend(headers or {})
    return resp


# 1.2 MyApi继承Api并重写
class MyApi(Api):
    def __init__(self, *args, **kwargs):
        super(MyApi, self).__init__(*args, **kwargs)
        self.representations = {
            "application/json": out_json,
            # 支持多种类型返回格式定义
            # "application/xml": out_xml,
            # "text/html": out_html,
            # "text/csv": out_csv
        }
    
    
if __name__ == '__main__':
    app = Flask(__name__)
    api = MyApi(app)  # 方式一
    api.add_resource(StuResource, "/", endpoint='hello')
    app.run(debug=True)

# -*- coding:utf-8 -*-
'''
flask-restful 扩展知识
'''
from functools import wraps

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


common_res = {
    "code": fields.Integer(default=0),
    "msg": fields.String(default="")
}


def response_return(func):
    @wraps(func)
    def inner_wraps(*args, **kwargs):
        data = func(*args, **kwargs)
        data = data if data else None
        return_data = marshal(None, common_res)
        return_data["data"] = data
        return return_data, 200
    return inner_wraps


# 2.2 自定义资源类
class MyResource(Resource):
    method_decorators = [response_return]  # 继承MyResource的子类会全局生效


# 响应域(实际输出的数据结构)
resource_fields = {
    'name': fields.String(attribute='other_name'),  # 别名 attribute指定实际属性名
    'cno': fields.String(attribute=lambda x: x.cno),  # lambda表达式__call__对象属性
    'age': fields.Integer(default=12),  # default 默认值指定
}


# ORM实体类
class StuInfo:
    def __init__(self, **stu_info):
        self.other_name = stu_info.get("name")
        self.cno = stu_info.get("cno")
        self.age = stu_info.get("age")


# pv访问资源类
class StuResource(MyResource):
    @marshal_with(resource_fields)
    def get(self):
        # 查询输出特定实体类
        stu_info = {
            'name': "n1",
            'cno': "c1",
            'age': None,
        }
        s1 = StuInfo(**stu_info)
        return s1
    
    
if __name__ == '__main__':
    app = Flask(__name__)
    api = Api(app)  # 方式二
    api.add_resource(StuResource, "/", endpoint='hello')
    app.run(debug=True)


4.2 资源类-装饰器应用

# -*- coding:utf-8 -*-
import time
from functools import wraps

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


# 装饰器作用方法或者资源类Resource
# 1. 自定义装饰器-局部作用范围
def cal_time(func):
    @wraps(func)
    def inner(*args, **kwargs):
        st = time.time()
        data = func(*args, **kwargs)
        data = data if data else {}
        time.sleep(0.1)
        et = round((time.time() - st), 2)
        data.update({'costTime(s)': et, "dataType": str(type(data))})
        return data
    return inner


# 2. 自定义装饰器-全局作用范围
def response_return(func):
    @wraps(func)
    def inner_wraps(*args, **kwargs):
        data = func(*args, **kwargs)
        data = data if data else None
        return_data = marshal(None, {"code": fields.Integer(default=0), "msg": fields.String(default="")})
        return_data["data"] = data
        return return_data, 200
    return inner_wraps


# 自定义资源类
class MyResource(Resource):
    method_decorators = [response_return]  # 继承MyResource的子类会全局生效
    

# 响应域(实际输出的数据结构)
resource_fields = {
    'name': fields.String(attribute='other_name'),
    'cno': fields.String(attribute=lambda x: x.cno),
    'age': fields.Integer(default=12),
}


# ORM实体类
class StuInfo:
    def __init__(self, **stu_info):
        self.other_name = stu_info.get("name")
        self.cno = stu_info.get("cno")
        self.age = stu_info.get("age")


# pv访问资源类
class StuResource(MyResource):
    method_decorators = {'get': [cal_time]}  # 特定资源类生效(覆盖全局)
    
    @marshal_with(resource_fields)
    def get(self):
        # 查询输出特定实体类
        stu_info = {
            'name': "n1",
            'cno': "c1",
            'age': None,
        }
        s1 = StuInfo(**stu_info)
        return s1
    
    
if __name__ == '__main__':
    app = Flask(__name__)
    api = Api(app)  # 方式二
    api.add_resource(StuResource, "/", endpoint='hello')
    app.run(debug=True)


4.3 蓝图应用

# -*- coding:utf-8 -*-
from flask import Flask, Blueprint
from flask_restful import Api, Resource


# 访问资源类
class StuResource(Resource):
    def get(self):
        return "OK"
    

if __name__ == '__main__':
    app = Flask(__name__)
    api_bp = Blueprint('api', "api_bp")  # 创建蓝图
    api = Api(api_bp)  # api关联蓝图
    api.add_resource(StuResource, "/", endpoint='api_bp')  # 资源类和路由关联映射
    app.register_blueprint(api_bp)  # 注册蓝图
    app.run(debug=True)

4.4 资源类引用外部依赖

# -*- coding:utf-8 -*-
'''
初始化资源类完成外呼依赖引入
'''
from flask import Flask
from flask_restful import Api, Resource


# 访问资源类
class StuResource(Resource):
    def __init__(self, *args, **kwargs):
        self.outside_dependencies = kwargs['od']
    
    def get(self):
        return self.outside_dependencies


if __name__ == '__main__':
    app = Flask(__name__)
    api = Api(app)
    api.add_resource(StuResource, "/", endpoint='hello', resource_class_kwargs={"od": "外部依赖引用"})
    app.run(debug=True)


5. 参考文档

[1] flask-restful https://flask-restful.readthedocs.io/en/latest/index.html

posted @   爱编程_喵  阅读(81)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
jQuery火箭图标返回顶部代码

jQuery火箭图标返回顶部代码

滚动滑动条后,查看右下角查看效果。很炫哦!!

适用浏览器:IE8、360、FireFox、Chrome、Safari、Opera、傲游、搜狗、世界之窗.

点击右上角即可分享
微信分享提示