flask插件系列之flask_restful设计API
前言
flask框架默认的路由和视图函数映射规则是通过在视图函数上直接添加路由装饰器来实现的,这使得路由和视图函数的对应关系变得清晰,但对于统一的API开发就变得不怎么美妙了,尤其是当路由接口足够多的时候,可读性会变差。flask_restful可以使我们像Django那样统一在一个地方设计所有的API规则。
flask_restful
安装
pip install flask_restful
初始化
# __init__.py
from flask import Flask, current_app
app = Flask(__name__)
app.config['SECRET_KEY'] = '123'
# rest/__init___.py
from flask import Blueprint
from flask_restful import Api
# 创建蓝图
rest = Blueprint("rest", __name__)
flask_api = Api(app=rest)
__all__ = ["rest","api"]
# 加载蓝图所对应的视图上下文
from .api import *
# 设计路由
flask_api.add_resource(ToDo1,'/restful')
flask_api.add_resource(Todo,"/restful1")
# rest/api.py
from flask_restful import Resource, fields, marshal_with, reqparse
parser = reqparse.RequestParser()
class ToDo1(Resource):
def get(self):
# 拷贝一个对象备用
new_parser = parser.copy()
# 添加请求参数和它的验证规则
new_parser.add_argument('n', type=int, help='Rate to charge for this resource,{error_msg}')
# 进行验证,得到通过验证后的字典或者抛出400错误
args = new_parser.parse_args(strict=True)
print(args)
return "OK"
说明:
Api类管理着视图函数的注册和相关资源,使用app或蓝图对象对其进行初始化;
我们定义一个类继承Resource,通过方法名指定post、get等请求方式的处理逻辑;
使用Api.add_resource方法设计api接口,指定路由和视图函数的映射关系;
使用RequestParser对象对参数进行验证;
核心的对象有Api和Resource,针对其一一分析;
APi对象分析
- 初始化
针对需要使用restful风格的蓝图进行初始化。
test = Blueprint("test", __name__)
api = Api(app=test, prefix='/rest1') # prefix添加url的前缀,如果蓝图也有前缀则拼接
- Api.add_resource
该方法对路由和视图函数进行设计设定。创建了RULE对象,并将其添加到Maps对象中,同时将resource添加到app的view_functions中;
# 参数
resource:视图类
urls:路由url,可以多个指向一个视图类
endpoint:视图函数的标志,默认用类名替代
api.add_resource(Todo,"/restful","/index", endpoint="restful")
请求数据验证
对于收到的每一个请求,如果它带了参数,后台是需要对参数进行验证的,这是一个十分枯燥的工作,RequestParser对象就是帮助我们对参数进行验证的,它可以实现验证方法对验证参数的解耦,是的代码结构趋向统一。
- RequestParser
# 创建一个RequestParser,其会拦截request上下文,但是其内部存储的参数列表对所有的请求都是同一个,因此针对每个请求都需要一个新的对象
parser = reqparse.RequestParser()
class ToDo1(Resource):
def get(self):
# 拷贝一个对象备用
new_parser = parser.copy()
# 向参数列表添加请求参数和它的验证规则,每个参数单独添加
new_parser.add_argument('n', type=int, help='Rate to charge for this resource,{error_msg}')
# 进行验证,得到通过验证后的字典或者抛出400错误
args = new_parser.parse_args(strict=True)
print(args)
return "OK"
# 使用 strict=True 调用 parse_args 能够确保当请求包含你的解析器中未定义的参数的时候会抛出一个异常。
- RequestParser.add_argument
该方法其实是创建了一个Argument对象,其参数对应了Argument的初始化参数
def __init__(self, name, default=None, dest=None, required=False,
ignore=False, type=text_type, location=('json', 'values',),
choices=(), action='store', help=None, operators=('=',),
case_sensitive=True, store_missing=True, trim=False,
nullable=True):
self.name = name # 参数的名字
self.default = default # 如果没有该参数,其默认的值
self.dest = dest # name的别名,解析后该参数的键编程别名
self.required = required # 参数是否可以省略,设置为True时,这个参数是必须的
self.ignore = ignore # 是否忽略参数类型转换失败的情况,默认不忽略
self.location = location # 从请求对象中获取的参数类型,默认从values和json中解析值,但是可以指定'args','form','json',''headers','files'等解析;或指定列表['args','form'],优先解析列表末尾的值
self.type = type # 转换的参数类型,默认转换成str,失败就报错
self.choices = choices # 参数的值的有限集合,不在其中就报错,如[1,2,3]
self.action = action # 如果要接受一个键有多个值的话,可以传入 action='append', 默认是禁止的
self.help = help # 参数无效时可以返回的信息,默认返回错误信息
self.case_sensitive = case_sensitive # 是否区分大小写,默认所有大写转换成小写
self.operators = operators
self.store_missing = store_missing # 缺少参数时是否加默认的值
self.trim = trim # 是否去除参数周围的空白
self.nullable = nullable # 是否允许参数使用空值
new_parser.add_argument('n', type=int, help='msgerr',location=('args',), required=True) # 从参数中获取n,该参数是必须的
- RequestParser.replace_argument和RequestParser.remove_argument
覆盖解析规则和删除解析规则,主要用在当多个api有相同的解析参数时,可以设置共享参数解析;
parser = RequestParser()
parser.add_argument('foo', type=int)
parser_copy = parser.copy()
parser_copy.add_argument('bar', type=int)
parser_copy.replace_argument('foo', type=str, required=True, location='json')
验证方法
Argument对象的type参数指定了参数的验证方法,除了我们python常用的数据类型外,inputs模块预定义了一些验证方法供我们验证。
- input模块
parser_copy.add_argument('bar', type=url) # 验证是否符合url格式
parser_copy.add_argument('bar', type=inputs.regex('^[0-9]+$')) # 自定义正则验证
date:转换成datatime.data对象;
natural:转换类型为自然数;
positive:转换类型为正整数
int_range:输入整数的范围,设置最大最小值
boolean:限制必须为布尔类型;
- 自定义验证方法
input模块提供的方法也是不够用的。很多时候我们需要自定义验证方法:
def ver_num(value, name):name表示参数名
if value % 2 == 0:
raise ValueError("Value is not odd")
return value
parser_copy.add_argument('bar', type=ver_num) # 验证是调用:ver_num("xxx","bar")
# 方法必须有两个参数,第一个value表示参数的值,name表示参数的名字,
# 如果验证不通过,我们就抛出一个ValueError错误。
- RequestParser.parse_args
当调用parse_args时就会按添加时的顺序依次验证请求参数,如果有验证不通过的参数,调用flask.abort(400);
如果设置了strict=True,请求包含解析器中未定义的参数也会抛出400状态码。
parse_args
#参数:
req:请求上下文,默认使用flask.request
strict:请求是否可以包含解析器中未定义的参数,默认false,即可以。
实际使用时如果我们想改变抛出异常的错误处理就最好捕捉一下异常:
class ToDo1(Resource):
try:
new_parser = parser.copy()
new_parser.add_argument('n', type=int, help='Rate to charge for this resource,{error_msg}')
args = new_parser.parse_args(strict=True)
except HTTPException as e:
do somthing...
raise e
return "OK"
小结
对于RequestParser来说,其主要的功能对请求的参数进行验证,验证通过反馈参数字典,否则抛出400异常;
我们可以设置自定义的验证类型来验证额外的需求;
格式化返回数据
通过marshal_with装饰器,我们可以对视图函数返回的数据进行格式化,使得呈现给前端的数据符合我们预期要返回的数据格式,而不需要去手动使用代码转化;
class Todo(Resource):
@marshal_with({'data':fields.String}, envelope='resource')
def get(self, **kwargs):
return {'data':'xiao'}
# 前端数据
{"resource": {"data": "xiao"}}
# 参数
fields:数据规则;
envelope:数据的键,如果定义了该值,数据就以该值为键返回。
使用方式
- 定义想要返回的数据格式,可以是复杂的嵌套结构
resource_dict = {"status_code": fields.Integer,
"message": {"count": fields.Integer, "newcount": fields.Integer}}
但是我们视图函数的返回值可以是扁平结构:
resource_fields = {"status_code":fields.Integer,
"data":{"count": fields.Integer, "newcount": fields.List(fields.Integer)}},
@app.route('/')
@marshal_with(resource_fields,
envelope='message')
def index(self):
return {"status_code":200, "count":10, "newcount": [5,6]}
marshal_with会将你返回的数据和预定义的数据进行比较,存在的就填充,不存在就用None替代,用envelope为键将结果包裹;装饰过后视图函数的返回结果是OrderedDict对象。
- 自定义返回的数据类型
restful模块默认对前端返回的是json格式的字符串数据,所有的Api都只支持json,即所有视图返回的数据都会默认尝试转化成json格式,不成功就会报错。我们可以指定APi默认输出的格式,如html,csv,json等;
# 设置输出html格式,所有视图返回的数据都会默认尝试转化成text/html格式
rest_api = Api(app)
@rest_api.representation('text/html')
def output_html(data, code, headers=None):
resp = make_response(data, code)
resp.headers.extend(headers or {})
return resp
#或者这样写:
rest_api.representations = OrdereDict({'text/html':output_html})
# 同理如果想设置其他格式的处理函数
@rest_api.representation('application/json')
def output_json(data, code, headers=None):
pass
@api.representation('text/csv')
def output_csv(data, code, headers=None):
pass
# 或者
rest_api.representations = OrdereDict({'application/json':output_json})
rest_api.representations = OrdereDict({'text/csv':output_csv})
fields模块
fields模块可以说是专为json数据交互准备的,被marshal_with装饰的视图函数必须返回字典对象,它定义了许多的针对视图函数对外的格式化处理方法。
resource_fields = {
'name': fields.String,
'address': fields.String,
'date_updated': fields.DateTime(dt_format='rfc822'),
}
# 相应的字段有:
"String":字符串,参数attribute指定别名,default指定默认值;
"FormattedString":格式化字符串,参数如"name{age}"这种形式
"Url":生成url,参数endpoint和url_for的参数类似,根据它找到对应的url,参数absolute=True则生成绝对url,参数scheme='https'则修改协议
"DateTime":转化日期的标准格式,如datetime.datetime(2018,11,23)--》"Fri, 23 Nov 2018 00:00:00 -0000"
"Float":小数;
"Integer":整形,默认为0;
"Arbitrary":任意精度的浮点数
"Nested":嵌套结构;
"List":列表类型,参数cls_or_instance指定列表元素的类型,如:List(String)
"Raw":基类,参数attribute指定别名,default指定默认值;
"Boolean":布尔值,返回True或False
"Fixed":固定精度的十进制数,参数decimals指定精度;
"Price":Fixed的引用;
- 如果内置的字段不满足我们的要求,我们可以自定义:
class UrgentItem(fields.Raw):
def format(self, value):
return "big" if value == 1 else "small"
# 只需要继承Raw并实现format函数即可
参考:
flask插件系列之flask_restful设计API的更多相关文章
- flask插件系列之flask_uploads上传文件
前言 flask可以实现上传文件和下载文件的基本功能,但如果想要健壮的功能,使用flask_uploads插件是十分方便的. 安装 pip install flask_uploads 基本使用 # e ...
- flask插件系列之flask_caching缓存
前言 为了尽量减少缓存穿透,同时减少web的响应时间,我们可以针对那些需要一定时间才能获取结果的函数和那些不需要频繁更新的视图函数提供缓存服务,可以在一定的时间内直接返回结果而不是每次都需要计算或者从 ...
- flask插件系列之flask_session会话机制
flask_session是flask框架实现session功能的一个插件,用来替代flask自带的session实现机制. 配置参数详解 SESSION_COOKIE_NAME 设置返回给客户端的c ...
- flask插件系列之Flask-WTF表单
flask_wtf是flask框架的表单验证模块,可以很方便生成表单,也可以当做json数据交互的验证工具,支持热插拔. 安装 pip install Flask-WTF Flask-WTF其实是对w ...
- flask插件系列之flask_celery异步任务神器
现在继续学习在集成的框架中如何使用celery. 在Flask中使用celery 在Flask中集成celery需要做到两点: 创建celery的实例对象的名字必须是flask应用程序app的名字,否 ...
- flask插件系列之flask_cors跨域请求
前后端分离在开发调试阶段本地的flask测试服务器需要允许跨域访问,简单解决办法有二: 使用flask_cors包 安装 pip install flask_cors 初始化的时候加载配置,这样就可以 ...
- flask插件系列之SQLAlchemy基础使用
sqlalchemy是一个操作关系型数据库的ORM工具.下面研究一下单独使用和其在flask框架中的使用方法. 直接使用sqlalchemy操作数据库 安装sqlalchemy pip install ...
- Flask插件系列之flask_celery
现在继续学习在集成的框架中如何使用celery. 在Flask中使用celery 在Flask中集成celery需要做到两点: 创建celery的实例对象的名字必须是flask应用程序app的名字,否 ...
- flask插件系列之SQLAlchemy实用技巧
下面记录一下SQLAlchemy使用的技巧. 在多模块下定义models 如果由多个蓝图下读定义了model模块,在初始化的时候需要加载到上下文中. 当使用flask_Migrate迁移数据库的时候, ...
随机推荐
- Spring 学习7 -事务
1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是 ...
- 第157天:canvas基础知识详解
目录 一.canvas简介 1.1 什么是canvas?(了解) 1.2 canvas主要应用的领域(了解) 二.canvas绘图基础 2.0 sublime配置canvas插件(推荐) 2.1 Ca ...
- 对Spark2.2.0文档的学习2-Job Scheduling
Job Scheduling Link:http://spark.apache.org/docs/2.2.0/job-scheduling.html 概况: (1)集群中多个应用的调度主要考虑的是不同 ...
- 【bzoj4695】最假女选手 线段树区间最值操作
题目描述 给定一个长度为 N 序列,编号从 1 到 N .要求支持下面几种操作:1.给一个区间[L,R] 加上一个数x 2.把一个区间[L,R] 里小于x 的数变成x 3.把一个区间[L,R] 里大于 ...
- Eclipse中使用git提交代码,报错Testng 运行Cannot find class in classpath的解决方案
一.查找原因方式 1.点击Project——>Clear...——>Build Automatically 2.查看问题 二.报错因素 1.提交.xlsx文件 2.提交时,.xlsx文件被 ...
- VC 生成后事件 Post-Build Event
原文链接地址:https://blog.csdn.net/jfkidear/article/details/27313643.https://blog.csdn.net/kevindr/article ...
- Sort Integers II
Given an integer array, sort it in ascending order. Use quick sort, merge sort, heap sort or any O(n ...
- SNMP-网络管理协议
SNMP协议简介: a. 轮询(Polling) -- 定时获取状态, 中断(Interrupt)--出问题通知 b. 共同体名(community) -- 口令--只读口令 --读写口令 使用SNM ...
- Python相关资料收集
读写Excel: http://blog.csdn.net/five3/article/details/7034826http://tech.ddvip.com/2012-10/13515777031 ...
- python学习笔记(四) 思考和准备
一.zip的坑 zip()函数接收多个可迭代数列,将数列中的元素重新组合,在3.0中返回迭代器指向 数列首地址,在3.0以下版本返回List类型的列表数列.我用的是3.5版本python, 所以zip ...