0x00 内容概览

  1. Flask-RESTPlus安装
  2. 快速入门
    1. 初始化
    2. 一个最简单的API示例
    3. 资源路由
    4. 端点
    5. 参数解析
    6. 数据格式化
    7. 顺序保留
    8. 完整例子 

0x01 Flask-RESTPlus安装

1、Python版本兼容性

当前Flask-RESTPlus的最新版本为v0.11.0,支持2.7或3.4+版本的Python。

2、安装方式

可以通过以下几种方式来安装:

pip安装:$ pip install flask-restplus

easy_install安装:$ easy_install flask-restplus

离线安装:首先下载flask-restplus包,然后本地解压切换到包目录,使用python setup.py install安装

安装开发版:

git clone https://github.com/noirbizarre/flask-restplus.git
cd flask-restplus
pip install -e .[dev,test]

0x02 快速入门

本教程假设你已经熟悉了Flask,并已经正常安装了Flask和Flask-RESTPlus。如果还未安装Flask-RESTPlus,那么请参考0x01部分进行安装。

1、初始化

在使用Flask-RESTPlus之前,需要进行初始化,这一点与Flask的其他扩展是一样的,通过传入Flask实例进行初始化:

from flask import Flask
from flask_restplus import Api app = Flask(__name__)
api = Api(app)

或者使用工厂模式进行初始化:

from flask import Flask
from flask_restplus import Api api = Api() app = Flask(__name__)
api.init_app(app)

2、一个最简单的API示例

一个最简单的API示例程序如下:

 # file:1-Quick-Start.py

 from flask import Flask
from flask_restplus import Resource, Api app = Flask(__name__)
api = Api(app) @api.route('/hello')
class HelloWorld(Resource):
def get(self):
return {'hello': 'world'} if __name__ == '__main__':
app.run(debug=True)

需要注意的是,此时在程序中我们开启了Flask的调试模式,即设置了debug=True,这是为了更详细地打印错误信息,以及确保我们每次修改代码时,都会自动发现变更并重新启动运行最新的代码。不过,生产环境绝对不要开启调试模式,因为它会使你的后台服务处于被攻击的风险之中!

此时在PyCharm中运行该程序,正常情况下会打印出以下信息:

C:\SelfFiles\Install\Python36\python.exe C:/SelfFiles/Codes/Python/codes/Flask-RESTPlus-Tutorial/1_Quick_Start/1-Quick-Start.py
* Restarting with stat
* Debugger is active!
* Debugger PIN: 156-529-095
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

此时在浏览器中访问http://127.0.0.1:5000/hello会返回以下结果:

或者使用curl工具进行访问:

另外,我们也可以在浏览器中直接访问我们API的根路径,即http://127.0.0.1:5000,此时会显示Swagger的界面,里面包含了我们的Restful API的相应信息,这就是Flask-RESTPlus的强大之处(当然,其实是Swagger的强大之处):

3、资源路由

Flask-RESTPlus提供的主要创建对象就是资源。资源创建于Flask可插入视图(pluggable view)之上,使得我们可以通过在资源上定义方法来很容易地访问多个HTTP方法。下面是一个对todo应用的基本CRUD资源操作的示例:

 from flask import Flask, request
from flask_restplus import Resource, Api app = Flask(__name__)
api = Api(app) todos = {} @api.route('/<string:todo_id>')
class TodoSimple(Resource):
def get(self, todo_id):
return {todo_id: todos[todo_id]} def put(self, todo_id):
todos[todo_id] = request.form['data']
return {todo_id: todos[todo_id]} if __name__ == '__main__':
app.run(debug=True)

可以通过curl对其进行访问操作:

或者,如果你的Python中安装了Requests包,那也也可以使用它来进行访问:

Flask-RESTPlus理解视图方法中的多种类型的返回值。类似于Flask,你可以返回任何可迭代的类型,它会将该返回值转换成响应对象(response),包括原始的Flask响应对象。Flask-RESTPlus还提供了设置响应码和响应头的功能,这一点可以通过使用多个返回值来实现,如下所示:

