前言

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. 【Linux笔记】阿里云服务器被暴力破解

    一.关于暴力破解 前几天新购进了一台阿里云服务器,使用过程中时常会收到“主机被暴力破解”的警告,警告信息如下: 云盾用户您好!您的主机:... 正在被暴力破解,系统已自动启动破解保护.详情请登录htt ...

  2. postman md5加密 然后传给下一个接口作为参数调用

    https://www.v2ex.com/api/nodes/show.json?name=python get请求 断言: tests["Body matches string" ...

  3. DateTime Toxxx() 方法获取时间

    直接上代码 static void Main(string[] args) { DateTime time = DateTime.Now; Console.WriteLine("ToFile ...

  4. UVA12546_LCM Pair Sum

    题目的意思是求 [西伽马(p+q)]其中lcm(p,q)=n. 又见数论呀. 其实这个题目很简单,考虑清楚了可以很简单的方法飘过. 我一开始是这样来考虑的. 对于每一个单独的质因子,如果为p,它的次数 ...

  5. ZOJ2290_Game

    题目意思是这样的,给定一个数N,第一个可以减去任意一个数(不能为N本身),然后接下来轮流减去一个数字,下一个人减去的数字必须大于0,且不大于2倍上一次被减去的数字. 把N减为0的人获胜. 看完题目后不 ...

  6. 【JavaScript】checkBox的多选行<tr>信息获取

    页面的列表table显示(后台model.addAttribute("page", page);传来page信息,page通过foreach标签迭代展示表格数据): <!-- ...

  7. C++解析-外传篇(2):函数的异常规格说明

    0.目录 1.异常规格说明 2.unexpected() 函数 3.小结 1.异常规格说明 问题: 如何判断一个函数是否会抛出异常,以及抛出哪些异常? C++提供语法用于声明函数所抛出的异常 异常声明 ...

  8. 英文报道:China challenged Australian warships in South China Sea, reports say

    学习地道新闻英语表达,以下文章来自CNN By Ben Westcott and Jamie Tarabay, CNN Updated 0830 GMT (1630 HKT) April 20, 20 ...

  9. 深入理解JVM一内存模型、可见性、指令重排序

    一.内存模型 首先我们思考一下一个java线程要向另外一个线程进行通信,应该怎么做,我们再把需求明确一点,一个java线程对一个变量的更新怎么通知到另外一个线程呢?我们知道java当中的实例对象.数组 ...

  10. KMPnext数组自看

    emm...无数次再看kmp了 因为一直没做相关的题..看了就忘看了就忘..emm.. next[i]表示去掉第i个元素后,自已的前缀和后缀完全匹配的最大长度 例 字符串 a b a b a b z ...