TDX 運輸資料流通服務 API 介接範例

API 認證機制

TDX API 皆使用 OIDC Client Credentials 流程進行身份認證,認證完成後即取得 Access Token,將Access Token 帶入即可存取 TDX API 服務

API 認證機制操作步驟

STEP1. 註冊 TDX 會員並取得 API 金鑰

可參考筆記 TDX 註冊流程

STEP2. 取得 Access Token 接 API

2-1. 事前需準備的套件 :

Axios CDN : 方便使用 ajax 的套件。 ( cdn.js 連結 )

1
https://cdnjs.cloudflare.com/ajax/libs/axios/1.0.0-alpha.1/axios.min.js

2-2. 取得 Access Token 介接 PAI :

這邊使用 TDX 文件中 JavaScript 資料夾內的 Sample.js 範例程式碼

  1. 把範例程式碼中的 ajax 調整成 axios,如下:

    1. 準備好 API 金鑰

      1. Client Id 與 Client Secret 。可參考 「 TDX 註冊流程
    2. 取得 Access Token

      1. 為下方調整後程式碼中的 GetAuthorizationHeader(),帶入 Client IdClient Secret 發出網路請求成功後,伺服器回傳 tokenCode
    3. 呼叫 TDX API 服務

      1. b 步驟回傳的 tokenCode ( Access Toke ) 帶入 Headers 做驗證,成功後就會回傳 API 資料。 ( 下方調整後程式碼中的 GetApiResponse()GetApiResponse2() )

        1
        2
        3
        4
        5
        axios.get(url, {
        headers: {
        authorization: Bearer ACCESS_TOKEN
        }
        })
    4. 調整後程式碼 ( CodePen )

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      let tokenCode = '';
      // ------ 初始化
      function init() {
      GetAuthorizationHeader();
      }
      init();

      // ------ 呼叫 API 服務取得資料
      function GetApiResponse() {
      const url = `https://tdx.transportdata.tw/api/basic/v2/Tourism/ScenicSpot/Keelung?%24top=33&%24format=JSON`;
      axios.get(url, {
      headers: {
      authorization: `Bearer ${tokenCode}`
      }
      })
      .then((res) => {
      console.log(res);
      console.log('GetApiResponse');
      })
      .catch((err) => {
      console.log(err);
      })
      }
      function GetApiResponse2() {
      console.log(tokenCode);
      const url = `https://tdx.transportdata.tw/api/basic/v2/Bike/Availability/City/NewTaipei?%24top=24&%24format=JSON`;
      axios.get(url, {
      headers: {
      authorization: `Bearer ${tokenCode}`
      }
      })
      .then((res) => {
      console.log(res);
      })
      .catch((err) => {
      console.log(err);
      })
      }

      // ------ API認證
      function GetAuthorizationHeader() {
      const parameter = {
      grant_type: "client_credentials",
      client_id: "XXXXXXXXXX-XXXXXXXX-XXXX-XXXX",
      client_secret: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
      };
      let auth_url = "https://tdx.transportdata.tw/auth/realms/TDXConnect/protocol/openid-connect/token";
      axios({
      method: 'post',
      url: auth_url,
      dataType:'JSON',
      data: parameter,
      headers: {
      'content-type': 'application/x-www-form-urlencoded',
      },
      })
      .then((res) => {
      tokenCode = res.data.access_token;
      GetApiResponse();
      GetApiResponse2();
      })
      .catch((err) => {
      console.log(err);
      })
      }
    5. 調整後程式碼 ( CodePen ), 使用 axios 全域預設配置 ( 建議用此 )

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      // ------ 初始化
      function init() {
      GetAuthorizationHeader();
      }
      init();

      // ------ 呼叫 API 服務取得資料
      function GetApiResponse() {
      const url = `https://tdx.transportdata.tw/api/basic/v2/Tourism/ScenicSpot/Keelung?%24top=33&%24format=JSON`;
      axios.get(url)
      .then((res) => {
      console.log(res.data);
      })
      .catch((err) => {
      console.log(err);
      })
      }
      function GetApiResponse2() {
      const url = `https://tdx.transportdata.tw/api/basic/v2/Bike/Availability/City/NewTaipei?%24top=24&%24format=JSON`;
      axios.get(url)
      .then((res) => {
      console.log(res);
      })
      .catch((err) => {
      console.log(err);
      })
      }

      // ------ API認證
      function GetAuthorizationHeader() {
      const parameter = {
      grant_type: "client_credentials",
      client_id: "XXXXXXXXXX-XXXXXXXX-XXXX-XXXX",
      client_secret: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
      };
      let auth_url = "https://tdx.transportdata.tw/auth/realms/TDXConnect/protocol/openid-connect/token";
      axios({
      method: 'post',
      url: auth_url,
      dataType:'JSON',
      data: parameter,
      headers: {
      'content-type': 'application/x-www-form-urlencoded',
      },
      })
      .then((res) => {
      axios.defaults.headers.common['Authorization'] = `Bearer ${res.data.access_token}`;
      GetApiResponse();
      GetApiResponse2();
      })
      .catch((err) => {
      console.log(err);
      })
      }