class Todo1(Resource):
def get(self):
# 默认为200 OK
return {'task': 'Hello world'} class Todo2(Resource):
def get(self):
# 设置响应码为201
return {'task': 'Hello world'}, 201 class Todo3(Resource):
def get(self):
# 设置响应码为201,并返回自定义的响应头
return {'task': 'Hello world'}, 201, {'Etag': 'some-opaque-string'}

4、端点(Endpoints)

大多数情况下,某个资源都会有多个URL。所以,我们可以向Api对象的add_resource()方法或route()装饰器中传入多个URL,这样每个URL都将会路由到该资源上:

api.add_resource(HelloWorld, '/hello', '/world')

# 或者下面装饰器方式,二者等价

@api.route('/hello', '/world')
class HelloWorld(Resource):
pass

另外,也可以将URL中的部分内容设置成变量,以此来匹配资源方法,如下所示:

api.add_resource(Todo, '/todo/<int:todo_id>', endpoint='todo_ep')

# 或者下面装饰器方式,二者等价

@api.route('/todo/<int:todo_id>', endpoint='todo_ep')
class HelloWorld(Resource):
pass # 这样,URL为/todo/1、/todo/2等以/todo/加一个int型整数的URL都可以路由到该资源

注意:如果一个请求(request)与应用的任何端点都不匹配,那么Flask-RESTPlus将会返回一个404错误信息,并给出其他与所请求端点最匹配的建议信息。不过,我们可以通过在程序配置中设置ERROR_404_HELP为False来关闭该功能。

未关闭时程序如下:

 from flask import Flask, request
from flask_restplus import Resource, Api app = Flask(__name__)
api = Api(app) todos = {} @api.route('/<string:todo_id>')
class TodoSimple(Resource):
def get(self, todo_id):
return {todo_id: todos[todo_id]} def put(self, todo_id):
todos[todo_id] = request.form['data']
return {todo_id: todos[todo_id]} if __name__ == '__main__':
app.run(debug=True)

运行改程序并在浏览器中访问http://localhost:5000/hello/hello,结果如下:

设置ERROR_404_HELP为False后的程序为:

 from flask import Flask, request
from flask_restplus import Resource, Api app = Flask(__name__)
api = Api(app)
app.config['ERROR_404_HELP'] = False

todos = {} @api.route('/<string:todo_id>')
class TodoSimple(Resource):
def get(self, todo_id):
return {todo_id: todos[todo_id]} def put(self, todo_id):
todos[todo_id] = request.form['data']
return {todo_id: todos[todo_id]} if __name__ == '__main__':
app.run(debug=True)

再次运行并访问http://localhost:5000/hello/hello,结果如下:

此处两种情况返回结果一致,尚未尝试出给出相近端点的建议信息,也许是我没用使用对,后续再补充。

5、参数解析(Argument Parsing)

尽管Flask提供了容易的方式来访问请求数据(例如,查询字符串querystring或者POST表单编码数据),但验证表单数据仍旧是一件令人头疼的事。Flask-RESTPlus内置支持对请求数据的验证,这一功能是通过使用一个类似于argparse的库来实现的,如下:

from flask_restplus import reqparse

parser = reqparse.RequestParser()
parser.add_argument('rate', type=int, help='Rate to charge for this resource')
args = parser.parse_args()

注意:与argparse模块不同的是,parse_args()返回的是一个Python字典,而不是自定义数据结构。

使用RequestParser类还能获取完整的错误信息。如果一个参数未验证通过,Flask-RESTPlus将响应一个400坏请求,以及一个高亮错误信息的响应。示例程序如下:

 from flask import Flask, request
from flask_restplus import Resource, Api,reqparse app = Flask(__name__) api = Api(app) from flask_restplus import reqparse parser = reqparse.RequestParser()
parser.add_argument('rate', type=int,required=True,help='Rate to charge for this resource') todos = {
'':'eat',
'':'sleep'
} @api.route('/<string:todo_id>')
class TodoSimple(Resource):
def get(self, todo_id):
return {todo_id: todos[todo_id]} def put(self, todo_id):
args = parser.parse_args()
todos[todo_id] = request.form['data']
return {todo_id: todos[todo_id]} if __name__ == '__main__':
app.run(debug=True)

