Flask Restful

Flask Restful

1.安装

Flask 中,为了写出更优雅的API ,提供了一个插件FLask Restful .

$ pip install flask_restful

官网

2.基本应用

一个简单的API 类似如下:

#!/usr/bin/env python
# coding=utf-8

from flask import Flask
from flask_restful import Resource,Api

app = Flask(__name__)

# 1.Api绑定app
api = Api(app)

# 定义一个GET/Post方法
class HelloWorld(Resource):
 def get(self):
     return {'hello':'world'}

# 注册URL 资源
api.add_resource(HelloWorld,'/api/hello/')

@app.route('/')
def hello_world():
 return 'Hello World!'


if __name__ == '__main__':
 app.run(debug=True)

测试联通性:

❯ curl http://127.0.0.1:5000/api/hello/
{
 "hello": "world"
}

3.Resource routing 资源路由

Flask_Restful提供的最主要的基础就是资源Resource 类.它是构建在Flask可拔插视图  之上的,利用它可以很容易的访问多个HTTP 方法.

>>> from flask_restful import Resource
>>> help(Resource)
class Resource(MethodView):
# Resource继承自MethodView,这意味者它可以重写 get/post等方法
from flask import Flask,request
from flask_restful import Resource,Api


app = Flask(__name__)

# 1. Api初始化app
api = Api(app)

todos = {}

# 2.重写 Get/Post方法
class TodoSimple(Resource):

 def get(self,todo_id):
     '''重写 get 方法'''
     return {todo_id:todos[todo_id]}

 def post(self,todo_id):
     '''重写 post 方法'''
     todos[todo_id] = request.form.get('data')
     return {todo_id:todos[todo_id]}

# 3.注册资源
# 访问URL的变量部分,可以使用 <converter:variable_name>
# 此部分可以写成
# api.add_resource(TodoSimple,'/api/todos/<todo_id>')
api.add_resource(TodoSimple,'/api/todos/<string:todo_id>')


if __name__ == '__main__':
 app.run(debug=True)

访问:

❯ curl http://127.0.0.1:5000/api/todos/todo1 -d "data=test" -X POST
{
 "todo1""test"
}
❯ curl http://127.0.0.1:5000/api/todos/todo1
{
 "todo1""test"
}
❯ curl http://127.0.0.1:5000/api/todos/todo2 -d "data=test2" -X POST
{
 "todo2""test2"
}
❯ curl http://127.0.0.1:5000/api/todos/todo2
{
 "todo2""test2"
}

add_resource ,可以添加url 中的变量参数,以及多个url .

4.endpoint

可以在API 中指定多个URL ,他们指定在Api.add_resource() 方法中.每个URL 都能访问Resource .

api.add_resource(HelloWorld, '/','/hello')
# 127.0.0.1:5000
# 127.0.0.1:5000/hello 都可以访问到 HelloWold类中定义的内容

还可以为资源方法指定endpoint

api.add_resource(TodoSimple,'/api/todos/<string:todo_id>',endpoint='todos')
# 1. Api初始化app
api = Api(app)

todos = {}

# 2.重写 Get/Post方法
class TodoSimple(Resource):

 def get(self,todo_id=None):
     '''重写 get 方法'''
     return {todo_id:todos[todo_id]}

 def post(self,todo_id=None):
     '''重写 post 方法'''
     todos[todo_id] = request.form.get('data')
     return {todo_id:todos[todo_id]}

# 3.注册资源
# 访问URL的变量部分,可以使用 <converter:variable_name>
# 此部分可以写成
# api.add_resource(TodoSimple,'/api/todos/<todo_id>')
api.add_resource(TodoSimple,'/api/todos/<string:todo_id>',endpoint='todos')

with app.test_request_context():
 print(url_for('todos',todo_id='test3'))


if __name__ == '__main__':
 app.run(debug=True)
  • endpoint是用来给url_for反转的时候指定的.如果不写endpoint,那么将会使用视图的名字的小写作为endpoint.

以上如果不写endpoint

with app.test_request_context():
    print(url_for('todosimple',todo_id='test3'))

5.Argument Parsing 参数解析

Flask_Restful 提供了简单的表单验证功能,类似与WTForms ,但是非常的简陋.它使用了一个argparse 库.

from flask_restful import reqparse

parse = reqparse.RequestParser()
parse.add_argument('rate', type=int, help='this is a test')
args = parse.parse_args()  # 返回一个字典对象

