API Star:一個 Python 3 的 API 框架

weixin_33806914發表於2018-10-14

為了在 Python 中快速構建 API,我主要依賴於 Flask。最近我遇到了一個名為 “API Star” 的基於 Python 3 的新 API 框架。由於幾個原因,我對它很感興趣。首先,該框架包含 Python 新特點,如型別提示和 asyncio。而且它再進一步為開發人員提供了很棒的開發體驗。我們很快就會講到這些功能,但在我們開始之前,我首先要感謝 Tom Christie,感謝他為 Django REST Framework 和 API Star 所做的所有工作。

現在說回 API Star —— 我感覺這個框架很有成效。我可以選擇基於 asyncio 編寫非同步程式碼,或者可以選擇傳統後端方式就像 WSGI 那樣。它配備了一個命令列工具 —— apistar 來幫助我們更快地完成工作。它支援 Django ORM 和 SQLAlchemy,這是可選的。它有一個出色的型別系統,使我們能夠定義輸入和輸出的約束,API Star 可以自動生成 API 的模式(包括文件),提供驗證和序列化功能等等。雖然 API Star 專注於構建 API,但你也可以非常輕鬆地在其上構建 Web 應用程式。在我們自己構建一些東西之前,所有這些可能都沒有意義的。

開始

我們將從安裝 API Star 開始。為此實驗建立一個虛擬環境是一個好主意。如果你不知道如何建立一個虛擬環境,不要擔心,繼續往下看。


pip install apistar

(譯註:上面的命令是在 Python 3 虛擬環境下使用的)

如果你沒有使用虛擬環境或者你的 Python 3 的 pip 名為 pip3,那麼使用 pip3 install apistar 代替。

一旦我們安裝了這個包,我們就應該可以使用 apistar 命令列工具了。我們可以用它建立一個新專案,讓我們在當前目錄中建立一個新專案。


apistar new .

現在我們應該建立兩個檔案:app.py,它包含主應用程式,然後是 test.py,它用於測試。讓我們來看看 app.py 檔案:


from apistar import Include, Route
from apistar.frameworks.wsgi import WSGIApp as App
from apistar.handlers import docs_urls, static_urls
def welcome(name=None):
    if name is None:
        return {'message': 'Welcome to API Star!'}
    return {'message': 'Welcome to API Star, %s!' % name}
routes = [
    Route('/', 'GET', welcome),
    Include('/docs', docs_urls),
    Include('/static', static_urls)
]
app = App(routes=routes)
if __name__ == '__main__':
    app.main()

在我們深入研究程式碼之前,讓我們執行應用程式並檢視它是否正常工作。我們在瀏覽器中輸入 http://127.0.0.1:8080/,我們將得到以下響應:


{"message": "Welcome to API Star!"}

如果我們輸入:http://127.0.0.1:8080/?name=masnun


{"message": "Welcome to API Star, masnun!"}

同樣的,輸入 http://127.0.0.1:8080/docs/,我們將看到自動生成的 API 文件。

現在讓我們來看看程式碼。我們有一個 welcome 函式,它接收一個名為 name 的引數,其預設值為 None。API Star 是一個智慧的 API 框架。它將嘗試在 url 路徑或者查詢字串中找到 name 鍵並將其傳遞給我們的函式,它還基於其生成 API 文件。這真是太好了,不是嗎?

然後,我們建立一個 RouteInclude 例項的列表,並將列表傳遞給 App 例項。Route 物件用於定義使用者自定義路由。顧名思義,Include 包含了在給定的路徑下的其它 url 路徑。

路由

路由很簡單。當構造 App 例項時,我們需要傳遞一個列表作為 routes 引數,這個列表應該有我們剛才看到的 RouteInclude 物件組成。對於 Route,我們傳遞一個 url 路徑,http 方法和可呼叫的請求處理程式(函式或者其他)。對於 Include 例項,我們傳遞一個 url 路徑和一個 Routes 例項列表。

路徑引數

我們可以在花括號內新增一個名稱來宣告 url 路徑引數。例如 /user/{user_id} 定義了一個 url,其中 user_id 是路徑引數,或者說是一個將被注入到處理函式(實際上是可呼叫的)中的變數。這有一個簡單的例子:


from apistar import Route
from apistar.frameworks.wsgi import WSGIApp as App
def user_profile(user_id: int):
    return {'message': 'Your profile id is: {}'.format(user_id)}
routes = [
    Route('/user/{user_id}', 'GET', user_profile),
]
app = App(routes=routes)
if __name__ == '__main__':
    app.main()

如果我們訪問 http://127.0.0.1:8080/user/23,我們將得到以下響應:


{"message": "Your profile id is: 23"}

