經過上星期的教訓之後,趁著最近放假的時間把 Udemy 課程 — REST APIs with Flask and Python 看光,並動手實作一個簡單的 API 應用程式。這個課程的講者說話精闢,讓觀眾很容易把 Flask REST API 這個技能上手, 並有教授開發 API 其他要點和技巧,例如是使用 Postman 的一些進階方法,就算不是有關 Big Data 學習,這個課程的知識也有助我現在的項目。
講者有提供 Python 的入門學習,例如是 Syntax 和部份 Python OOP 的概念,我在這個教學文就省去,直接跳到 Flask 與 REST API 的介紹了。
[1] - 以 Flask 製作簡單的 Web API → 你在這裡
[2] — 以 Flask API 庫以更簡潔的代碼創造複雜的 Web API
[3] — API 與 Sqlite3 連接
[4] — 利用 flask-sqlalchemy 與不同的 production environment (如: PostgreSQL) 連接
[5] — 部署 Flask API 於 Heroku 或其他服務器
RESTful API: 什麼是 HTTP, Web Server 和 HTTP Verbs?
HTTP 是一個其中一個通訊網絡,讓兩台或以上的電腦交流。Web Server 就是一個用來 HTTP 接收請求的軟件。以下就是 Get Request 的例子
GET / HTTP/1.1
請求分了很多種類,主要使用的有以下四個:
- 當向 Web Server 請求內容時,它是 Get Request
- 當於請求內加入內容,並於回應使用時,它是 Post Request
- 當於請求內更新內容,或者確保一些東西存在的,它是 Put Request
- 當請求刪除時,它是 Delete Request
這些 HTTP 就是 API 的元素,但軟件工程學者覺得使用 API 是有標準的,否則會各自做自各的 API,所有就出現了 REST 設計模式,詳情可以參考這裡:RESTful API Designing guidelines(English) / RESTful API 設計指南(中文版)
環境設定:
Windows 10
Python 3.6
Visual Studio Code (安裝 Python Extension)
模擬我們將會建立一個 eshop 應用程式,它會有以下的 API 功能
- 創造帳號
- 登入帳號
- 建立商店
- 查詢現有所有商店
- 查詢商店資訊
- 更新商店資訊
- 刪除商店
- 建立商店貨品
- 查詢單一商店內所有貨品
- 更新貨品價格
- 刪除單一商店內貨品
Iteration 1: 使用 Flask 來建立一個 API
首先我們透過 Python 庫管理工具 pip 安裝 Flask。開啟 CMD 然後輸入以下指令:
pip3 install flask
Flask 是一個輕量級的 Python Web Framework,主要由請求 (Request) 與回應 (Response) 構成。Request 就好像瀏覽器,當你去一個網站,你就是建立了一個 Request 。當那個網站背後的服務器收到之後,就會觸發一連串的動作,最後返回 Response 給你。
首先,先建立圍繞商店的功能
開啟 VS Code 並建立一個新資料夾 Iteration_1_Flask REST API,在裡面,建立檔案 app.py
在 app.py,輸入以下代碼
from flask import Flask
app = Flask(__name__)
app.run(port=5000, debug=True)
在 CMD 輸入以下指令,你應該可以成功運行 Flask 的 Web Server 並看到以下的句子:
* Restarting with stat
* Debugger is active!
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
由於我們需要建立商店有關的功能,大概想了一下,會有以下的 API Endpoint
- POST /store data: {name:}
- GET /store/<string:name>
- GET /store
- POST /store<string:name>/item
- GET /store/<string:name>/item
首先,我們先建立所有 endpoint 的 route, 之後才實現它們的功能
from flask import Flaskapp = Flask(__name__)#post /store data: {name :}
@app.route('/store' , methods=['POST'])
def create_store():
pass#get /store/<name> data: {name :}
@app.route('/store/<string:name>')
def get_store(name):
pass#get /store
@app.route('/store')
def get_stores():
pass#post /store/<name> data: {name :}/item
@app.route('/store/<string:name>/item' , methods=['POST'])
def create_item_in_store(name):
pass#get /store/<name>/item data: {name :}/item
@app.route('/store/<string:name>/item')
def get_item_in_store(name):
passapp.run(port=5000, debug=True)
當中的 app.route 就是 Flask 的 routing decorator,而第一個需要輸入的參數是 endpoint 的路徑,如果需要從 API 傳入參數到路徑,就需要使用 <> 符號,例如 @app.route(‘/store/<string:name>’)
在 decorator 後面就是 API 的方法,它定義了 API 被觸發後的動作,最終給予結果
我們從比較簡單的方法出發,那就是 GET 所有商店。在建立之前,我們先 hard code 了 2 個商店,而我們使用 Dictionary 作為儲存的數據結構
stores = [{
'name': 'Elton's first store',
'items': [{'name':'my item 1', 'price': 30 }],
},
{
'name': 'Elton's second store',
'items': [{'name':'my item 2', 'price': 15 }],
},
]
要 GET 所有結果,直接 Call stores 這個變數就行了。但要是給予 Client 結果,就需要把數據結構從 Dictionary 轉化成 JSON。此時我們需要使用 Jsonify 這個庫
from flask import Flask, jsonify
...#get /store
@app.route('/store')
def get_stores():
return jsonify(stores)...
此時運行 python app.py,再打開 Postman,你應該可以 GET 到所有商店
那我們把其他的 GET 端口都實現吧~我們先 iterate 現有的商店,如找到資料,則 return 該結果
from flask import Flask, jsonify
...#get /store
@app.route('/store')
def get_stores():
return jsonify(stores)@app.route('/store/<string:name>')
def get_store(name):
for store in stores:
if store['name'] == name:
return jsonify(store)
return jsonify ({'message': 'store not found'})#get /store/<name>/item data: {name :}
@app.route('/store/<string:name>/item')
def get_item_in_store(name):
for store in stores:
if store['name'] == name:
return jsonify( {'items':store['items'] } )
return jsonify ({'message':'store not found'})...
完成 GET 之後就到 POST 了。
POST 的話 Endpoint 會先傳送數據,我們需要讓 Flask 讀取 JSON,所以需要 import request 庫
from flask import Flask, jsonify, request
然後,我們使用調用 request.get_json() 方法,把 json 轉化成 object,再以 dictionary 形式儲存,最後 return 已儲存的數據。最後你的 code 應該是這個樣子
from flask import Flask,jsonify,requestapp = Flask(__name__)stores = [{
'name': 'Elton's first store',
'items': [{'name':'my item 1', 'price': 30 }],
},
{
'name': 'Elton's second store',
'items': [{'name':'my item 2', 'price': 15 }],
},
]#get /store
@app.route('/store')
def get_stores():
return jsonify(stores)@app.route('/store/<string:name>')
def get_store(name):
for store in stores:
if store['name'] == name:
return jsonify(store)
return jsonify ({'message': 'store not found'})#get /store/<name>/item data: {name :}
@app.route('/store/<string:name>/item')
def get_item_in_store(name):
for store in stores:
if store['name'] == name:
return jsonify( {'items':store['items'] } )
return jsonify ({'message':'store not found'})#post /store data: {name :}
@app.route('/store' , methods=['POST'])
def create_store():
request_data = request.get_json()
new_store = {
'name':request_data['name'],
'items':[]
}
stores.append(new_store)
return jsonify(new_store)#post /store/<name> data: {name :}
@app.route('/store/<string:name>/item' , methods=['POST'])
def create_item_in_store(name):
request_data = request.get_json()
for store in stores:
if store['name'] == name:
new_item = {
'name': request_data['name'],
'price': request_data['price']
}
store['items'].append(new_item)
return jsonify(new_item)app.run(port=5000, Debug=True)
下一步就是使用 Flask API 庫,以更簡潔的代碼創造複雜的 Web API!