parse.add_argument() 常用参数有

  • default='value'  :指定默认值,如果前端没有传递值,就使用默认值.
  • required=True :要求前端必须传递值,默认为False .
  • type=str :指定数据类型,要求前端必须传递规定的数据类型,可以使用python 字典的数据类型,也可以指定flask_restful.inputs 模块下的值,比如:
    • falsk_restful.inputs.url:验证URL
    • falsk_restful.inputs.boolean:验证布尔值,可以是True/False01
    • falsk_restful.inputs.date :验证日期格式是YYYY-mm-dd
    • falsk_restful.inputs.reqex():验证正则表达式
  • choices=[] :指定一个列表,前端的值必须是列表中的值.
  • help='xxx‘:  指定出错的一个帮助信息.
  • trim=True : 指定去除前端返回值的空格.默认是False.
from flask import Flask,request
from flask_restful import Api,Resource,reqparse,inputs

app = Flask(__name__)
app.config.update({'DEBUG':True})

# 1.Api绑定app
api = Api(app)

# 2.重载get/post 方法

class RegiseterView(Resource):

    def get(self):
        return {'login':'???'}

    def post(self):
        '''验证的输入'''
        parse = reqparse.RequestParser()
        # 去除前端返回值的空格
        parse.add_argument('username',type=str,help='用户名不正确',trim=True)
        parse.add_argument('password',type=str,help='密码不正确',trim=True)
        # 使用type=int,使用默认值
        parse.add_argument('age',type=int,help='年龄必须是整数',trim=True,default=18)
        # 使用inputs.date 数据类型
        parse.add_argument('birthday',type=inputs.date,help='日期不正确',trim=True)
        # 使用inputs.regex
        parse.add_argument('phone',type=inputs.regex(r'1[3789]d{9}'),help='手机号码不正确',trim=True)
        # 使用inputs.boolean
        parse.add_argument('gender',type=str,choices=['male','female'],help='性别不正确',trim=True)

        # 打印
        args = parse.parse_args()
        print(args)
        return args

# 3.注册路由
api.add_resource(RegiseterView,'/api/register/',endpoint='register')


if __name__=='__main__':
    app.run()

访问并得到

❯ curl http://127.0.0.1:5000/api/register/ -d "username=Jack&password=aaa&age=19&phone=18618609966&gender=male" -X POST
{
    "username""Jack",
    "password""aaa",
    "age": 19,
    "birthday": null,
    "phone""18618609966",
    "gender""male"
}

6.格式化字段

Flask_Restful 提供了一种简单的方法来控制在HTTP Response 中实际呈现的数据.可以简单的返回一个字典类型的数据,也可以使用flask_restful.fields 模块,这个模块允许在资源中使用所需要的任何对象(ORM自定义类等等),它还可以格式化或过滤HTTP Response ,而不必担心暴露内部数据结构.

1.基本用法

可以定义一个fieldsOrderedDict 字典,键指向属性名称或要呈现在对象上的键,值是格式化并返回该字段的类.类似如下:

from flask_restful import Resource,fields,marshal_with,Api
from flask import Flask
from datetime import datetime
# pytz是一个时区的datetime object
import pytz

app = Flask(__name__)

# 1.Api绑定app
api = Api(app)

class Register:
 def __init__(self,name):
     self.name = name
     self.date = datetime.now(tz=pytz.timezone('Asia/Shanghai'))

# 2.重载Get/Post方法
class Todo(Resource):

 resource_fields = {
     'name':fields.String,
     'date': fields.DateTime(dt_format='rfc822')
 }

 @marshal_with(resource_fields)
 def get(self,name):
     print(name)
     return Register(name),200  # 可以返回状态码

# 3.注册路由/资源
api.add_resource(Todo,'/api/todo/<name>/',endpoint='todo')

if __name__ == '__main__':
 app.run(debug=True)

访问并得到:

❯ curl http://127.0.0.1:5000/api/todo/Jack/
{
 "name": "Jack",
 "date": "Wed, 11 Dec 2015 15:07:36 -0000"
}

@marshel_with 是一个装饰器,能真正接受对象,并过滤字段.

2.重命名属性

出于隐藏后台数据,保证安全的角度考虑,可以使用attribute 配置面向公众的字段.比如

resource_fields={
 'name' = fields.String(attribute='true_name')
}

# 任何可调用的函数,比如 lambda 也可以指定 attribute

resource_fields={
 'name' = fields.String(attribute=lambda x:x.true_name)
}
# 也可以指定嵌套属性
resource_fields = {
 'name': fields.String(attribute='people_list.0.person_dictionary.name'),
 'address': fields.String,
}

3.默认值

可以指定默认值,这样就不会返回None

resource_fields = {
 'name':fields.String(default='any')
}

4.自定义字段和值

如果需要自定义格式,可以继承自fields.Raw 类并重载format 方法.比如:

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"

resource_fields = {
 'name': fields.String,
 'priority': UrgentItem(attribute='flags'),
 'status': UnreadItem(attribute='flags'),
}

5.fields.Url

fields.Url:为请求的资源综合一个URL .类似如下

