Python Flask Programming for Beginner - 輕量級API快速開發
專案開發過程中,不論是內部架構分層設計或外部系統的資料介階,依現行的分層觀念,我們常會建議使用API的形式進行資料交換或控制。然而,在程式開發中,介接雙方往往希望對方都已經準備好相對應的Spec及mock server以利格式確認及程式開發。本文提供作法為不使用線上的solution(如postman)而是能直接透過 Python Flask 架構快速開發,進而提供彈性且不受限制的API建置模式。
為什麼選用Flask
在Python 的眾多Web框架中,Flask絕對是最輕量的框架(Microframework)。相較於包山包海的Django, Flask僅提供了基本必要的功能。所以,使用Flask時我們無法直接使用ORM、Access control及Authentication等功能,而是需要透過第三方的工具和extensions來完成。當然這也就代表了它的彈性及自主性。也許我們準備要開發的API功能,正好不需要ORM及Authentication認證。
再來是人氣指數,依2018年的Python開發人員年度調查顯示,Flask 為Python開發人員最愛使用的web框架,相較於前一年度成長了15%,47%的開發者正在使用或準備使用Flask。雖然我們都知道人氣指數和好不好用是兩回事,但高人氣同樣代表著資源多,發現問題時更容易的找到解決的支援及方案。
(數據來源為 Python Developers Survey 2018 Results)
Flask 的 VSCode 整合開發介紹
選擇一個適合的IDE,一定是開發人員最關切的一件事,本例使用VSCode做為整合開發之範例:
開始之前,請先安裝好VSCode、Python及設定相關環境境變數,本範例是用Python 3.7.5版本
安裝虛擬環境
相信在專案開發的過程中,經常會遇到版本問題,某套件只試用於某些版號下,而某些語法又會對某些版本套件產生衝突,使用者端的機器上的版號又和開發端的不一樣,以致於開發過程需要進行多次升、降版的測試,又亦或我們只是單純的希望本專案的版本不會影響到其它的專案,諸如此類的問題。
我們可以透過虛擬環境的功能,將本專案所需的專屬環境,建置(綁定)在特定目錄下,不論我們做了什麼事,都可以獨立於其它的開發環境。
首先透過CMD介面來安裝虛擬環境套件virtualenv,這邊建議套件管理完全透過 pip 指令來安裝及管理
pip install virtualenv
安裝完成後,我們可以透過指令 pip list 來確認已安裝的套件及版號列表
當然我們也可以用VSCode的整合開發功能來做後續的CLI介面操作 。
VSCode 操作介面
首頁進入VSCode 歡迎頁,點選檢視->終端機,或直接按ctrl+`打開終端機分割畫面
可以看到畫面下方出現powershell的終端機畫面
然後將目錄移至個人的工作目錄下(如: \Documents\Python\PyFlask) 建立虛擬環境
virtualenv --no-site-packages .ryantest
完成後訊息如下
Using base prefix 'c:\\users\\yltu1\\appdata\\local\\programs\\python\\python37'
New python executable in C:\Users\yltu1\Documents\Python\PyFlask\.ryantest\Scripts\python.exe
Installing setuptools, pip, wheel...
done.
這時就表示該工作目錄下,已經立(綁定)一新的虛擬環境。我們可以透過檔案總管檢視該目錄
可以看到已經將環境設定,套件目錄及CLI script建立完成了。
再來只需要啟動虛擬環境 .\.ryantest\Scripts\activate.ps1
PS C:\Users\yltu1\Documents\Python\PyFlask> .\.ryantest\Scripts\activate.ps1
(.ryantest) PS C:\Users\yltu1\Documents\Python\PyFlask>
就可以看到指令列的前方出現(.ryantest)開頭之指令列表,表示現在安裝的任何套件,只會應用在本虛擬環境(目錄)下之python專案內
當然,我們可以簡單的透過指令 deactivate 來關閉虛擬環境。
使用需擬環境安裝Flask
再來就開始隨便裝吧,把想裝想玩的,全部放進這個專案裡吧! 但這邊還是先介紹Flask的框架安裝。
輸入 pip install flask 如下
(.ryantest) PS C:\Users\yltu1\Documents\Python\PyFlask> pip install flask
Collecting flask
Using cached https://files.pythonhosted.org/packages/9b/93/628509b8d5dc749656a9641f4caf13540e2cdec85276964ff8f43bbb1d3b/Flask-1.1.1-py2.py3-none-any.whl
Collecting Jinja2>=2.10.1
Using cached https://files.pythonhosted.org/packages/65/e0/eb35e762802015cab1ccee04e8a277b03f1d8e53da3ec3106882ec42558b/Jinja2-2.10.3-py2.py3-none-any.whl
Collecting itsdangerous>=0.24
Using cached https://files.pythonhosted.org/packages/76/ae/44b03b253d6fade317f32c24d100b3b35c2239807046a4c953c7b89fa49e/itsdangerous-1.1.0-py2.py3-none-any.whl
Collecting click>=5.1
Using cached https://files.pythonhosted.org/packages/fa/37/45185cb5abbc30d7257104c434fe0b07e5a195a6847506c074527aa599ec/Click-7.0-py2.py3-none-any.whl
Collecting Werkzeug>=0.15
Using cached https://files.pythonhosted.org/packages/ce/42/3aeda98f96e85fd26180534d36570e4d18108d62ae36f87694b476b83d6f/Werkzeug-0.16.0-py2.py3-none-any.whl
Collecting MarkupSafe>=0.23
Using cached https://files.pythonhosted.org/packages/65/c6/2399700d236d1dd681af8aebff1725558cddfd6e43d7a5184a675f4711f5/MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl
Installing collected packages: MarkupSafe, Jinja2, itsdangerous, click, Werkzeug, flask
Successfully installed Jinja2-2.10.3 MarkupSafe-1.1.1 Werkzeug-0.16.0 click-7.0 flask-1.1.1 itsdangerous-1.1.0
就可以快速安裝完成。現在我們再看一下 pip list 中,我們在虛擬環境下安裝了那些東西
如果大家有興趣,可以試試裝另一個人氣框架django,就可以比較兩者間安裝套件份量的差異。
來個Hello吧
透過VSCode的檔案總管,或在終端機上鍵入 「code .」 開啟VSCode下的工作目錄。
在工作目錄下手動新增一檔案 main.py (如/PyFlask/main.py)
範例程式:
import flask
app = flask.Flask(__name__)
app.config["DEBUG"] = True
@app.route('/', methods=['GET'])
def home():
return "<h1>Hello, I am here !</h1>"
app.run()
定義從request 來的 / 根目錄,會直接return 一段html "<h1>Hello, I am here !</h1>"
然後就可以在終端機畫面執行 py main.py
可以看到執行結果成功 (讓我們先乎略favicon的問題吧)!
打開瀏覽器 ,網址列輸入 http:// 127.0.0.1:5000 就可以看到讓人開心的 Hello, I am here ! 了
API開發
API種類與格式
在討論API(或webservice時),最常聽到的兩個名字一定是 SOAP 和 Rest,但事實上他們是兩種完全不同的概念與技術,所以很難在同一維度上進行比較。
大致來說:
Rest (Representational State Transfer)
•是一種架構的方式,而非規格
•多數情況下使用的資料格式為JSON
•直接使用HTTP的協定及標準 (GET/POST/PUT/DELETE/PATCH)
•簡單/輕巧
SOAP (Simple Object Access Protocol)
•當然是一種規格、標準及協定
•必需使用XML格式做為交換標準格式
•使用自有協定,更加安全及可靠
•開發較複雜也需要較高的網路資源
更多API的資訊及解決方案可以參考這邊
PS. Rest 和 Restful 兩個用法常常聽到,兩個之間到底有什麼差別呢?不用太在意了,網路上給的定義是:
The short answer is that REST stands for Representational State Transfer. It’s an architectural pattern for creating web services. A RESTful service is one that implements that pattern.
快來用Flask建立Restful API吧!
前面我們已經把Python Flask 的框架進立了,現在就來建立我們的第一隻Restful API
範例程式:
from flask import Flask
from flask import jsonify, request
app = Flask(__name__)
ironman = {
"id":1,
"name":"Tony",
"nickname":"iron man",
"nationality":"American",
"gender":"M",
"superpower":"n"
}
spiderman = {
"id":2,
"name":"Peter",
"nickname":"spider man",
"nationality":"American",
"gender":"M",
"superpower":"y"
}
blockwidor = {
"id":3,
"name":"Natasha",
"nickname":"Block Widow",
"nationality":"Russia",
"gender":"F",
"superpower":"n"
}
avengers =[ironman, spiderman, blockwidor]
@app.route("/")
def hello():
return "Hello World!"
@app.route('/avengers/all', methods=['GET'])
def avengers_all():
return jsonify(avengers)
@app.route('/avengers', methods=['GET'])
def avengers_properties():
results = []
nationality = ""
if 'nationality' in request.args:
nationality = request.args['nationality']
else:
print("no hero")
for avenger in avengers:
if avenger['nationality'] == nationality:
results.append(avenger)
return jsonify(results)
if __name__ == '__main__':
app.debug = False
app.run(host='localhost', port=5000)
首先們定義本範例的Dataset -- avergers:[ironman, spiderman, blockwidor],其下也包含各自的屬性及內容,id、name、national、superpower、nickname、gender 等
from flask import jsonify, request
程式一開始,我們需要import另外兩個套件jsonify 和 request,這邊可以透過pip的方式這些3'rd party套件納入專案套件中
@app.route('/avengers/all', methods=['GET'])
描述前端request 進入的位置字串及方法,這邊先以GET做為範例
if 'nationality' in request.args:
nationality = request.args['nationality']
else:
print("no hero")
for avenger in avengers:
if avenger['nationality'] == nationality:
results.append(avenger)
邏輯部分可以依實際需求做改寫,這邊範例為,當request 透過 GET的方式,從網址例帶進來參數,而當程式 parse 到有 nationality 這個參數時,就會去比對 Dataset中,是否有包含這個參數的資料。
若有,就會將該比資料放入陣列 results 之中
return jsonify(results)
最後將結果以JSON的格式回傳。
透過postman 測試我們可以得到以下結果
除了GET之外呢?
當然沒問題,基本上Flask同樣可以支援 GET/POST/PATCH/PUT/DELETE
以POST為例:
@app.route('/avengers', methods=['POST'])
def avengers_post():
if 'id' in request.args:
id = request.args['id']
print ("id",id)
if 'name' in request.args:
name = request.args['name']
print ("name",name)
if 'nationality' in request.args:
nationality = request.args['nationality']
print ("nationality",nationality)
if 'nickname' in request.args:
nickname = request.args['nickname']
print ("nickname",nickname)
if 'superpower' in request.args:
superpower = request.args['superpower']
print ("superpower",superpower)
return jsonify(avengers)
我們將Method 改為 POST,我們就可以很簡單的把POST帶的資訊收下來供後續邏輯使用
結論及延伸
Python Flask 的特性就是可以讓我們很快速的開發出"接近完整"的API,不論要提供開發測試或是真正的上線使用,都是一套很成熟的解決方案。
後續我們可以持特擴大API後端的使用範圍,包含:如何將SQLAlchemy套用進ORM的資料庫關聯方式及延伸使用GraphQL開發成更強大的API。