前言

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的更多相关文章

  1. flask插件系列之flask_uploads上传文件

    前言 flask可以实现上传文件和下载文件的基本功能,但如果想要健壮的功能,使用flask_uploads插件是十分方便的. 安装 pip install flask_uploads 基本使用 # e ...

  2. flask插件系列之flask_caching缓存

    前言 为了尽量减少缓存穿透,同时减少web的响应时间,我们可以针对那些需要一定时间才能获取结果的函数和那些不需要频繁更新的视图函数提供缓存服务,可以在一定的时间内直接返回结果而不是每次都需要计算或者从 ...

  3. flask插件系列之flask_session会话机制

    flask_session是flask框架实现session功能的一个插件,用来替代flask自带的session实现机制. 配置参数详解 SESSION_COOKIE_NAME 设置返回给客户端的c ...

  4. flask插件系列之Flask-WTF表单

    flask_wtf是flask框架的表单验证模块,可以很方便生成表单,也可以当做json数据交互的验证工具,支持热插拔. 安装 pip install Flask-WTF Flask-WTF其实是对w ...

  5. flask插件系列之flask_celery异步任务神器

    现在继续学习在集成的框架中如何使用celery. 在Flask中使用celery 在Flask中集成celery需要做到两点: 创建celery的实例对象的名字必须是flask应用程序app的名字,否 ...

  6. flask插件系列之flask_cors跨域请求

    前后端分离在开发调试阶段本地的flask测试服务器需要允许跨域访问,简单解决办法有二: 使用flask_cors包 安装 pip install flask_cors 初始化的时候加载配置,这样就可以 ...

  7. flask插件系列之SQLAlchemy基础使用

    sqlalchemy是一个操作关系型数据库的ORM工具.下面研究一下单独使用和其在flask框架中的使用方法. 直接使用sqlalchemy操作数据库 安装sqlalchemy pip install ...

  8. Flask插件系列之flask_celery

    现在继续学习在集成的框架中如何使用celery. 在Flask中使用celery 在Flask中集成celery需要做到两点: 创建celery的实例对象的名字必须是flask应用程序app的名字,否 ...

  9. flask插件系列之SQLAlchemy实用技巧

    下面记录一下SQLAlchemy使用的技巧. 在多模块下定义models 如果由多个蓝图下读定义了model模块,在初始化的时候需要加载到上下文中. 当使用flask_Migrate迁移数据库的时候, ...

随机推荐

  1. webgl学习笔记四-动画

    写在前面 建议先阅读下前面我的三篇文章. webgl学习笔记一-绘图单点 webgl学习笔记二-绘图多点 webgl学习笔记三-平移旋转缩放   下面我们将讲解下如何让一个正方形动起来~不断擦除和重绘 ...

  2. 【bzoj3518】点组计数 欧拉函数(欧拉反演)

    题目描述 平面上摆放着一个n*m的点阵(下图所示是一个3*4的点阵).Curimit想知道有多少三点组(a,b,c)满足以a,b,c三点共线.这里a,b,c是不同的3个点,其顺序无关紧要.(即(a,b ...

  3. 基于三个kinect的人体建模

       单个kinect的人体重建,在Kinect SDK 1.8中,Kinect Fusion的效果已经很不错了.其缺点显而易见,一是扫描时间长,重建对象也需要长时间保持静态:二是需要人体或者kine ...

  4. xpose修改手机imei码,注入广告

      何为hook Hook英文翻译过来就是“钩子”的意思,那我们在什么时候使用这个“钩子”呢? 我们知道,在Android操作系统中系统维护着自己的一套事件分发机制.应用程序,包括应用触发事件和后台逻 ...

  5. CF891E [数学题]

    1.答案=初始乘积-最终乘积的期望.然后直接dp+ntt是O(nklogk) 2.考虑展开式子ans=sum(a[i]-b[i]),大概感受一下未知数个数相同的项系数相同,问题在于如何求系数 3.没思 ...

  6. linux 递归删除目录文件

    比如删.svn文件 >find . -name ".svn" | xargs -exec rm -rf

  7. 修改Docker默认镜像和容器的存储位置

    一.Why Docker默认的镜像和容器存储位置在/var/lib/docker中,如果仅仅是做测试,我们可能没有必要修改,但是当大量使用的时候,我们可能就要默认存储的位置了. 二.How 2.1 修 ...

  8. centos pure-ftpd配置及错误解决

    使用yum安装pure-ftpd Pure-FTPd是Linux上的一个开源的FTP服务程序,在易用性.配置性上比vsftp较方便,下面我们使用centos6演示安装和配置pure-ftpd. 安装e ...

  9. js-之闭包的理解

    说说你对闭包的理解? 答:闭包是能够读取其它函数内部变量的函数.本质上闭包是将函数内部和函数外部连接起来的一座桥梁.由于js的链式作用域,因为函数也是对象,函数内部访问函数外部的变量就类似于子对象一级 ...

  10. 高可用rabbitmq集群服务部署步骤

    消息队列是非常基础的关键服务,为保证公司队列服务的高可用及负载均衡,现通过如下方式实现: RabbitMQ Cluster + Queue HA + Haproxy + Keepalived 3台ra ...