其中,parser.add_argument('rate', type=int,required=True,help='Rate to charge for this resource')表示,参数名为rate,数据类型为int,请求时必须发送此参数,如果验证不通过时将会返回help指定的信息。

运行程序并使用curl进行访问,分别验证以下几种情况:

  • 提供rate值,但不是int型(验证不通过)
  • 提供rate值,且是int型(验证通过)
  • 不提供rate值(验证不通过)

结果分别如下:

另外,以参数strict=True调用parse_args()能够保证如果请求中包含了解析器中未定义的参数时,将会抛出一个错误。示例程序如下:

 from flask import Flask, request
from flask_restplus import Resource, Api,reqparse app = Flask(__name__) api = Api(app) from flask_restplus import reqparse parser = reqparse.RequestParser()
parser.add_argument('rate', type=int,required=True,help='Rate to charge for this resource') todos = {
'':'eat',
'':'sleep'
} @api.route('/<string:todo_id>')
class TodoSimple(Resource):
def get(self, todo_id):
return {todo_id: todos[todo_id]} def put(self, todo_id):
args = parser.parse_args(strict=True)
todos[todo_id] = todo_id
return {todo_id: todos[todo_id]} if __name__ == '__main__':
app.run(debug=True)

此时,运行该程序并使用curl访问,结果如下:

6、数据格式化(Data Formatting)

默认情况下,在返回的可迭代对象中的所有字段都会原样返回。虽然在处理Python基本数据结构时这种方式很不错,但是当涉及到对象时将会变得非常棘手。为了解决这个问题,Flask-RESTPlus提供了fields模块和marshal_with()装饰器。类似于Django ORM和WTForm,你可以使用fields模块来描述响应的数据结构。示例程序如下:

 from flask import Flask
from flask_restplus import fields, Api, Resource app = Flask(__name__)
api = Api(app) model = api.model('Model', {
'task': fields.String,
'uri': fields.Url('todo_ep',absolute=True) # absolute参数表示生成的url是否是绝对路径
}) class TodoDao(object):
def __init__(self, todo_id, task):
self.todo_id = todo_id
self.task = task # 该字段不会发送到响应结果中
self.status = 'active' @api.route('/todo',endpoint='todo_ep')
class Todo(Resource):
@api.marshal_with(model)
def get(self, **kwargs):
return TodoDao(todo_id='my_todo', task='Remember the milk') if __name__ == '__main__':
app.run(debug=True)

运行上述程序并使用curl访问结果如下:

上述示例接受了一个Python对象,并将其进行结构转换。而marshal_with()装饰器就是用来对结果按照model的结构进行转换的,从上面的结果和代码中可以知道,我们仅仅从TodoDao对象中提取了task字段的值,而model中的fields.Url字段是一个特殊字段,它接受一个端点名,并在响应中生成该端点名对应的URL。此外,使用marshal_with()装饰器还可以以swagger规范对输出进行归档。fields模块中包含了你所需要的大多数类型,详细信息可以查看fields模块的说明文档。

7、顺序保留

默认情况下,字段顺序并未得到保留,因为它会损耗性能。不过,如果你确实需要保留字段顺序,那么可以向类或函数传入一个ordered=True的参数项,以此强制进行顺序保留:

  • Api全局保留:api = Api(ordered = True)
  • Namespace全局保留:ns = Namespace(ordered=True)
  • marshal()局部保留:return marshal(data, fields, ordered=True)

本例中只举例局部保留方式的使用方法,程序如下:

 from flask import Flask
