跳至主要內容
3.2 API 開發要點
一個月前已更新

本節要點

  • 掌握API開發中的要點

  • 掌握JSONPath的概念

  • 瞭解內網穿透的概念

課前準備

課程內容

API開發要點

預覽推播的格式

擴展功能 - 資料推播 - 欄位對照表及JSON樣例

1. 判斷推播類型*

op:推播事件;包括data_create(資料送出)、data_update(資料修改)、data_remove(資料刪除)、data_recover(資料恢復)

透過op來區分具體的推播事件

from flask import Flask, request import json app = Flask(__name__)      @app.route('/test/', methods=['POST'])    #因為Jodoo只接收POST請求 def hello_world():          print("params: ",request.args)     print("headers: ",request.headers)     print("未編碼data: ",request.data)       print("已編碼data: ",json.loads(request.data))     return 'success',200 if __name__ == '__main__':     app.run(host='0.0.0.0',port=10000)
url: http://101.132.103.228:10000/test/?nonce=5932d0&timestamp=1585724440 params:  ImmutableMultiDict([('nonce', '5932d0'), ('timestamp', '1585724440')]) headers:  Content-Type: application/json X-Jdy-Deliverid: 2b85594d-454a-4063-ac1d-91a2b3318b63 Host: 101.132.103.228:10000 Content-Length: 765 Connection: close 原始data:  b'{"data":{"_id":"5dd6740646357c0006e6eb6e","_widget_1574134687834":"\xe6\xb6\xa1\xe8\xbd\xae\xe5\x87\x8f\xe9\x80\x9f\xe6\x9c\xba1","_widget_1574134687849":"DJSM9K2000","_widget_1574134687894":"2019-11-21T11:24:54.000Z","_widget_1574134688078":345999,"_widget_1574134688093":101,"_widget_1574134688108":3459990,"_widget_1574134688185":"\xe5\x8d\x97\xe4\xba\xac\xe6\x82\x9f\xe5\xb8\x86\xe7\xa7\x91\xe6\x8a\x80\xe8\x82\xa1\xe4\xbb\xbd\xe6\x9c\x89\xe9\x99\x90\xe5\x85\xac\xe5\x8f\xb8","_widget_1574142293244":"TSCJ001574335494","appId":"5dce13f43087860006c70e7a","createTime":"2019-11-21T11:24:54.213Z","creator":{"_id":"5cecd40dd23e194ab0867aab","name":"\xe6\x9f\xa5\xe7\x90\x86","username":"cxt7777"},"deleteTime":null,"deleter":null,"entryId":"5dce145c26aecf00062e7db0","formName":"\xe8\xae\xa2\xe5\x8d\x95\xe6\xb1\x87\xe6\x80\xbb","updateTime":"2020-04-01T07:00:40.876Z","updater":{"_id":"5cecd40dd23e194ab0867aab","name":"\xe6\x9f\xa5\xe7\x90\x86","username":"cxt7777"}},"op":"data_update"}' 轉換成Python字典後的data:  {'data': {'_id': '5dd6740646357c0006e6eb6e', '_widget_1574134687834': '渦輪減速機1', '_widget_1574134687849': 'DJSM9K2000', '_widget_1574134687894': '2019-11-21T11:24:54.000Z', '_widget_1574134688078': 345999, '_widget_1574134688093': 101, '_widget_1574134688108': 3459990, '_widget_1574134688185': '南京悟帆科技股份有限公司', '_widget_1574142293244': 'TSCJ001574335494', 'appId': '5dce13f43087860006c70e7a', 'createTime': '2019-11-21T11:24:54.213Z', 'creator': {'_id': '5cecd40dd23e194ab0867aab', 'name': '查理', 'username': 'cxt7777'}, 'deleteTime': None, 'deleter': None, 'entryId': '5dce145c26aecf00062e7db0', 'formName': '訂單彙總', 'updateTime': '2020-04-01T07:00:40.876Z', 'updater': {'_id': '5cecd40dd23e194ab0867aab', 'name': '查理', 'username': 'cxt7777'}}, 'op': 'data_update'}

2. 多線程處理

Threading :Python 實現多線程編程需要藉助於 threading 模塊,我們使用多線程來防止處理時間長導致回應超時;

多線程類似於同時執行多個不同程式

  • 使用線程可以把佔據長時間的程式中的任務放到後台去處理;

  • 使用者界面可以更加吸引人,比如使用者點擊了一個按鈕去觸發某些事件的處理,可以彈出一個進度條來顯示處理的進度;

  • 程式的運行速度可能加快;

  • 在一些等待的任務實現上如使用者輸入、文件讀寫和網絡收發資料等,線程就比較有用了。在這種情況下我們可以釋放一些珍貴的資源如內存佔用等等。

