2019年4月25日 星期四

[GAE] 使用Google Endpoints建立REST API

  如題,我們要來使用Google Endpoints建立REST API。
  什麼是REST API?   REST全名Representational State Transfer,中文譯作表徵性狀態傳輸,嗯…我們很難從名字理解這是什麼東西。

  REST其實是一種風格架構,通常是透過HTTP協定進行通訊,Client端透過HTTP request提出對於Server端資源的操作請求,資源的操作包括取得、建立、修改和刪除(就是俗稱的CRUD,Create、Retrieve、Update、Delete),Sever端也會以HTTP回應資源,資源通常以JSON或XML表示。

  HTTP協定中的每一個request都是獨立,stateless特性讓Sever端與Client端低耦合,因此REST架構延展性佳,方便擴展新功能。Server端就負責維護資源狀態,Client端透過URI指定資源,使用HTTP協定提供的GET、POST、PUT和DELETE對映取得、建立、修改和刪除達到對資源的操作。

範例 (資料來源:WIKI):
資源
GET
PUT
POST
DELETE
一組資源的URI,比如https://example.com/resources/
列出URI,以及該資源組中每個資源的詳細資訊(後者可選)。
使用給定的一組資源替換目前整組資源。
在本組資源中建立/追加一個新的資源。該操作往往返回新資源的URL
刪除整組資源。
單個資源的URI,比如https://example.com/resources/142
取得指定的資源的詳細資訊,格式可以自選一個合適的網路媒體類型(比如:XMLJSON等)
替換/建立指定的資源。並將其追加到相應的資源組中。
把指定的資源當做一個資源組,並在其下建立/追加一個新的元素,使其隸屬於目前資源。
刪除指定的元素。

HTTP狀態碼:
  • 200 OK:成功
  • 204 No Content:成功,但回應沒有資料,通常是DELETE請求的回傳
  • 400 Bad Request:請求本身有錯
  • 401 Unauthorized:無授權
  • 404 Not Found:找不到想要的資源
  • 405 Method Not Allowed:該資源不支援這個HTTP動作
  • 409 Conflict:代表更新系統狀態時出現衝突
  • 500 Internal Server Error:伺服器內部錯誤
  • 503 Service Unavailable:伺服器暫時無法提供服務

  在介紹如何使用Google Cloud Endpoints之前,不知各位有沒有跟我一樣的疑問,我們之前做的以webapp2提供的RequestHandler不就能處理HTTP傳輸,何必還要使用Cloud Endpoints,而在stackoverflow也有類似的發問(https://stackoverflow.com/questions/24211737/google-cloud-endpoints-vs-normal-request-handlers-for-small-webapps),可以看看,總之Cloud Endpoints有它的好處像是有監控後台以及支援library之類的。


  Google Cloud Endpoints服務提供我們開發、部署、保護和監控REST API。   部屬API在Cloud Endpoints上有三種做法 (參考:https://cloud.google.com/endpoints/docs/quickstart-endpoints):
  1. Endpoints OpenAPI
  2. Endpoints gRPC
  3. Endpoints Framework    
  前兩個方法因為環境依賴Kubernets或Compute Engine+Docker,目前我試這些都是想要跑在Google App Engine這個運算平台上,OpenAPI雖在Google App Engine的環境可用,但不能Local端測試,也還是算了,所以下面只會以Endpoints Framework來建立我們的REST API。

  Cloud Endpoints以Google Protocol RPC Library為根基,這是一套在HTTP協定之上實作RPC服務的軟體開發框架。運作上從請求接收某一訊息類型的物件,在回應裡回傳另一訊息類型,訊息類型繼承自protorpc.messages.Message,而服務則繼承自protorpc.remote.Service。

  目前Google App Engine改採用Cloud Endpoints Frameworks 2.0,1.0版的方法是不建議使用了,架設Cloud Endpoints Frameworks 2.0環境的方法請看這:遷移到 Cloud Endpoints Frameworks 2.0 版,大致上就是先下載google-endpoints函式庫再做app.yaml libraries設定。



  那下面我們就以官方提供的echo api作為例子,功能就是client端送字串與一n值,server會回傳重複n次的字串回來。Code可參考這:https://github.com/GoogleCloudPlatform/python-docs-samples/tree/master/appengine/standard/endpoints-frameworks-v2/echo,下面有經過簡化。   app.yaml:
runtime: python27
api_version: 1
threadsafe: true

handlers:
- url: /_ah/api/.*
  script: main_api.api

libraries:
- name: pycrypto
  version: 2.6

- name: ssl
  version: 2.7.11

  main_api.py:先定義訊息類別,有點像在定義ORM model,會定義欄位與型別,欄位參數內有一整數,這個整數是識別碼,在訊息類別內必須獨一無二。
import endpoints
from endpoints import message_types
from endpoints import messages
from endpoints import remote

# [START messages]
class EchoRequest(messages.Message):
    message = messages.StringField(1)

class EchoResponse(messages.Message):
    message = messages.StringField(1)

ECHO_RESOURCE = endpoints.ResourceContainer(
    EchoRequest,
    n=messages.IntegerField(2, default=1)
)
# [END messages]

  * 如果要求包含路徑或查詢字串引數(query string arguments),請使用適當的ResourceContainer 取代 Message 類別。看下面的request,n在URL路徑後加上?n=...表示值。

main_api.py:加入服務
@endpoints.api(name='echo', version='v1')
class EchoApi(remote.Service):
    @endpoints.method(
        ECHO_RESOURCE,  # 求用的訊息類別
        EchoResponse,  # 回應中的訊息類別
        path='echo',
        http_method='POST',
        name='echo')  # 代表端點名的字串
    def echo(self, request):
        output_message = ' '.join([request.message]*request.n)
        return EchoResponse(message=output_message)

  * 請求path會是這樣:/_ah/api/[name]/[version]/[path]

  * 如果要求主體中未顯示引數 (例如在 GET 要求中),您可以省略要求的 Message 類別,只使用 message_types.VoidMessage 值。

  或是
@endpoints.method(
        ECHO_RESOURCE,
        EchoResponse,
        path='echo/{n}',
        http_method='POST',
        name='echo_path_parameter')
    def echo_path_parameter(self, request):
        output_message = ' '.join([request.message]*request.n)
        return EchoResponse(message=output_message)
  如此輸入 /_ah/api/echo/v1/echo/3,n會等於3

  最後別忘記要開啟api server,在main_api.py後加上程式碼。
  main_api.py:
# [START api_server]
api = endpoints.api_server([EchoApi])
# [END api_server]


Local測試:     1. dev_appserver.py app.yaml
    2. 瀏覽器輸入 http:localhost:8080/_ah/api/explorer 會看到Google API Explorer
  你大概會看到兩個警告,一個是說這個網站已經過時現在改用Cloud Endpoints Portal,另一個則是HTTP安全性問題,這些暫且不管,我們還是能試用我們完成的API。
  3. 點 echo API再點echo.echo,就可以輸入測試,點選Execute without OAuth執行。

  4. 另外你也可以透過window cmd來送出請求,打上這樣的命令:curl -X POST -H "Content-type: application/json" -d "{\"message\": \"hello\"}" http://localhost:8080/_ah/api/echo/v1/echo?n=3     ( \ :跳脫字元;^&:^可用來跳脫&)
  也試試echo_path_parameter


  題外話,其實試了一下message也可放在URL中,n也可放body中,會優先body的值。




參考資料:
  1. 官方資料:https://cloud.google.com/endpoints/docs/frameworks/python/create_api
  2. 雲端網頁程式設計:Google App Engine使用Python (博碩文化出版)

沒有留言:

張貼留言