from flask_restplus import fields, Api, Resource app = Flask(__name__)
api = Api(app) model = api.model('Model', {
'task': fields.String,
'uri': fields.Url('todo_ep',absolute=True), # absolute参数表示生成的url是否是绝对路径
'developer':fields.String(default='jack')
}) class TodoDao(object):
def __init__(self, todo_id, task, developer):
self.todo_id = todo_id
self.task = task
self.developer = developer # 该字段不会发送到响应结果中
self.status = 'active' @api.route('/todo',endpoint='todo_ep')
class Todo(Resource):
# @api.marshal_with(model)
def get(self, **kwargs):
return api.marshal(TodoDao(todo_id='my_todo', task='Remember the milk', developer='Tom'),model,ordered=False) if __name__ == '__main__':
app.run(debug=True)

运行并使用curl进行访问,结果如下:

8、完整例子

 from flask import Flask
from flask_restplus import Api, Resource, fields
from werkzeug.contrib.fixers import ProxyFix app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app) api = Api(app, version='1.0', title='TodoMVC API',
description='A simple TodoMVC API',
) # 定义命名空间
ns = api.namespace('todos', description='TODO operations') todo = api.model('Todo', {
'id': fields.Integer(readOnly=True, description='The task unique identifier'),
'task': fields.String(required=True, description='The task details')
}) class TodoDAO(object):
def __init__(self):
self.counter = 0
self.todos = [] def get(self, id):
for todo in self.todos:
if todo['id'] == id:
return todo
api.abort(404, "Todo {} doesn't exist".format(id)) def create(self, data):
todo = data
todo['id'] = self.counter = self.counter + 1
self.todos.append(todo)
return todo def update(self, id, data):
todo = self.get(id)
todo.update(data)
return todo def delete(self, id):
todo = self.get(id)
self.todos.remove(todo) DAO = TodoDAO()
DAO.create({'task': 'Build an API'})
DAO.create({'task': '?????'})
DAO.create({'task': 'profit!'}) @ns.route('/')
class TodoList(Resource):
'''获取所有todos元素,并允许通过POST来添加新的task'''
@ns.doc('list_todos')
@ns.marshal_list_with(todo)
def get(self):
'''返回所有task'''
return DAO.todos @ns.doc('create_todo')
@ns.expect(todo)
@ns.marshal_with(todo, code=201)
def post(self):
'''创建一个新的task'''
return DAO.create(api.payload), 201 @ns.route('/<int:id>')
@ns.response(404, 'Todo not found')
@ns.param('id', 'The task identifier')
class Todo(Resource):
'''获取单个todo项,并允许删除操作'''
@ns.doc('get_todo')
@ns.marshal_with(todo)
def get(self, id):
'''获取id指定的todo项'''
return DAO.get(id) @ns.doc('delete_todo')
@ns.response(204, 'Todo deleted')
def delete(self, id):
'''根据id删除对应的task'''
DAO.delete(id)
return '', 204 @ns.expect(todo)
@ns.marshal_with(todo)
def put(self, id):
'''更新id指定的task'''
return DAO.update(id, api.payload) if __name__ == '__main__':
app.run(debug=True)

更多其他示例参考GitHub

0x03 参考链接