API 呼叫次數限制

  1. 若不使用 API 金鑰呼叫 API,則僅能透過瀏覽器呼叫 API,且每個呼叫來源端IP的上限為每日 50次。
  2. 使用 API 金鑰呼叫 API ( 上方API 認證機制操作步驟方式 ),每個呼叫來源端IP呼叫次數限制為 50 次 / 秒 ( 無每日上限 )。

自我梳理

  1. 方式一,如果要取多支 API ,就需要像 GetApiResponse();GetApiResponse2(); 一樣都帶入 GetAuthorizationHeader() 中等伺服器回傳 tokenCode 資料,tokenCode 資料一一帶入 GetApiResponse();GetApiResponse2(); 的 headers 中做驗證,驗證成功就可以會員方式呼叫 TDX API。
  2. 方式二,如果要取多支 API ,GetApiResponse();GetApiResponse2(); 不需要再 headers 中做驗證,直接在 GetAuthorizationHeader() 使用 axios 全域預設配置取得 token ,再把 GetApiResponse();GetApiResponse2(); 帶入 GetAuthorizationHeader()
  3. 向 TDX 發出請求,每發出一次可在會員專區 / 服務呼叫統計中的 “ 當月呼叫次數 “ 看到 ( 一般會員每月上限好像是兩萬次 )。

查看是否順利取得 token 並以會員方式存取 API

可看 Responses Headers 中「 RateLimit-Limit 與 RateLimit-Remaining 」

成功狀態

開啟開發者工具的 Network 重新整理頁面,點擊左方 JSON 檔,查看右方 Responses Headers 中 「 RateLimit-Limit 與 RateLimit-Remaining 」,因會員 IP 呼叫次數限制為 50 次/秒,所以正常每呼叫一支 API 應該為 「RateLimit-Limit: 50,RateLimit-Remaining: 49 」。

開發者工具的 Network ▲

成功 → 每打一支 API ,RateLimit-Remaining 會扣 1,但不會持續下降 ▲

每打不同 API ,Request Headers 的 authorization 會再重新顯示一次 Access Token ▲

失敗狀態

如果取 token 失敗無法以會員方式存取 API ( Token 帶入 HTTP Header 失敗 ),就會遇到每打一次不同 API, RateLimit-Remaining 的次數就會瘋狂下降,直到 0 然後 Console 出現 **429 Too Many Requests**。

失敗 → 每打一次不同 API, RateLimit-Remaining 的次數就會持續下降 ▲

關於 Rate Limits

Rate Limits 主要功能是防止request 過量打爆服務,當同一來源的 request 到達特定頻率時,就會直接從 Istio Envoy 這邊直接 return http status 429 Too Many Requests,後面的服務不會持續被request 到。( 出處 )

參考資訊