pip install flask-restful
git clone https://github.com/twilio/flask-restful.git
cd flask-restful
python setup.py develop
from flask import Flask
from flask_restful import Api, Resource
app = Flask(__name__)
api = Api(app)
class HellowWOrld(Resource):
def get(self):
return {"hello": "world"}
api.add_resource(HellowWOrld, '/')
if __name__ == "__main__":
app.run(debug=True)
from flask import Flask, request
from flask_restful import Api, Resource
app = Flask(__name__)
api = Api(app)
todos = {}
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]}
api.add_resource(TodoSimple, '/' )
if __name__ == "__main__":
app.run()
from flask import Flask
from flask_restful import Api, Resource
app = Flask(__name__)
api = Api(app)
class Todo1(Resource):
def get(self):
# 默认响应状态码为200
return {"task": 'Hello World'}
class Todo2(Resource):
def get(self):
# 修改状态码为201
return {"task": "Hello World"}, 201
class Todo3(Resource):
def get(self):
# 添加响应头信息
return {"task": "Hello World"}, 201, {"Python": "Flask", "python": "Tornado"}
api.add_resource(Todo1, '/todo1')
api.add_resource(Todo2, '/todo2')
api.add_resource(Todo3, '/todo3')
if __name__ == "__main__":
app.run()
WTF
扩展表单验证from flask_restful import reqparse
parser = reqparse.RequestParser()
parser.add_argument('rate', type=int, help='Rate cannot be converted')
parser.add_argument('name', type=str)
args = parser.parse_args()
# coding:utf-8
from flask import Flask, request
from flask_restful import Api, Resource, reqparse
app = Flask(__name__)
api = Api(app)
# 创建请求参数解析对象
parser = reqparse.RequestParser()
# 添加验证字段,以及验证信息
parser.add_argument('uname', type=str, help='is must str')
parser.add_argument('password', type=str, required=True, help='is must exists')
parser.add_argument('age', type=int, help='is must int')
class Register(Resource):
def get(self):
return "welcome come to register page"
def post(self):
data = parser.parse_args(strict=True)
return data
api.add_resource(Register, '/register')
if __name__ == "__main__":
app.run(debug=True)
限制该字段不能为空
required=True
parser.add_argument('name', type=str, required=True, help="Name cannot be blank!")
接受该字段可以有多个值&列表
action='append'
parser.add_argument('name', type=str, action='append')
curl http://api.example.com -d "Name=bob" -d "Name=sue" -d "Name=joe"
args = parser.parse_args()
args['name'] # ['bob', 'sue', 'joe']
设置请求参数来源(url、表单、请求头、cookie…)
# 表单数据
parser.add_argument('name', type=int, location='form')
# url数据
parser.add_argument('PageSize', type=int, location='args')
# 请求头数据
parser.add_argument('User-Agent', type=str, location='headers')
# cookies数据
parser.add_argument('session_id', type=str, location='cookies')
# 多媒体文件
parser.add_argument('picture', type=werkzeug.datastructures.FileStorage, location='files')
多个位置参数
parser.add_argument('text', location=['headers', 'values'])
继承解析
copy()
扩充它。你也可以使用 replace_argument()
覆盖父级的任何参数,或者使用 remove_argument()
完全删除参数。 例如:from flask.ext.restful import RequestParser
parser = RequestParser()
parser.add_argument('foo', type=int)
parser_copy = parser.copy()
parser_copy.add_argument('bar', type=int)
# parser_copy has both 'foo' and 'bar'
parser_copy.replace_argument('foo', type=str, required=True, location='json')
# 'foo' is now a required str located in json, not an int as defined
# by original parser
parser_copy.remove_argument('foo')
# parser_copy no longer has 'foo' argument
(1)基本用法
键名是
要呈现的对象的属性
或键的名称,键值是一个类
,该类格式化和返回的该字段的值。下面例子有三个字段,两个是字符串(Strings)以及一个是日期时间(DateTime),格式为 RFC 822 日期字符串(同样也支持 ISO 8601)。from flask_restful import Resource, fields, marshal_with
resource_fields = {
'name': fields.String,
'address': fields.String,
'date_updated': fields.DateTime(dt_format='rfc822'),
}
class Todo(Resource):
@marshal_with(resource_fields, envelope='resource')
def get(self, **kwargs):
return db_get_todo() # Some function that queries the db
数据库对象
(todo),它具有属性:name
, address
, 以及 date_updated
。该对象上任何其它的属性可以被认为是私有的不会在输出中呈现出来。一个可选的 envelope 关键字参数被指定为封装结果输出。marshal_with
是一个很便捷的装饰器,在功能上等效于如下的 return marshal(db_get_todo(), resource_fields), 200。这个明确的表达式能用于返回 200 以及其它的 HTTP 状态码作为成功响应。# coding:utf-8
from flask import Flask, request
from flask_restful import Api, Resource, reqparse, fields, marshal_with
from flask_sqlalchemy import SQLAlchemy
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
app = Flask(__name__)
api = Api(app)
class Config(object):
"""配置参数"""
SQLALCHEMY_DATABASE_URI = "mysql://root:[email protected]:3306/db_flask"
# 设置sqlalchemy自动跟踪数据库
SQLALCHEMY_TRACK_MODIFICATIONS = True
SECRET_KRY = '#%*(_)?./DFVDjnd34534'
app.config.from_object(Config)
# 创建数据库sqlalchemy工具对象
db = SQLAlchemy(app)
# 创建flask脚本管理工具对象
manager = Manager(app)
# 创建数据库迁移工具对象
Migrate(app, db)
# 向manager对象中添加数据库操作命令
manager.add_command('db', MigrateCommand)
# 创建请求参数解析对象
parser = reqparse.RequestParser()
parser.add_argument('name', type=str, help='is must str')
parser.add_argument('password', type=str, required=True, help='is must exists')
parser.add_argument('id', type=int, help='is must int')
# 对象输出属性
resource_fields = {
'id': fields.String,
'name': fields.String,
'email': fields.String
}
# 创建数据库模型类
class Role(db.Model):
"""用户角色表"""
__tablename__ = 'tbl_roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(32), unique=True)
# 需要手动添加,方便使用Role.user查询用户对象,user列不是真实存在的,backref="role"为了方便通过User.role获取角色对象,
# 因为使用User.role_id只能获取到角色id,要想获取角色对象,还需要再在Role表中查询一次
users = db.relationship("User", backref="role")
def __repr__(self):
"""定义之后,可以让显示对象的时候更直观,类似于Django中的__str__"""
return "Rloe object: name=%s" % self.name
class User(db.Model):
"""用户表"""
__tablename__ = 'tbl_users' # 指明数据库表名
id = db.Column(db.Integer, primary_key=True) # 整型主键,会默认设置为自增主键
name = db.Column(db.String(64), unique=True)
email = db.Column(db.String(128), unique=True)
password = db.Column(db.String(128), nullable=False) # nullable=False 参数必须传
role_id = db.Column(db.Integer, db.ForeignKey("tbl_roles.id"))
class Users(Resource):
@marshal_with(resource_fields, envelope='users')
def post(self):
args = parser.parse_args()
id = args.get('id')
name = args.get('name')
password = args.get('password')
user = User.query.filter_by(name=name, password=password).all()
return user
api.add_resource(Users, '/users')
if __name__ == "__main__":
app.run(debug=True)
resource_fields
后,使用@marshal_with(resource_fields, envelope='users')
,则会返回resource_fields
内定义了的字段属性,envelope
:即你想要用户看到的对象,我们上述示例中因为获取的是user
对象信息,所以我定义为users
,在返回结果中看到的users
即我们此处定义的。(3)重命名属性
attribute
可以配置这种映射。fields = {
'name': fields.String(attribute='private_name'),
'address': fields.String,
}
lambda
也能在 attribute 中使用fields = {
'name': fields.String(attribute=lambda x: x._private_name),
'address': fields.String,
}
(4)默认值
没有
你定义的字段列表中的属性
,你可以指定一个
默认值而不是返回 None。fields = {
'name': fields.String(default='Anonymous User'),
'address': fields.String,
}
(5)自定义字段&多个值
class UrgentItem(fields.Raw):
def format(self, value):
return "Urgent" if value & 0x01 else "Normal"
class UnreadItem(fields.Raw):
def format(self, value):
return "Unread" if value & 0x02 else "Read"
fields = {
'name': fields.String,
'priority': UrgentItem(attribute='flags'),
'status': UnreadItem(attribute='flags'),
}
(6)Url & 其它具体字段
fields.Url
,即为所请求的资源合成一个 uri。这也是一个好示例,它展示了如何添加
并不
真正在你的数据对象中存在的数据
到你的响应中
。class RandomNumber(fields.Raw):
def output(self, key, obj):
return random.random()
fields = {
'name': fields.String,
# todo_resource is:访问视图时的路由
'uri': fields.Url('todo_resource'),
'random': RandomNumber,
}
相对的 uri
。为了生成包含协议(scheme),主机名以及端口的绝对 uri
,需要在字段声明的时候传入 absolute=True
。传入 scheme
关键字参数可以覆盖默认的协议(scheme):fields = {
'uri': fields.Url('todo_resource', absolute=True)
'https_uri': fields.Url('todo_resource', absolute=True, scheme='https')
}
(7)复杂结构
扁平的结构
,marshal_with 将会把它转变为一个嵌套结构>>> from flask_restful import fields, marshal
>>> import json
>>>
>>> resource_fields = {'name': fields.String}
>>> resource_fields['address'] = {}
>>> resource_fields['address']['line 1'] = fields.String(attribute='addr1')
>>> resource_fields['address']['line 2'] = fields.String(attribute='addr2')
>>> resource_fields['address']['city'] = fields.String
>>> resource_fields['address']['state'] = fields.String
>>> resource_fields['address']['zip'] = fields.String
>>> data = {'name': 'bob', 'addr1': '123 fake street', 'addr2': '', 'city': 'New York', 'state': 'NY', 'zip': '10468'}
>>> json.dumps(marshal(data, resource_fields))
'{"name": "bob", "address": {"line 1": "123 fake street", "line 2": "", "state": "NY", "zip": "10468", "city": "New York"}}'
address
字段并不真正地存在于数据对象中,但是任何一个子字段(sub-fields)可以直接地访问对象的属性,就像没有嵌套一样。(8)列表字段
>>> from flask_restful import fields, marshal
>>> import json
>>>
>>> resource_fields = {'name': fields.String, 'first_names': fields.List(fields.String)}
>>> data = {'name': 'Bougnazal', 'first_names' : ['Emile', 'Raoul']}
>>> json.dumps(marshal(data, resource_fields))
>>> '{"first_names": ["Emile", "Raoul"], "name": "Bougnazal"}'
(10)高级:嵌套字段
Nested
解组(unmarshal)嵌套数据结构并且合适地呈现它们。>>> from flask_restful import fields, marshal
>>> import json
>>>
>>> address_fields = {}
>>> address_fields['line 1'] = fields.String(attribute='addr1')
>>> address_fields['line 2'] = fields.String(attribute='addr2')
>>> address_fields['city'] = fields.String(attribute='city')
>>> address_fields['state'] = fields.String(attribute='state')
>>> address_fields['zip'] = fields.String(attribute='zip')
>>>
>>> resource_fields = {}
>>> resource_fields['name'] = fields.String
>>> resource_fields['billing_address'] = fields.Nested(address_fields)
>>> resource_fields['shipping_address'] = fields.Nested(address_fields)
>>> address1 = {'addr1': '123 fake street', 'city': 'New York', 'state': 'NY', 'zip': '10468'}
>>> address2 = {'addr1': '555 nowhere', 'city': 'New York', 'state': 'NY', 'zip': '10468'}
>>> data = { 'name': 'bob', 'billing_address': address1, 'shipping_address': address2}
>>>
>>> json.dumps(marshal_with(data, resource_fields))
'{"billing_address": {"line 1": "123 fake street", "line 2": null, "state": "NY", "zip": "10468", "city": "New York"}, "name": "bob", "shipping_address": {"line 1": "555 nowhere", "line 2": null, "state": "NY", "zip": "10468", "city": "New York"}}'
默认的API仅支持JSON格式内容,我们可以通过扩展,使其可以支持更多类型数据@api.representation
app = Flask(__name__)
api = restful.Api(app)
@api.representation('application/json')
def output_json(data, code, headers=None):
resp = make_response(json.dumps(data), code)
resp.headers.extend(headers or {})
return resp
@api.representation('application/xml')
def xml(data, code, headers):
resp = make_response(convert_data_to_xml(data), code)
resp.headers.extend(headers)
return resp
(1)自定义字段 & 输入
Raw
并且实现 format()
方法:name
改为大写NAME
输出class AllCapsString(fields.Raw):
def format(self, value):
return value.upper()
# example usage
fields = {
'name': fields.String,
'all_caps_name': AllCapsString(attribute=name),
}
(2)输入
RequestParser
中使用parser = reqparse.RequestParser()
parser.add_argument('OddNumber', type=odd_number)
parser.add_argument('Status', type=task_status)
args = parser.parse_args()
def odd_number(value):
if value % 2 == 0:
raise ValueError("Value is not odd")
return value
访问参数的名称(OddNumber)
def odd_number(value, name):
if value % 2 == 0:
raise ValueError("The parameter '{}' is not odd. You gave us the value: {}".format(name, value))
return value
value
返回列表中对应数据)# 'init' => 0
# 'in-progress' => 1
# 'completed' => 2
def task_status(value):
statuses = [u"init", u"in-progress", u"completed"]
return statuses.index(value)
(3)响应格式
representation()
装饰器。你需要在你的 API 中引用它。api = restful.Api(app)
@api.representation('text/csv')
def output_csv(data, code, headers=None):
pass
# implement csv output!
data
是你从你的资源方法返回的对象code
是预计的 HTTP 状态码headers
是设置在响应中任意的 HTTP 头。你的输出函数应该返回一个 Flask 响应对象。def output_json(data, code, headers=None):
"""Makes a Flask response with a JSON encoded body"""
resp = make_response(json.dumps(data), code)
resp.headers.extend(headers or {})
return resp
另外一种实现这一点的就是继承 Api 类并且提供你自己输出函数。
class Api(restful.Api):
def __init__(self, *args, **kwargs):
super(Api, self).__init__(*args, **kwargs)
self.representations = {
'application/xml': output_xml,
'text/html': output_html,
'text/csv': output_csv,
'application/json': output_json,
}
(4)自定义错误处理器
app = Flask(__name__)
api = flask_restful.Api(app, catch_all_404s=True)
got_request_exception()
方法把自定义错误处理加入到异常。def log_exception(sender, exception, **extra):
""" Log an exception to our logging framework """
sender.logger.debug('Got exception during processing: %s', exception)
from flask import got_request_exception
got_request_exception.connect(log_exception, app)
(5)定义自定义错误消息
errors = {
'UserAlreadyExistsError': {
'message': "A user with that username already exists.",
'status': 409,
},
'ResourceDoesNotExist': {
'message': "A resource with that ID no longer exists.",
'status': 410,
'extra': "Any extra information you want.",
},
}
status
’ 键可以设置响应的状态码。如果没有指定的话,默认是 500
,一旦你的 errors 字典定义,简单地把它传给 Api 构造函数app = Flask(__name__)
api = flask_restful.Api(app, errors=errors)