flask_restful(转载)
<div id="post_detail">
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函数即可
参考:
</div>
<div class="postDesc">posted @ <span id="post-date">2018-09-26 22:30</span> <a href="https://www.cnblogs.com/cwp-bg/">天宇之游</a> 阅读(<span id="post_view_count">306</span>) 评论(<span id="post_comment_count">0</span>) <a href="https://i.cnblogs.com/EditPosts.aspx?postid=9687222" rel="nofollow">编辑</a> <a href="#" onclick="AddToWz(9687222);return false;">收藏</a></div>
</div>
<script src="//common.cnblogs.com/highlight/9.12.0/highlight.min.js"></script><script>markdown_highlight();</script><script type="text/javascript">var allowComments=true,cb_blogId=349326,cb_entryId=9687222,cb_blogApp=currentBlogApp,cb_blogUserGuid='585e0a6f-e112-e711-845c-ac853d9f53ac',cb_entryCreatedDate='2018/9/26 22:30:00';loadViewCount(cb_entryId);var cb_postType=1;var isMarkdown=true;</script>
【推荐】百度云“猪”你开年行大运,红包疯狂拿,低至1折
【推荐】专业便捷的企业级代码托管服务 - Gitee 码云
【活动】2019第四届全球人工技术大会解码“智能+时代”
</div><!--end: forFlow -->
</div>
flask_restful(转载)的更多相关文章
- Crystal Clear Applied: The Seven Properties of Running an Agile Project (转载)
作者Alistair Cockburn, Crystal Clear的7个成功要素,写得挺好. 敏捷方法的关注点,大家可以参考,太激动所以转载了. 原文:http://www.informit.com ...
- RTP与RTCP协议介绍(转载)
RTSP发起/终结流媒体.RTP传输流媒体数据 .RTCP对RTP进行控制,同步.RTP中没有连接的概念,本身并不能为按序传输数据包提供可靠的保证,也不提供流量控制和拥塞控制,这些都由RTCP来负责完 ...
- 《Walking the callstack(转载)》
本文转载自:https://www.codeproject.com/articles/11132/walking-the-callstack Download demo project with so ...
- [转载]MVVM模式原理分析及实践
没有找到很好的MVVM模式介绍文章,简单找了一篇,分享一下.MVVM实现了UI\UE设计师(Expression Blend 4设计界面)和软件工程师的合理分工,在SilverLight.WPF.Wi ...
- [转载]:STM32为什么必须先配置时钟再配置GPIO
转载来源 :http://blog.csdn.net/fushiqianxun/article/details/7926442 [原创]:我来添两句,就是很多同学(包括我)之前搞低端单片机,到了stm ...
- [转载]从MyEclipse到IntelliJ IDEA-让你摆脱鼠标,全键盘操作
从MyEclipse转战到IntelliJ IDEA的经历 注转载址:http://blog.csdn.net/luoweifu/article/details/13985835 我一个朋友写了一篇“ ...
- TCP同步与异步,长连接与短连接【转载】
原文地址:TCP同步与异步,长连接与短连接作者:1984346023 [转载说明:http://zjj1211.blog.51cto.com/1812544/373896 这是今天看到的一篇讲到T ...
- 在CentOS 7/6.5/6.4 中安装Java JDK 8(转载)
转载在CentOS 7/6.5/6.4 中安装Java JDK 8 首先,在你的服务器上运行一下更新. yum update 然后,在您的系统上搜索,任何版本的已安装的JDK组件. rpm -qa | ...
- 用C#实现MD5的加密(转载)
方法一 首先,先简单介绍一下MD5 MD5的全称是message-digest algorithm 5(信息-摘要算法,在90年代初由mit laboratory for computer scien ...
随机推荐
- jvisualvm远程监控服务器tomcat
1.在 {服务器tomcat路径}/bin/catalina.sh 中,的[# OS specific support. $var _must_ be set to either true or f ...
- Java研发书单
Java研发书单 计算机基础:<深入理解计算机系统><计算机网络> 网络方面:<TCP/IP协议卷一><unix网络编程卷一>(部分章节,JAVA主要是 ...
- Oracle 更新Opatch、打补丁
1.更新Opatch; 2.打补丁; 3.grid 打补丁; 1.更新Opatch(实验版本:oracle:11.2.0.3.0): 默认安装数据库后,在ORACLE_HOME 下会有个OPatch ...
- 4.4.1 CAS详解
一.什么是CAS CAS,compare and swap的缩写,中文翻译成比较并交换. 我们都知道,在java语言之前,并发就已经广泛存在并在服务器领域得到了大量的应用.所以硬件厂商老早就在芯片中加 ...
- org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'deptDao_a' defined in class path resource [beansAndHibernate.xml]: Cannot resolve reference to bean 'sessionFact
Error creating bean with name 'deptDao_a' defined in class path 因为更改了类的名字,所以其setter方法没有更改,需要 private ...
- CodeForces 288B Polo the Penguin and Houses (暴力或都快速幂)
题意:给定 n 和k,n 表示有n个房子,然后每个有一个编号,一只鹅要从一个房间中开始走,下一站就是房间的编号,现在要你求出有多少种方法编号并满足下面的要求: 1.如果从1-k房间开始走,一定能直到 ...
- 编写高质量代码改善C#程序的157个建议——建议107:区分静态类和单例
建议107:区分静态类和单例 有一种观点认为:静态类可以作为单件模式的一种实现方式.事实上,这是不妥当的.按照传统的观点来看,单例是一个实例对象.而静态类并不满足这一点.静态类也直接违反面向对象三大特 ...
- Arcgis Android 常见问题
关于arcgis android 安装包较大的问题 如果想缩小大小,可以只保留armeabi,只是这样就不支持x86类型cpu的手机了. 可以考虑做成单独的版本,供用户下载. 即打2个包,一个供普通a ...
- ES6——数据结构 Map
数据结构 Map 字典: 用来存储不重复key的 Hash结构.不同于集合(Set)的是,字典使用的是 [键,值] 的形式来存储数据的. JavaScript 的对应那个(Object:{}) 只能用 ...
- python - seletors实现IO多路复用
服务端 from socket import * import selectors sel = selectors.DefaultSelector() def accept(server_fileob ...