from flask_restful import Resource,fields,marshal_with,Api
from flask import Flask
from datetime import datetime
# pytz是一个时区的datetime object
import pytz

app = Flask(__name__)

# 1.Api绑定app
api = Api(app)

class Register:
 def __init__(self,name):
     self.name = name
     self.date = datetime.now(tz=pytz.timezone('Asia/Shanghai'))

# 2.重载Get/Post方法
class Todo(Resource):

 resource_fields = {
     'name':fields.String,
     'date': fields.DateTime(dt_format='rfc822'),
     # 返回一个url 相对路径
     'uri': fields.Url('todo'),
     # 返回一个url 绝对路径
     'uri_absolute': fields.Url('todo',absolute=True),
     # 返回一个url http绝对路径
     'https_uri':fields.Url('todo',absolute=True,scheme='https')
 }

 @marshal_with(resource_fields)
 def get(self,name):
     print(name)
     return Register(name)

# 3.注册路由/资源
api.add_resource(Todo,'/api/todo/<name>/',endpoint='todo')

if __name__ == '__main__':
 app.run(debug=True)

访问:

❯ curl http://127.0.0.1:5000/api/todo/Jack/
{
 "name": "Jack",
 "date": "Thu, 12 Dec 2019 11:14:05 -0000",
 "uri": "/api/todo/Jack/",
 "uri_absolute": "http://127.0.0.1:5000/api/todo/Jack/",
 "https_uri": "https://127.0.0.1:5000/api/todo/Jack/"
}

6.复杂结构

引入2个方法:

  • flask_restful.marshal(data,fields,envelope=None):把dictlist 数据类型转换为OrderedDict 数据类型,用于过滤数据.
# data:实际的对象
# fields: 需要过滤的数据类型
# envelope: 类似别名

# 原始数据,需要过滤的数据
>>> data = {'a':100,'b':'foo'}
# 过滤条件
>>> mfields = {'a':fields.Raw}
# 过滤
>>> marshal(data,mfields)
OrderedDict([('a'100)])

# 过滤后的数据,可以打包成 json数据
>>> import json
>>> json.dumps(marshal(data,mfields))
'{"a": 100}'
  • flask_restful.marshal_with(fields, envelope=None) :一个装饰器,可以支持自定义函数,返回的效果和上一个相同.
>>> from flask_restful import marshal_with
>>> mfields = {'a':fields.String}
>>> @marshal_with(mfields)
... def get():
...     return {'a':'foo','b':100}
...     
... 
>>> 
>>> get()
OrderedDict([('a''foo')])
>>> json.dumps(get())
'{"a": "foo"}'

处理一个复杂的结构:

# 过滤器,过滤条件,输出格式.
>>> resource_fields = {
...     'name':fields.String,
...     'address': {
...         'line1':fields.String,
...         'line2':fields.Integer,
...         'line3':fields.Raw
...     }
... }

# 原始数据
>>> data = {
...     'name':'Jack',
...     'line1':'This is a test',
...     'line2':123,
...     'line3':'hello'
... }

# 组装成 OrderDict对象
>>> marshal(data,resource_fields)
OrderedDict([('name''Jack'), ('address', OrderedDict([('line1''This is a test'), ('line2'123), ('line3''hello')]))])

# 生成Json数据
>>> json.dumps(marshal(date,resource_fields))
'{"name": "jack", "address": {"line1": null, "line2": 0, "line3": null}}'


也可以对列表进行处理:

# 需要处理的数据
>>> data = {
...     'book':'Python',
...     'detail':['price','description']
... }

# 过滤器
>>> resource_fields = {
...     'name': fields.String,
...     'detail':fields.List(fields.String)
... }

# 转化
>>> marshal(data,resource_fields)
OrderedDict([('name'None), ('detail', ['price''description'])])

# Json数据
>>> json.dumps(marshal(data,resource_fields))
'{"name": null, "detail": ["price", "description"]}'
  • fiels.List() 只能过滤一种数据类型

处理嵌套数据fields.Nested

>>> data = {
...     'book':'Python',
...     'detail':{
...         'price':12,
...         'description':'This is a Python book.'
...     }
... }
>>> 
>>> resource_fields = {
...     'name': fields.String,
...     'detail':fields.List(fields.Nested({
...         'price':fields.Integer,
...         'description':fields.String
...     }))
... }
>>> 
>>> marshal(data,resource_fields)
OrderedDict([('name'None), ('detail', [OrderedDict([('price'12), ('description''This is a Python book.')])])])
>>> json.dumps(marshal(data,resource_fields))
'{"name": null, "detail": [{"price": 12, "description": "This is a Python book."}]}'

– END –


原文始发于微信公众号(Flask学习笔记):Flask Restful

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/36431.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!