但如果我們嘗試訪問 http://127.0.0.1:8080/user/some_string,它將無法匹配。因為我們定義了 user_profile 函式,且為 user_id 引數新增了一個型別提示。如果它不是整數,則路徑不匹配。但是如果我們繼續刪除型別提示,只使用 user_profile(user_id),它將匹配此 url。這也展示了 API Star 的智慧之處和利用型別和好處。

包含/分組路由

有時候將某些 url 組合在一起是有意義的。假設我們有一個處理使用者相關功能的 user 模組,將所有與使用者相關的 url 分組在 /user 路徑下可能會更好。例如 /user/new/user/1/user/1/update 等等。我們可以輕鬆地在單獨的模組或包中建立我們的處理程式和路由,然後將它們包含在我們自己的路由中。

讓我們建立一個名為 user 的新模組,檔名為 user.py。我們將以下程式碼放入這個檔案:


from apistar import Route
def user_new():
    return {"message": "Create a new user"}
def user_update(user_id: int):
    return {"message": "Update user #{}".format(user_id)}
def user_profile(user_id: int):
    return {"message": "User Profile for: {}".format(user_id)}
user_routes = [
    Route("/new", "GET", user_new),
    Route("/{user_id}/update", "GET", user_update),
    Route("/{user_id}/profile", "GET", user_profile),
]

現在我們可以從 app 主檔案中匯入 user_routes,並像這樣使用它:


from apistar import Include
from apistar.frameworks.wsgi import WSGIApp as App
from user import user_routes
routes = [
    Include("/user", user_routes)
]
app = App(routes=routes)
if __name__ == '__main__':
    app.main()

現在 /user/new 將委託給 user_new 函式。

訪問查詢字串/查詢引數

查詢引數中傳遞的任何引數都可以直接注入到處理函式中。比如 url /call?phone=1234,處理函式可以定義一個 phone 引數,它將從查詢字串/查詢引數中接收值。如果 url 查詢字串不包含 phone 的值,那麼它將得到 None。我們還可以為引數設定一個預設值,如下所示:


def welcome(name=None):
    if name is None:
        return {'message': 'Welcome to API Star!'}
    return {'message': 'Welcome to API Star, %s!' % name}

在上面的例子中,我們為 name 設定了一個預設值 None

注入物件

通過給一個請求程式新增型別提示,我們可以將不同的物件注入到檢視中。注入請求相關的物件有助於處理程式直接從內部訪問它們。API Star 內建的 http 包中有幾個內建物件。我們也可以使用它的型別系統來建立我們自己的自定義物件並將它們注入到我們的函式中。API Star 還根據指定的約束進行資料驗證。

讓我們定義自己的 User 型別,並將其注入到我們的請求處理程式中:


from apistar import Include, Route
from apistar.frameworks.wsgi import WSGIApp as App
from apistar import typesystem
class User(typesystem.Object):
    properties = {
    'name': typesystem.string(max_length=100),
    'email': typesystem.string(max_length=100),
    'age': typesystem.integer(maximum=100, minimum=18)
    }
    required = ["name", "age", "email"]
def new_user(user: User):
    return user
routes = [
    Route('/', 'POST', new_user),
]
app = App(routes=routes)
if __name__ == '__main__':
    app.main()

現在如果我們傳送這樣的請求:


curl -X POST \
  http://127.0.0.1:8080/ \
  -H 'Cache-Control: no-cache' \
  -H 'Content-Type: application/json' \
  -d '{"name": "masnun", "email": "masnun@gmail.com", "age": 12}'

猜猜發生了什麼?我們得到一個錯誤,說年齡必須等於或大於 18。型別系允許我們進行智慧資料驗證。如果我們啟用了 docs url,我們還將自動記錄這些引數。

傳送響應

如果你已經注意到,到目前為止,我們只可以傳遞一個字典,它將被轉換為 JSON 並作為預設返回。但是,我們可以使用 apistar 中的 Response 類來設定狀態碼和其它任意響應頭。這有一個簡單的例子:


from apistar import Route, Response
from apistar.frameworks.wsgi import WSGIApp as App
def hello():
    return Response(
    content="Hello".encode("utf-8"),
    status=200,
    headers={"X-API-Framework": "API Star"},
    content_type="text/plain"
    )
routes = [
    Route('/', 'GET', hello),
]
app = App(routes=routes)
if __name__ == '__main__':
    app.main()

它應該返回純文字響應和一個自定義標響應頭。請注意,content 應該是位元組,而不是字串。這就是我編碼它的原因。

繼續

我剛剛介紹了 API Star 的一些特性,API Star 中還有許多非常酷的東西,我建議通過 Github Readme 檔案來了解這個優秀框架所提供的不同功能的更多資訊。我還將嘗試在未來幾天內介紹關於 API Star 的更多簡短的,集中的教程。

原文地址:https://linux.cn/article-9987-1.html

相關文章