threading.Thread(target=<函數名>).start() # 多線程賦值 threading.Thread(target=<函數名>, args=(<變數>,).start()
import threading import time def handle_a():     for i in range(5):         print("a",time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) )         time.sleep(1)          def handle_b():     for i in range(5):         print("b",time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) )         time.sleep(1)     #使用多線程 threading.Thread(target=handle_a).start() time.sleep(0.1) handle_b() #正常按順序執行函數 handle_a() handle_b() #傳入參數 def printtime(a):     print(a, time.time()) x = 123 threading.Thread(target=print,args=(x,)).start()

3. API 加密

加密:防止 API 被攻擊;

import hashlib  def get_signature(nonce, payload, secret, timestamp):     content = ':'.join([nonce, payload, secret, timestamp]).encode('utf-8')     m = hashlib.sha1()     m.update(content)     return m.hexdigest()  @app.route('/callback', methods=['POST']) def callback():     payload = request.data.decode('utf-8')     nonce = request.args['nonce']     timestamp = request.args['timestamp']     if request.headers['x-jdy-signature'] != get_signature(nonce, payload, 'test-secret', timestamp):         return 'fail', 401     threading.Thread(target=handle, args=(json.loads(payload), )).start()     return 'success' 

JSONPath

測試網址:點擊查看

{     "data": [         {             "creator": {                 "_id": "5cecd40dd23e194ab0867aab",                 "name": "查理",                 "username": "cxt7777"             },             "updater": {                 "_id": "5cecd40dd23e194ab0867aab",                 "name": "查理",                 "username": "cxt7777"             },             "deleter": null,             "createTime": "2020-03-26T02:41:06.491Z",             "updateTime": "2020-03-26T02:46:27.825Z",             "deleteTime": null,             "_widget_1557886562320": "iPhone 11",             "_widget_1557886562335": "5998",             "_widget_1557886562350": "17",             "_id": "5e7c164229e01a00063be284",             "appId": "5e798363b587cc0006b40445",             "entryId": "5cdb765b5a6ae613aeed0f84"         },         {             "creator": {                 "_id": "5cecd40dd23e194ab0867aab",                 "name": "查理",                 "username": "cxt7777"             },             "updater": {                 "_id": "5cecd40dd23e194ab0867aab",                 "name": "查理",                 "username": "cxt7777"             },             "deleter": null,             "createTime": "2020-03-26T02:47:02.037Z",             "updateTime": "2020-03-26T02:47:02.037Z",             "deleteTime": null,             "_widget_1557886562320": "iPhone X",             "_widget_1557886562335": "4998",             "_widget_1557886562350": "5",             "_id": "5e7c17a650bccb0006441778",             "appId": "5e798363b587cc0006b40445",             "entryId": "5cdb765b5a6ae613aeed0f84"         },         {             "creator": {                 "_id": "5cecd40dd23e194ab0867aab",                 "name": "查理",                 "username": "cxt7777"             },             "updater": {                 "_id": "5cecd40dd23e194ab0867aab",                 "name": "查理",                 "username": "cxt7777"             },             "deleter": null,             "createTime": "2020-03-26T02:47:43.059Z",             "updateTime": "2020-03-26T02:47:43.059Z",             "deleteTime": null,             "_widget_1557886562320": "iPhone 8",             "_widget_1557886562335": "3998",             "_widget_1557886562350": "32",             "_id": "5e7c17cfcd87510006cf8189",             "appId": "5e798363b587cc0006b40445",             "entryId": "5cdb765b5a6ae613aeed0f84"         }     ] } 

提取規則類似Python:

取出iPhone 11的商品數量(“_widget_1557886562350”)

$.data[0]._widget_1557886562350

$.data[0][‘_widget_1557886562350’]

內網穿透(演示)

現在我們編寫的API,僅能在本地(你自己的主機,localhost 或 127.0.0.1)造訪,我們需要將本地端口映射至公網環境,才能被Jodoo或是其他人造訪(這裡不要求掌握如何進行內網穿透,瞭解概念即可);

當然也可以直接把服務部署在具有公網地址的雲伺服器(ECS)上,這是我們下節課介紹的內容!

這裡編寫了一個API,請求與返回格式均為JSON

from flask import Flask, request import json app = Flask(__name__)      @app.route('/test/', methods=['POST'])    #因為Jodoo只接收POST請求 def hello_world():          print("params: ",request.args)     print("headers: ",request.headers)     print("未編碼data: ",request.data)       print("已編碼data: ",json.loads(request.data))     return 'success',200 if __name__ == '__main__':     app.run(host='0.0.0.0',port=3100)

是否回答了您的問題?