[練習課程] jQuery 基礎實作(二):OpenData API 串接
在之前的 Ajax 相關練習當中,不論是讀取 JSON 文件或是向後端伺服器請求,我們都是內部進行相對連結且安全。然而常常聽到 API 串接的技術,是指透過外部網路連結到別人站台的 JSON 文件(或其他格式),透過 Ajax 方式載入數據到你的網頁設計形成活絡的動態資訊網頁,也就是數據別人都定期更新幫你準備好了,你儘管去拿。
前端 AJAX 跨域做資料回應並不合法
然而在瀏覽器規範裡不允許前端技術的 JavaScript 能連接到非相同網域的 Ajax 資料請求(安全性問題),因此在使用跨網域的 API 就延伸了無法取得之窘境。目前使用外部 API 有三種解決方式:
- 使用後端伺服器 PHP 語言的 crul 進行遠端 API 串接(或其他後端伺服器),再讓同網域的前端 JavaScript 進行 Ajax 取得
- 遠端 API 端採用 JSONP 格式,透過請求夾帶遠端所指定之參數(或代號),再透過
<script src="xxx&callback=yyy"></script>
方式偷渡資料帶回前端,使得一開始 JavaScript 就擁有此物件變數而不是由 JavaScript 作跨網域資料請求 - 遠端 API 伺服器使用 W3C 所指定之 CORS 格式,使得此 API 本身受允許被共享資源(遊覽器需支援 CORS)
因此,通常如果你嘗試進行跨網域 API 串接失敗時做以下確認:
- 檢查語法是否有錯,替換曾成功之 URL 是否有正常取得資料 JSON
- 可能對方 API 並沒有設定 CROS 開放,檢查對方 API 是否採用 JSONP 做開放連接
- 如果是 JSONP 格式就參考對方文件要求,配合所需要的參數做
<script url>
。對方會用 js 夾帶 callback 方式回傳包好你需要的資源。使用教學請自行參閱網路文章 - 如果都沒有代表對方很懶沒特別處理,當你無法要求遠程後端 API 做開放跨域設定時,就使用後端對後端做 API 串接(不會受遊覽器阻擋),透過自己的後端讓前端盡情 Ajax(同網域)。
素材準備:使用中央氣象局提供的天氣 API 做為練習環境
我們將開始教學 OpenData 串接,在那之前請先到 氣象開放資料平臺 註冊帳號。接下來會利用 2 種範例作為天氣練習使用。
該平台提供透過 URL 下載檔案以及 RESTful API,簡單來說屬於前端 JS 可以直接存取的 API。唯獨需要有效會員之授權碼,避免流量異常被停權請不要輕易提供他人共用。
範例:氣象開放資料平臺 + Table
我們採用以下服務 api 為練習
一般天氣預報-今明 36 小時天氣預報:https://opendata.cwb.gov.tw/dataset/statisticDays/F-C0032-001
透過 postman 做 api 解讀
Postman 為第三方軟體,是一個可以模擬 HTTP Request 的工具,其中包含常見的 HTTP 的請求方式,例如: GET 、POST、PUT、DELETE,而它的主要功能就是能夠快速的測試你的 API 是否能夠正常的請求資料,並得到正確的請求結果。
這裡僅簡單使用,完整應用方式請參考 網路教學。確認你的 api 能正常讀取資源後,便可以開始規劃前端 Ajax,這裡使用 jQuery 來應用運作。
編寫 Ajax 取得 JSON 數據
這裡示範完整的寫法,你至少需提供目標位置、請求方式、資料類型、callback 行為。
建立 weather_table.html,引用 jQuery 後,先在空白底處執行 script
var data;
$.ajax({
/*本代碼已拔除授權代碼將無效化,請使用自身帳號授權碼*/
url: "https://opendata.cwb.gov.tw/fileapi/v1/opendataapi/F-C0032-001?Authorization=CWB-____&downloadType=WEB&format=JSON",
method: "GET",
dataType: "json",
success: function (re) {
// console.log(re); //先測試拿到了什麼
// data=re; //透過 chrome.f12.console 輸入 data 確認內容
data=re.cwbopendata.dataset.location; //簡化擷取的節點
}
})而較新的 jQuery 版本有支援 getJSON 之便利方式,你可以改用以下寫法:
getJSON 只要 URL 即可,同時可增加 done 成功與 fail 失敗分別執行不同動作增加應用姓。
/*本代碼已拔除授權代碼將無效化,請使用自身帳號授權碼*/
$.getJSON('https://opendata.cwb.gov.tw/fileapi/v1/opendataapi/F-C0032-001?Authorization=CWB-____&downloadType=WEB&format=JSON')
.done(function(re){
data=re.cwbopendata.dataset.location;
})
.fail(function(w){
alert("faill openapi,"+w)
});以上為使用 Ajax 編寫的最低完整限度,當然此範例你可以使用
$.get
來使用,但回傳資料只是一坨 JSON 字串,你還要手動轉成 JSON 物件。$.get('https://apiservice.mol.gov.tw/OdService/download/A17010000J-000103-EvF',function(e){
console.log(e);
console.log(JSON.parse(e));
});接下來就是等待 Ajax 的 callback 做後續動作,如果網頁只有一個 Ajax 就放在該 callback 內
反之如果你需要等待兩個以上 Ajax,你應該利用$.when().then() 來滿足等待事件
$.when(
$.getJSON(url_1).done(function (re) {
data1 = re;
}),
$.getJSON(url_2).done(function (re) {
data2 = re;
}))
.then(function () {
console.log(data1,data2);
});
規劃 TABLE 版型並塞入值
- 我們利用 table 來作為資料顯示
weather_table.html <h1>36H 天氣預報</h1>
<table id="dt" class="display compact hover">
<thead>
<tr>
<th>編號</th>
<th>地區</th>
<th>時間 1</th>
<th>時間 2</th>
<th>時間 3</th>
</tr>
</thead>
<tbody>
</tbody>
</table> - 接著透過 JS 開始做 DOM 塞入,我們在
.done()
這裡補完後續動作。 - 這裡會取得
<td>
三組資料,且分別含有天況、圖示、低溫、高溫。.done(function (re) {
data = re.cwbopendata.dataset.location;
for (let i = 0; i < data.length; i++) {
row = data[i];
print += `
<tr>
<td>${i + 1}</td>
<td>${row.locationName}</td>
<td>${row.weatherElement[0].time[0].parameter.parameterName} <img src="img/night/${row.weatherElement[0].time[0].parameter.parameterValue}.svg"> |
溫度 ${row.weatherElement[2].time[0].parameter.parameterName} ~ ${row.weatherElement[1].time[0].parameter.parameterName} ℃</td>
<td>${row.weatherElement[0].time[1].parameter.parameterName} <img src="img/day/${row.weatherElement[0].time[1].parameter.parameterValue}.svg"> |
溫度 ${row.weatherElement[2].time[1].parameter.parameterName} ~ ${row.weatherElement[1].time[1].parameter.parameterName} ℃</td>
<td>${row.weatherElement[0].time[2].parameter.parameterName} <img src="img/night/${row.weatherElement[0].time[2].parameter.parameterValue}.svg"> |
溫度 ${row.weatherElement[2].time[2].parameter.parameterName} ~ ${row.weatherElement[1].time[2].parameter.parameterName} ℃</td>
</tr>
`;
}
$('tbody').html(print);
}) - 如果希望時間 ISO 轉成我們能控制的 JS 時間,可以這樣規劃
var hourtext = new Array();
hourtext[0] = "凌晨";
hourtext[6] = "白天";
hourtext[12] = "下午";
hourtext[18] = "晚上";
date1 = new Date(data[0].weatherElement[0].time[0].startTime); //轉 date format
date2 = new Date(data[0].weatherElement[0].time[1].startTime); //轉 date format
date3 = new Date(data[0].weatherElement[0].time[2].startTime); //轉 date format
$('#dt>thead').find('th').eq(2).text(`${date1.getFullYear()}-${date1.getMonth() + 1}-${date1.getDate()} ${hourtext[date1.getHours()]}`);
$('#dt>thead').find('th').eq(3).text(`${date2.getFullYear()}-${date2.getMonth() + 1}-${date2.getDate()} ${hourtext[date2.getHours()]}`);
$('#dt>thead').find('th').eq(4).text(`${date3.getFullYear()}-${date3.getMonth() + 1}-${date3.getDate()} ${hourtext[date3.getHours()]}`);
DataTables.JS 套件
本範例將接著使用美化表格的視覺套件做練習。這裡會示範 UI 套用原 DataTables 與 Bootstrap 兩種效果與簡單應用,更多參數設定請自行參考官方文件。
素材準備:使用 DataTable.js 套件
DataTable.js 是一個視覺優化的 JS 套件,能將標準 HTML’s Table 代碼進行視覺與功能優化。你可以輕易地動態進行表格操作。提供各種參數,支援各種不同 UI Framework 環境使用 (Boostrap、Jquery UI),並對應不同的 JS 環境。
Base Style
透過左側連結
Downolad
進行下載前的選項設定step1:選擇 UI 框架為
DataTables
,這裡我們只用最基本的 CSS 樣式step2:選擇套件包為
DataTables
,這裡我們需要使用該 JS 外掛工具即可,而 jQuery 我們已經有了就不需要麻煩了Extensions 主要是提供一些進階功能(整合其他 JS 外掛),這裡我們不討論不選取
step3:選擇提供方式採 CDN,直接連線選擇 我們的基本 DataTables
得到 CDN 後,放置到你的 head 內並排序一下你的 jQuery 位置
<head>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/dt/dt-1.10.18/datatables.min.css" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/v/dt/dt-1.10.18/datatables.min.js"></script>
<style>
img {
height: 1em;
}
</style>
</head>接著設定並觸發 DTS,給予 table 標籤 ID,如果需要和效果就用指定 class 編入(依官網 Example 說明)
<table id="dt" class="display compact hover">
在 JS 內透過 jQuery 去觸發 DTS 效果,建議在 JS 最後執行,也就是 DOM 沒有需再異動後
$('#dt').DataTable({}); //啟用 datatable 套件
畫面效果如下
Bootstrap Style
- 透過左側或主選單連結
Downolad
進行下載前的選項設定 - step1:選擇 UI 外觀框架 Bootstrap,這裡我們引用 BS 的 CSS 樣式
- step2:選擇 DataTables+Bootstrap,我們還沒有 BS,這裡幫忙提供給我們工具包
- Extensions 主要是提供一些進階功能(整合其他 JS 外掛),這裡我們不討論不選取
- step3:選擇 CDN 並拿掉 Concatenate 項目(不要合併),可以清楚看到除了 DTS 還有 BS 也都有了
- 從下面代碼可以看到兩者差異
<head>
<meta charset="UTF-8">
<!--DTS base_Style -->
<!-- <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/dt/dt-1.10.18/datatables.min.css" /> -->
<!--DTS BS_Style -->
<link rel="stylesheet" type="text/css"
href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.1/css/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.18/css/dataTables.bootstrap4.min.css" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<!--DTS base_JS -->
<!-- <script type="text/javascript" src="https://cdn.datatables.net/v/dt/dt-1.10.18/datatables.min.js"></script> -->
<!--DTS BS_JS -->
<script type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/1.10.18/js/jquery.dataTables.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/1.10.18/js/dataTables.bootstrap4.min.js"></script>
<style>
img {
height: 1em;
}
</style>
</head> - 再來就是將你的 table 根據 BS 的設定做效果調整,舉例
<table id="dt" class="display compact table table-hover table-striped table-dark">
- 效果如下
範例:氣象開放資料平臺 + Chart
同個氣象開放資料平台我們換一個範例,找一個資料較多的 API。我們採用以下服務 api 為練習
鄉鎮天氣預報-台灣未來 1 週天氣預報:https://opendata.cwb.gov.tw/dataset/statisticDays/F-D0047-091
編寫 Ajax 取得 JSON 數據
建立 weather_chart.html,引用 jQuery 後,先在空白底處執行 script。我們先請求 OpenData 並試著 console.log 出來看看
/*本代碼已拔除授權代碼將無效化,請使用自身帳號授權碼*/ |
這裡我們將會設計為:
- 抽取出北部地區四個城市分別為:台北、新北、基隆、桃園
- 設計圖形化曲線圖,api 給的時間項目為我們的 x 軸
- 該時間的平均溫度為我們的 y 軸
ChartJS 套件
本範例將接著使用能將數據做成統計表的視覺套件做練習,只需要指定好所需物件陣列指定就能快速拉出圖表。本篇只介紹一種圖表方式其餘起自行參考官方文件。
素材準備:使用 DataTable.js 套件
Chart.js 是一個專門處理圖表化資料的 JS 外掛,他主要是依賴 canvas 作為繪圖技巧,並提供多種不同的統計圖樣式與形式提供使用。
學習官網的 bar 範例
- 透過左側連結
Installation
進行取得(這裡演示 CDN 取法) - 轉到 CDN 網址,取得 css 跟 js 兩者即可。放置到你的 head 內並排序一下你的 jQuery 位置
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.css" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js"></script>
</head> - 接著設定並觸發,參考範例說明在指定處規畫你的 HTML 版面添加具備 ID 的 canvas 元素。
<body>
<h1>北部地區一周天氣溫度</h1>
<div style="width:90%">
<canvas id="myChart" width="800" height="400"></canvas>
</div>
</body> - 複製範例上的簡單資料圖表,使用 JavaScript 編寫
var ctx = document.getElementById('myChart');
var myChart = new Chart(ctx, {
type: 'bar', //圖表類型
data: { //圖表資料
//x 軸之欄位名稱
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
//資料線細節,結構為 datasets:[{...},{...}] 如果有多筆
datasets: [{
//資料線標題名稱
label: '# of Votes',
//值
data: [12, 19, 3, 5, 2, 3],
//多顏色對應不同 x 欄位
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)'
],
//同上
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [{
ticks: {
//是否 y 軸要從 0 開始,如果不要則整段 option 其實可不寫
beginAtZero: true
}
}]
}
}
}); - 畫面效果如下
整合 API 資料並規劃 chartjs
請自行完成動作到已透過 Ajax 取得數據完畢,而以下程式碼都在.done(function(re){})
內執行
- 簡化我們的資料,使找到四個城市資料快些(少打些字)
/*簡化擷取的節點*/
let kl = re.cwbopendata.dataset.locations.location[13]; //基隆
let tp = re.cwbopendata.dataset.locations.location[16]; //台北
let nt = re.cwbopendata.dataset.locations.location[18]; //新北
let tu = re.cwbopendata.dataset.locations.location[21]; //桃園
// console.log(kl); - 整理一些新陣列變數,分別為存有 x 軸之內容,以及各城市的溫度之內容。
/*變數初始化*/
let date_line = new Array();
let k = new Array(), t = new Array(), n = new Array(), u = new Array();
for (let i = 0; i < kl.weatherElement[1].time.length; i++) {
/*write 時間軸*/
date_line[i] = kl.weatherElement[1].time[i].startTime.substr(0, kl.weatherElement[1].time[i].startTime.length - 12);
/*write 溫度資料*/
k.push(kl.weatherElement[1].time[i].elementValue.value);
t.push(tp.weatherElement[1].time[i].elementValue.value);
n.push(nt.weatherElement[1].time[i].elementValue.value);
u.push(tu.weatherElement[1].time[i].elementValue.value);
} - 引用 chart.js 的參數設定
var ctx = document.getElementById('myChart');
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: date_line,
datasets: [{
//keelung
label: kl.locationName,
data: k,
backgroundColor: 'rgba(255, 99, 132, 0.1)',
borderColor: 'rgba(255, 99, 132, 1)',
borderWidth: 1
}, {
//taipei
label: tp.locationName,
data: t,
backgroundColor: 'rgba(54, 162, 235, 0.1)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 1
}, {
//newtaipei
label: nt.locationName,
data: n,
backgroundColor: 'rgba(255, 206, 86, 0.1)',
borderColor: 'rgba(255, 206, 86, 1)',
borderWidth: 1
}, {
//taouang
label: tu.locationName,
data: u,
backgroundColor: 'rgba(75, 192, 192, 0.1)',
borderColor: 'rgba(75, 192, 192, 1)',
}]
}
}); - 畫面如下
範例:新北市政府資料開放平台 (CROS 禁止連線)
該平台為新北市政府的開放資料平台,而這些 OpenData 並不允許前端共享連接使用,這裡將示範如何改由後端伺服器 php 語言取得後再進行轉交給同網域之 JS。
我們採用以下服務 api 為練習
**新北市公共自行車租賃系統 (YouBike)**:http://data.ntpc.gov.tw/od/detail?oid=71CD1490-A2DF-4198-BEF1-318479775E8A
透過 Ajax 取得 api 失敗時
- 先用 postman 確認是否可測試 api 正常運作。
- 這裡示範 getJSON 用法取得,建立檔案 ntpc_curl.html此時能確認不允許前端 JS 直接共享請求,我們改用 php 來完成 api 串接,建立 ntpc_curl_api.php
$.getJSON('http://data.ntpc.gov.tw/od/data/api/54DDDC93-589C-4858-9C95-18B2046CC1FC?$format=json')
.done(function (re) {
console.log(re);
})
.fail(function (w) {
alert("faill openapi")
console.log(w);
});
透過 php 的 curl 取得 api
- 使用 curl 來達到 api 連接,參數說明如註解
// 初始化 CURL
$curl = curl_init();
// 識別發出請求的軟體類型或版本號、該軟體使用的作業系統、還有軟體開發者的字詞串。
// 參考 https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Headers/User-Agent
curl_setopt($curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36");
//要驗證伺服器 SSL 憑證,當拜訪 https 網站時,若未做任何 SSL 相關設定,會出現錯誤。
// 設為 false 為可以接受任何伺服器憑證。
// 參考 https://www.plurk.com/p/e797gs
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
// 將 curl_exec() 獲取結果以文字串方式返回,而不是直接印出。
// 如果你只是要轉給前端可以不寫此行(預設 false),剛好省下最後的 echo 動作
// 反之為 true 可事後要做 echo 給前端(用 Ajax 來取)或透過 php 輸出 HTML
// curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
// 設定 URL 位置
curl_setopt($curl, CURLOPT_URL, "http://data.ntpc.gov.tw/od/data/api/54DDDC93-589C-4858-9C95-18B2046CC1FC?\$format=json");
// 執行 curl
$result = curl_exec($curl);
// 關閉 curl
curl_close($curl);
// echo $result; - 回到 ntpc_curl.html,改連結到同網域的後端取得。
$.getJSON('ntpc_curl_api.php').done(function (re) { //自己做一個後端,再去跟同網域的後端取得
console.log(re);
}).fail(function (w) {
alert("faill openapi")
console.log(w);
}); - 此時可以正常顯示,整體過程就是:遠端 api => by_crul => php =>by Ajax => js
以上三題之完整代碼: view full code