【Flask-RESTPlus系列】Part1:快速入门的更多相关文章

  1. Flask开发系列之快速入门

    Flask开发系列之快速入门 文档 一个最小的应用 调试模式 路由 变量规则 构造 URL HTTP 方法 静态文件 模板渲染 访问请求数据 环境局部变量 请求对象 文件上传 Cookies 重定向和 ...

  2. SpringBoot系列: RestTemplate 快速入门

    ====================================相关的文章====================================SpringBoot系列: 与Spring R ...

  3. BIML 101 - ETL数据清洗 系列 - BIML 快速入门教程 - 序

    BIML 101 - BIML 快速入门教程 做大数据的项目,最花时间的就是数据清洗. 没有一个相对可靠的数据,数据分析就是无木之舟,无水之源. 如果你已经进了ETL这个坑,而且预算有限,并且有大量的 ...

  4. Flask简介,安装,demo,快速入门

    1.Flask简介 Flask是一个相对于Django而言轻量级的Web框架. 和Django大包大揽不同,Flask建立于一系列的开源软件包之上,这其中 最主要的是WSGI应用开发库Werkzeug ...

  5. pthon web框架flask(二)--快速入门

    快速入门 迫切希望上手?本文提供了一个很好的 Flask 介绍.假设你已经安装 Flask, 如果还没有安装话,请浏览下 安装 . 一个最小的应用 一个最小的应用看起来像这样: from flask ...

  6. BIML 101 - ETL数据清洗 系列 - BIML 快速入门教程 - 连接数据库执行SQL语句

    BIML 101 - BIML 快速入门教程 第一节 连接数据库执行SQL语句 本小节将用BIML建一个简单的可以执行的包. 新建一个biml文件,贴入下面的代码 1 <Biml xmlns=& ...

  7. Maven系列之快速入门

    文章结构 唯快不破---Maven快速入门 稳打稳扎---Maven核心知识 实用为先---Maven如何建立Web项目  1   唯快不破---Maven快速入门       1.1 Maven项目 ...

  8. Quartz.NET开源作业调度框架系列(一):快速入门step by step

    Quartz.NET是一个被广泛使用的开源作业调度框架 , 由于是用C#语言创建,可方便的用于winform和asp.net应用程序中.Quartz.NET提供了巨大的灵活性但又兼具简单性.开发人员可 ...

  9. SpringBoot系列: JdbcTemplate 快速入门

    对于一些小的项目, 我们没有必要使用MyBatis/JPA/Hibernate等重量级技术, 直接使用Spring JDBC 即可, Spring JDBC 是对 jdbc的简单封装, 很容易掌握. ...

  10. Zookeeper系列1 快速入门

    Zookeeper的简介这里我就不说了,在接下来的几篇文章会涉及zookeeper环境搭建,watcher以及相关配置说明, 三种操作zookeeper的方式(原生API方式,zkclient,Cur ...

随机推荐

  1. php 批量下载文件

    public function batchDownload() { $filename = 'tmp.zip'; $zipName = date('YmdHi') . '.zip'; $files = ...

  2. 基于接口的 InvocationHandler 动态代理(换种写法)

    InvocationHandler is the interface implemented by the invocation handler of a proxy instance. Each p ...

  3. HTML5的Rang对象

    基本概念 Range对象代表页面上的一段连续的区域.通过Range对象,可以获取或修改网页上的任何区域. Selection与Range对象的使用 <body> <script> ...

  4. java面试问题收集(2)

    1 Integer int相等问题 Integer对象和int比较的时候会有一个拆箱的过程,始终相等 Integer和new Integer对象不会相等,引用不同 两个Integer对象比较,Inte ...

  5. java0618

    1. java的基本数据类型,各占多少字节? byte 8位 short 16位 int 32位 long 64位 float 32位 double 64位 boolean 1位 char 16位 2 ...

  6. SQL Server 自动循环归档分区数据脚本

    标签:SQL SERVER/MSSQL SERVER/数据库/DBA/表分区 概述 在很多业务场景下我们需要对一些记录量比较大的表进行分区,同时为了保证性能需要将一些旧的数据进行归档.在分区表很多的情 ...

  7. 博客Hexo + github pages + 阿里云绑定域名搭建个人博客

    申请域名 万网购买的域名,地址:https://wanwang.aliyun.com/domain/com?spm=5176.8142029.388261.137.LoKzy7 控制台进行解析 控制台 ...

  8. 第50节:Java的当中的泛型

    Java当中的泛型 01 import java.util.ArrayList; import java.util.List; public class Demo{ public static voi ...

  9. 把ajax包装成promise的形式(2)

    概述 为了体验promise的原理,我打算自己把ajax包装成promise的形式.主要希望实现下列功能: // 1.使用success和error进行链式调用,并且可以在后面加上无限个 promis ...

  10. puppet-master搭建

    puppet 搭建 Table of Contents 配置yum源 配置hosts 安装puppet-server 部署puppet-agent trouble-shoting 配置yum源 备份系 ...