當前Flask-RESTPlus的最新版本爲v0.11.0,支持2.7或3.4+版本的Python。html
能夠經過如下幾種方式來安裝:python
pip安裝:$ pip install flask-restplusgit
easy_install安裝:$ easy_install flask-restplusgithub
離線安裝:首先下載flask-restplus包,而後本地解壓切換到包目錄,使用python setup.py install安裝flask
安裝開發版:api
git clone https://github.com/noirbizarre/flask-restplus.git cd flask-restplus pip install -e .[dev,test]
本教程假設你已經熟悉了Flask,並已經正常安裝了Flask和Flask-RESTPlus。若是還未安裝Flask-RESTPlus,那麼請參考0x01部分進行安裝。瀏覽器
在使用Flask-RESTPlus以前,須要進行初始化,這一點與Flask的其餘擴展是同樣的,經過傳入Flask實例進行初始化:數據結構
from flask import Flask from flask_restplus import Api app = Flask(__name__) api = Api(app)
或者使用工廠模式進行初始化:app
from flask import Flask from flask_restplus import Api api = Api() app = Flask(__name__) api.init_app(app)
一個最簡單的API示例程序以下:curl
1 # file:1-Quick-Start.py 2 3 from flask import Flask 4 from flask_restplus import Resource, Api 5 6 app = Flask(__name__) 7 api = Api(app) 8 9 @api.route('/hello') 10 class HelloWorld(Resource): 11 def get(self): 12 return {'hello': 'world'} 13 14 if __name__ == '__main__': 15 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的強大之處):
Flask-RESTPlus提供的主要建立對象就是資源。資源建立於Flask可插入視圖(pluggable view)之上,使得咱們能夠經過在資源上定義方法來很容易地訪問多個HTTP方法。下面是一個對todo應用的基本CRUD資源操做的示例:
1 from flask import Flask, request 2 from flask_restplus import Resource, Api 3 4 app = Flask(__name__) 5 api = Api(app) 6 7 todos = {} 8 9 @api.route('/<string:todo_id>') 10 class TodoSimple(Resource): 11 def get(self, todo_id): 12 return {todo_id: todos[todo_id]} 13 14 def put(self, todo_id): 15 todos[todo_id] = request.form['data'] 16 return {todo_id: todos[todo_id]} 17 18 if __name__ == '__main__': 19 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'}
大多數狀況下,某個資源都會有多個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/一、/todo/2等以/todo/加一個int型整數的URL均可以路由到該資源
注意:若是一個請求(request)與應用的任何端點都不匹配,那麼Flask-RESTPlus將會返回一個404錯誤信息,並給出其餘與所請求端點最匹配的建議信息。不過,咱們能夠經過在程序配置中設置ERROR_404_HELP爲False來關閉該功能。
未關閉時程序以下:
1 from flask import Flask, request 2 from flask_restplus import Resource, Api 3 4 app = Flask(__name__) 5 api = Api(app) 6 7 8 todos = {} 9 10 @api.route('/<string:todo_id>') 11 class TodoSimple(Resource): 12 def get(self, todo_id): 13 return {todo_id: todos[todo_id]} 14 15 def put(self, todo_id): 16 todos[todo_id] = request.form['data'] 17 return {todo_id: todos[todo_id]} 18 19 if __name__ == '__main__': 20 app.run(debug=True)
運行改程序並在瀏覽器中訪問http://localhost:5000/hello/hello,結果以下:
設置ERROR_404_HELP爲False後的程序爲:
1 from flask import Flask, request 2 from flask_restplus import Resource, Api 3 4 app = Flask(__name__) 5 api = Api(app) 6 app.config['ERROR_404_HELP'] = False 7 8 todos = {} 9 10 @api.route('/<string:todo_id>') 11 class TodoSimple(Resource): 12 def get(self, todo_id): 13 return {todo_id: todos[todo_id]} 14 15 def put(self, todo_id): 16 todos[todo_id] = request.form['data'] 17 return {todo_id: todos[todo_id]} 18 19 if __name__ == '__main__': 20 app.run(debug=True)
再次運行並訪問http://localhost:5000/hello/hello,結果以下:
此處兩種狀況返回結果一致,還沒有嘗試出給出相近端點的建議信息,也許是我沒用使用對,後續再補充。
儘管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壞請求,以及一個高亮錯誤信息的響應。示例程序以下:
1 from flask import Flask, request 2 from flask_restplus import Resource, Api,reqparse 3 4 app = Flask(__name__) 5 6 api = Api(app) 7 8 from flask_restplus import reqparse 9 10 parser = reqparse.RequestParser() 11 parser.add_argument('rate', type=int,required=True,help='Rate to charge for this resource') 12 13 14 todos = { 15 '1':'eat', 16 '2':'sleep' 17 } 18 19 @api.route('/<string:todo_id>') 20 class TodoSimple(Resource): 21 def get(self, todo_id): 22 return {todo_id: todos[todo_id]} 23 24 def put(self, todo_id): 25 args = parser.parse_args() 26 todos[todo_id] = request.form['data'] 27 return {todo_id: todos[todo_id]} 28 29 if __name__ == '__main__': 30 app.run(debug=True)
其中,parser.add_argument('rate', type=int,required=True,help='Rate to charge for this resource')表示,參數名爲rate,數據類型爲int,請求時必須發送此參數,若是驗證不經過時將會返回help指定的信息。
運行程序並使用curl進行訪問,分別驗證如下幾種狀況:
結果分別以下:
另外,以參數strict=True調用parse_args()可以保證若是請求中包含了解析器中未定義的參數時,將會拋出一個錯誤。示例程序以下:
1 from flask import Flask, request 2 from flask_restplus import Resource, Api,reqparse 3 4 app = Flask(__name__) 5 6 api = Api(app) 7 8 from flask_restplus import reqparse 9 10 parser = reqparse.RequestParser() 11 parser.add_argument('rate', type=int,required=True,help='Rate to charge for this resource') 12 13 14 todos = { 15 '1':'eat', 16 '2':'sleep' 17 } 18 19 @api.route('/<string:todo_id>') 20 class TodoSimple(Resource): 21 def get(self, todo_id): 22 return {todo_id: todos[todo_id]} 23 24 def put(self, todo_id): 25 args = parser.parse_args(strict=True) 26 todos[todo_id] = todo_id 27 return {todo_id: todos[todo_id]} 28 29 if __name__ == '__main__': 30 app.run(debug=True)
此時,運行該程序並使用curl訪問,結果以下:
默認狀況下,在返回的可迭代對象中的全部字段都會原樣返回。雖然在處理Python基本數據結構時這種方式很不錯,可是當涉及到對象時將會變得很是棘手。爲了解決這個問題,Flask-RESTPlus提供了fields模塊和marshal_with()裝飾器。相似於Django ORM和WTForm,你可使用fields模塊來描述響應的數據結構。示例程序以下:
1 from flask import Flask 2 from flask_restplus import fields, Api, Resource 3 4 app = Flask(__name__) 5 api = Api(app) 6 7 model = api.model('Model', { 8 'task': fields.String, 9 'uri': fields.Url('todo_ep',absolute=True) # absolute參數表示生成的url是不是絕對路徑 10 }) 11 12 class TodoDao(object): 13 def __init__(self, todo_id, task): 14 self.todo_id = todo_id 15 self.task = task 16 17 # 該字段不會發送到響應結果中 18 self.status = 'active' 19 20 @api.route('/todo',endpoint='todo_ep') 21 class Todo(Resource): 22 @api.marshal_with(model) 23 def get(self, **kwargs): 24 return TodoDao(todo_id='my_todo', task='Remember the milk') 25 26 if __name__ == '__main__': 27 app.run(debug=True)
運行上述程序並使用curl訪問結果以下:
上述示例接受了一個Python對象,並將其進行結構轉換。而marshal_with()裝飾器就是用來對結果按照model的結構進行轉換的,從上面的結果和代碼中能夠知道,咱們僅僅從TodoDao對象中提取了task字段的值,而model中的fields.Url字段是一個特殊字段,它接受一個端點名,並在響應中生成該端點名對應的URL。此外,使用marshal_with()裝飾器還能夠以swagger規範對輸出進行歸檔。fields模塊中包含了你所須要的大多數類型,詳細信息能夠查看fields模塊的說明文檔。
默認狀況下,字段順序並未獲得保留,由於它會損耗性能。不過,若是你確實須要保留字段順序,那麼能夠向類或函數傳入一個ordered=True的參數項,以此強制進行順序保留:
本例中只舉例局部保留方式的使用方法,程序以下:
1 from flask import Flask 2 from flask_restplus import fields, Api, Resource 3 4 app = Flask(__name__) 5 api = Api(app) 6 7 model = api.model('Model', { 8 'task': fields.String, 9 'uri': fields.Url('todo_ep',absolute=True), # absolute參數表示生成的url是不是絕對路徑 10 'developer':fields.String(default='jack') 11 }) 12 13 class TodoDao(object): 14 def __init__(self, todo_id, task, developer): 15 self.todo_id = todo_id 16 self.task = task 17 self.developer = developer 18 19 # 該字段不會發送到響應結果中 20 self.status = 'active' 21 22 @api.route('/todo',endpoint='todo_ep') 23 class Todo(Resource): 24 # @api.marshal_with(model) 25 def get(self, **kwargs): 26 return api.marshal(TodoDao(todo_id='my_todo', task='Remember the milk', developer='Tom'),model,ordered=False) 27 28 if __name__ == '__main__': 29 app.run(debug=True)
運行並使用curl進行訪問,結果以下:
1 from flask import Flask 2 from flask_restplus import Api, Resource, fields 3 from werkzeug.contrib.fixers import ProxyFix 4 5 app = Flask(__name__) 6 app.wsgi_app = ProxyFix(app.wsgi_app) 7 8 api = Api(app, version='1.0', title='TodoMVC API', 9 description='A simple TodoMVC API', 10 ) 11 12 # 定義命名空間 13 ns = api.namespace('todos', description='TODO operations') 14 15 todo = api.model('Todo', { 16 'id': fields.Integer(readOnly=True, description='The task unique identifier'), 17 'task': fields.String(required=True, description='The task details') 18 }) 19 20 21 class TodoDAO(object): 22 def __init__(self): 23 self.counter = 0 24 self.todos = [] 25 26 def get(self, id): 27 for todo in self.todos: 28 if todo['id'] == id: 29 return todo 30 api.abort(404, "Todo {} doesn't exist".format(id)) 31 32 def create(self, data): 33 todo = data 34 todo['id'] = self.counter = self.counter + 1 35 self.todos.append(todo) 36 return todo 37 38 def update(self, id, data): 39 todo = self.get(id) 40 todo.update(data) 41 return todo 42 43 def delete(self, id): 44 todo = self.get(id) 45 self.todos.remove(todo) 46 47 48 DAO = TodoDAO() 49 DAO.create({'task': 'Build an API'}) 50 DAO.create({'task': '?????'}) 51 DAO.create({'task': 'profit!'}) 52 53 54 @ns.route('/') 55 class TodoList(Resource): 56 '''獲取全部todos元素,並容許經過POST來添加新的task''' 57 @ns.doc('list_todos') 58 @ns.marshal_list_with(todo) 59 def get(self): 60 '''返回全部task''' 61 return DAO.todos 62 63 @ns.doc('create_todo') 64 @ns.expect(todo) 65 @ns.marshal_with(todo, code=201) 66 def post(self): 67 '''建立一個新的task''' 68 return DAO.create(api.payload), 201 69 70 71 @ns.route('/<int:id>') 72 @ns.response(404, 'Todo not found') 73 @ns.param('id', 'The task identifier') 74 class Todo(Resource): 75 '''獲取單個todo項,並容許刪除操做''' 76 @ns.doc('get_todo') 77 @ns.marshal_with(todo) 78 def get(self, id): 79 '''獲取id指定的todo項''' 80 return DAO.get(id) 81 82 @ns.doc('delete_todo') 83 @ns.response(204, 'Todo deleted') 84 def delete(self, id): 85 '''根據id刪除對應的task''' 86 DAO.delete(id) 87 return '', 204 88 89 @ns.expect(todo) 90 @ns.marshal_with(todo) 91 def put(self, id): 92 '''更新id指定的task''' 93 return DAO.update(id, api.payload) 94 95 96 if __name__ == '__main__': 97 app.run(debug=True)
更多其餘示例參考GitHub。