python flask api

Python Flask Programming for Beginner - 輕量級API快速開發

Ryan 2019/12/26 09:00:00
9588

專案開發過程中,不論是內部架構分層設計或外部系統的資料介階,依現行的分層觀念,我們常會建議使用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。

 

 

Ryan