Flask-Restful
一、安裝:
pip install flask-restful
完成安裝後就可以引入常用類或模塊
from flask_restful import Api, Resource, reqparse, abort
二、案例:
from flask import Flask, request
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
# 定義一個全局字典
USER_LIST = {
'1': {'name': 'Tom'},
'2': {'name': 'Candy'}
}
class HelloWorld(Resource):
def get(self):
return USER_LIST
def post(self):
# 添加數據
user_id = int(max(USER_LIST.keys())) + 1
# 將 int 類型 user_id 轉爲 str
user_id = '%i' % user_id
USER_LIST[user_id] = {'name': request.form['name']}
return USER_LIST
api.add_resource(HelloWorld, '/users')
if __name__ == '__main__':
app.run(debug=True)
從案例可以看出,Restful 擴展通過 api.add_resource()
方法來添加路由,其中方法的第一個參數是視圖類名(該類繼承自Resource類),其函數成員函數定義了不同的 HTTP 請求方法的邏輯;第二個參數定義了 URL 路徑。
運行上面案例,可以通過 http://127.0.0.1:5000/users
來訪問,GET 請求時會顯示全局變量 USER_LIST
中的內容,POST
請求時會在USER_LIST
中添加一項,並返回剛添加的項。如果在POST
請求中找不到 name
字段,則返回400 Bad Request
錯誤。由於類UserList
沒有定義put
和delete
函數,所以在PUT
或DELETE
請求時會返回405 Method Not Allowed
錯誤。
另外,路由支持多路徑:
api.add_resource(HelloWorld, '/userlist', '/users')
這樣訪問 http://127.0.0.1:5000/userlist
和 http://127.0.0.1:5000/users
效果是一樣的。
三、 帶參數的請求
class HelloWorld(Resource):
def get(self, user_id):
return USER_LIST[user_id]
def put(self, user_id):
USER_LIST[user_id] = {'name': request.form['name']}
return USER_LIST[user_id]
def delete(self, user_id):
del USER_LIST[user_id]
return USER_LIST
api.add_resource(HelloWorld, '/users/<user_id>', '/userlist')
四、參數解析
在 POST 和 PUT 請求中,直接訪問 form 表單並驗證的工作有些麻煩,Flask-RESTful 提供了 reqparse 庫來簡化:
parse = reqparse.RequestParser()
parse.add_argument('name', type=str)
class HelloWorld(Resource):
def put(self, user_id):
# 使用 strict=True 調用 parse_args 能夠確保當請求包含你的解析器中未定義的參數的時候會拋出一個異常。
args = parse.parse_args(strict=True)
USER_LIST[user_id] = {'name': args['name']}
return USER_LIST[user_id]
api.add_resource(HelloWorld, '/users/<user_id>', '/userlist')
reqparse.RequestParser.parse_args()
返回一個 Python 字典而不是一個自定義的數據結構。
可以通過”parser.add_argument()”方法來定義form表單字段,並指定其類型(本例中是字符型str)。然後在PUT函數中,就可以調用”parser.parse_args()”來獲取表單內容,並返回一個字典,該字典就包含了表單的內容。”parser.parse_args()”方法會自動驗證數據類型,並在類型不匹配時,返回400錯誤。你還可以添加”strict”參數,如”parser.parse_args(strict=True)”,此時如果請求中出現未定義的參數,也會返回400錯誤。
五、類視圖方法返回值
Flask-RESTful 支持視圖方法多種類型的返回值。同 Flask 一樣,你可以返回任一迭代器,它將會被轉換成一個包含原始 Flask 響應對象的響應。Flask-RESTful 也支持使用多個返回值來設置響應代碼和響應頭,如下所示:
class Todo1(Resource):
def get(self):
# Default to 200 OK
return {'task': 'Hello world'}
class Todo2(Resource):
def get(self):
# Set the response code to 201
return {'task': 'Hello world'}, 201
class Todo3(Resource):
def get(self):
# Set the response code to 201 and return custom headers
return {'task': 'Hello world'}, 201, {'Etag': 'some-opaque-string'}
六、 數據格式化
默認情況下,在你的返回迭代中所有字段將會原樣呈現。儘管當你剛剛處理 Python 數據結構的時候,覺得這是一個偉大的工作,但是當實際處理它們的時候,會覺得十分沮喪和枯燥。爲了解決這個問題,Flask-RESTful 提供了 fields
模塊和 marshal_with()
裝飾器。類似 Django ORM 和 WTForm,你可以使用 fields 模塊來在你的響應中格式化結構。
from collections import OrderedDict
from flask_restful import fields, marshal_with
resource_fields = {
'task': fields.String,
'uri': fields.Url('todo_ep')
}
class TodoDao(object):
def __init__(self, todo_id, task):
self.todo_id = todo_id
self.task = task
# This field will not be sent in the response
self.status = 'active'
class Todo(Resource):
# 我們使用marshal_with來將輸出的內容格式化
@marshal_with(resource_fields)
def get(self, **kwargs):
return TodoDao(todo_id='my_todo', task='Remember the milk')
上面的例子接受一個 python 對象並準備將其序列化。marshal_with()
裝飾器將會應用到由 resource_fields
描述的轉換。從對象中提取的唯一字段是 task
。fields.Url
域是一個特殊的域,它接受端點(endpoint)名稱作爲參數並且在響應中爲該端點生成一個 URL。許多你需要的字段類型都已經包含在內。請參閱 fields
指南獲取一個完整的列表。
七、完整案例
from flask import Flask
from flask_restful import Resource, Api, reqparse, abort
app = Flask(__name__)
api = Api(app)
TODOS = {
'todo1': {'task': 'Build an Api'},
'todo2': {'task': '??????'},
'todo3': {'task': 'profit!'}
}
def abort_if_todo_doesnot_exists(todo_id):
if todo_id not in TODOS:
abort(404, message='todo {} does not exists.'.format(todo_id))
parse = reqparse.RequestParser()
parse.add_argument('task', type=str)
# Todo
# show a single todo item and lets you delete them
class Todo(Resource):
def get(self, todo_id):
abort_if_todo_doesnot_exists(todo_id)
return TODOS[todo_id]
def delete(self, todo_id):
abort_if_todo_doesnot_exists(todo_id)
del TODOS[todo_id]
return TODOS
def put(self, todo_id):
args = parse.parse_args()
task = {'task': args['task']}
TODOS[todo_id] = task
return task, 201
# TodoList
# shows a list of all todos, and lets you POST to add new tasks
class TodoList(Resource):
def get(self):
return TODOS
def post(self):
args = parse.parse_args()
todo_id = int(max(TODOS.keys()).lstrip('todo')) + 1
todo_id = "todo%i" % todo_id
TODOS[todo_id] = {'task': args['task']}
return TODOS[todo_id]
##
## Actually setup the Api resource routing here
##
api.add_resource(TodoList, '/todos')
api.add_resource(Todo, '/todos/<todo_id>')
if __name__ == '__main__':
app.run(debug=True)
用法示例:
# 獲取列表:
http://127.0.0.1:5000/todos
# 獲取單個
http://127.0.0.1:5000/todos/todo1
# 刪除單個
http://127.0.0.1:5000/todos/todo1
# 增加一個新的任務
http://127.0.0.1:5000/todos/todo4
# 更新一個任務
http://127.0.0.1:5000/todos/todo3