第三方 API
我們到目前為止介紹的 API 都內建在瀏覽器中,但並非所有 API 都是如此。許多大型網站和服務,如 Google Maps、Twitter、Facebook、PayPal 等,都提供 API,允許開發者利用其資料(例如,在你的部落格上顯示你的 Twitter 流)或服務(例如,使用 Facebook 登入來登入你的使用者)。本文探討了瀏覽器 API 和第三方 API 之間的區別,並展示了一些後者的典型用法。
| 先決條件 | JavaScript 基礎知識(參見 第一步、構建模組、JavaScript 物件),以及 客戶端 API 基礎知識 |
|---|---|
| 目標 | 學習第三方 API 的工作原理,以及如何使用它們來增強你的網站。 |
什麼是第三方 API?
第三方 API 是由第三方(通常是 Facebook、Twitter 或 Google 等公司)提供的 API,允許你透過 JavaScript 訪問其功能並在你的網站上使用它。最明顯的例子之一是使用地圖 API 在你的頁面上顯示自定義地圖。
讓我們看一個 簡單的 Mapquest API 示例,並用它來說明第三方 API 與瀏覽器 API 的區別。
注意:你可能只想 一次獲取我們所有的程式碼示例,在這種情況下,你只需在倉庫中搜索每個部分所需的示例檔案即可。
它們位於第三方伺服器上
瀏覽器 API 內置於瀏覽器中——你可以立即從 JavaScript 訪問它們。例如,我們在 入門文章中看到的 Web Audio API 是使用原生的 AudioContext 物件訪問的。例如
const audioCtx = new AudioContext();
// …
const audioElement = document.querySelector("audio");
// …
const audioSource = audioCtx.createMediaElementSource(audioElement);
// etc.
另一方面,第三方 API 位於第三方伺服器上。要從 JavaScript 訪問它們,你首先需要連線到 API 功能並使其在你的頁面上可用。這通常涉及首先透過 <script> 元素連結到伺服器上提供的 JavaScript 庫,如我們的 Mapquest 示例所示
<script
src="https://api.mqcdn.com/sdk/mapquest-js/v1.3.2/mapquest.js"
defer></script>
<link
rel="stylesheet"
href="https://api.mqcdn.com/sdk/mapquest-js/v1.3.2/mapquest.css" />
然後,你可以開始使用該庫中提供的物件。例如
const map = L.mapquest.map("map", {
center: [53.480759, -2.242631],
layers: L.mapquest.tileLayer("map"),
zoom: 12,
});
這裡我們建立一個變數來儲存地圖資訊,然後使用 mapquest.map() 方法建立一個新地圖,該方法的引數是你想要顯示地圖的 <div> 元素的 ID('map'),以及一個包含我們想要顯示的特定地圖詳細資訊的選項物件。在本例中,我們指定了地圖中心的座標、要顯示的型別為 map 的地圖圖層(使用 mapquest.tileLayer() 方法建立),以及預設縮放級別。
這就是 Mapquest API 繪製簡單地圖所需的所有資訊。你連線到的伺服器處理所有複雜的事情,例如顯示正在顯示區域的正確地圖瓦片等。
注意:一些 API 對其功能的訪問方式略有不同,要求開發者向特定 URL 模式發出 HTTP 請求以檢索資料。這些稱為 RESTful API——我們稍後將展示一個示例。
它們通常需要 API 金鑰
瀏覽器 API 的安全性傾向於透過許可權提示來處理,如 我們在第一篇文章中所討論的。這樣做的目的是讓使用者知道他們訪問的網站發生了什麼,並且不太可能成為某人以惡意方式使用 API 的受害者。
第三方 API 具有略微不同的許可權系統——它們傾向於使用開發者金鑰來允許開發者訪問 API 功能,這更多是為了保護 API 供應商而不是使用者。
你將在 Mapquest API 示例中找到類似以下內容的行
L.mapquest.key = "YOUR-API-KEY-HERE";
此行指定在你的應用程式中使用的 API 或開發者金鑰——應用程式的開發者必須申請獲取金鑰,然後將其包含在他們的程式碼中才能獲得對 API 功能的訪問許可權。在我們的示例中,我們只提供了一個佔位符。
注意:建立你自己的示例時,你將在任何佔位符的位置使用你自己的 API 金鑰。
其他 API 可能要求你以稍微不同的方式包含金鑰,但對於大多數 API 來說,模式都比較相似。
要求金鑰使 API 提供商能夠追究 API 使用者對其行為的責任。當開發者註冊了金鑰後,API 提供商就會知道他們,如果他們開始對 API 做任何惡意的事情(例如跟蹤人們的位置或嘗試用大量請求轟炸 API 以使其停止工作),就可以採取行動。最簡單的行動就是撤銷他們的 API 許可權。
擴充套件 Mapquest 示例
讓我們為 Mapquest 示例新增更多功能,以展示如何使用 API 的其他一些功能。
- 要開始本節,請在新的目錄中複製 mapquest 初始檔案。如果你已經 克隆了示例儲存庫,那麼你已經擁有該檔案的副本,你可以在 javascript/apis/third-party-apis/mapquest/start 目錄中找到它。
- 接下來,你需要訪問 Mapquest 開發者網站,建立一個帳戶,然後建立一個開發者金鑰以用於你的示例。(在撰寫本文時,它在網站上被稱為“消費者金鑰”,金鑰建立過程還要求提供可選的“回撥 URL”。你無需在此處填寫 URL:只需將其留空即可。)
- 開啟你的起始檔案,並將 API 金鑰佔位符替換為你的金鑰。
更改地圖型別
可以使用 Mapquest API 顯示多種不同型別的地圖。為此,請找到以下行
layers: L.mapquest.tileLayer("map");
嘗試將 'map' 更改為 'hybrid' 以顯示混合樣式地圖。也嘗試一些其他值。 tileLayer 參考頁面 顯示了不同的可用選項,以及更多資訊。
新增不同的控制元件
地圖提供了一些不同的控制元件;預設情況下,它只顯示縮放控制元件。你可以使用 map.addControl() 方法擴充套件可用的控制元件;將其新增到你的程式碼中
map.addControl(L.mapquest.control());
mapquest.control() 方法 只建立一個簡單的功能齊全的控制元件集,預設情況下它位於右上角。你可以透過指定一個包含 position 屬性的選項物件作為控制元件的引數來調整位置,該屬性的值是一個字串,指定控制元件的位置。例如,試試這個
map.addControl(L.mapquest.control({ position: "bottomright" }));
還有其他型別的控制元件可用,例如 mapquest.searchControl() 和 mapquest.satelliteControl(),其中一些非常複雜且功能強大。嘗試一下,看看你能想出什麼。
新增自定義標記
在地圖上的某個點新增標記(圖示)很容易——你只需使用 L.marker() 方法(似乎在相關的 Leaflet.js 文件中進行了說明)。將以下程式碼新增到你的示例中,同樣在 window.onload 內部
L.marker([53.480759, -2.242631], {
icon: L.mapquest.icons.marker({
primaryColor: "#22407F",
secondaryColor: "#3B5998",
shadow: true,
size: "md",
symbol: "A",
}),
})
.bindPopup("This is Manchester!")
.addTo(map);
如你所見,這在最簡單的情況下需要兩個引數,一個包含要顯示標記的座標的陣列,以及一個包含定義該點要顯示的圖示的 icon 屬性的選項物件。
圖示是使用 mapquest.icons.marker() 方法定義的,如你所見,它包含標記的顏色和大小等資訊。
在第一個方法呼叫的末尾,我們連結 .bindPopup('This is Manchester!'),它定義了單擊標記時要顯示的內容。
最後,我們在鏈的末尾連結 .addTo(map) 以將標記實際新增到地圖中。
嘗試文件中顯示的其他選項,看看你能想出什麼!Mapquest 提供了一些非常高階的功能,例如路線、搜尋等。
注意:如果示例無法正常工作,請將你的程式碼與我們的 完成版本 進行比較。
RESTful API — NYTimes
現在讓我們看另一個 API 示例——紐約時報 API。此 API 允許你檢索紐約時報新聞報道資訊並在你的網站上顯示。這種型別的 API 被稱為 **RESTful API**——我們不像使用 Mapquest 時那樣使用 JavaScript 庫的功能來獲取資料,而是透過向特定 URL 發出 HTTP 請求來獲取資料,並將搜尋詞和其他屬性編碼在 URL 中(通常作為 URL 引數)。這是你在使用 API 時會遇到的常見模式。
下面我們將帶你完成一個練習,向你展示如何使用 NYTimes API,它還提供了一套更通用的步驟,你可以將其用作處理新 API 的方法。
查詢文件
當你想使用第三方 API 時,必須找出文件在哪裡,以便你可以瞭解 API 具有哪些功能,如何使用它們等。紐約時報 API 文件位於 https://developer.nytimes.com/。
獲取開發者金鑰
出於安全和問責制的原因,大多數 API 都要求你使用某種開發者金鑰。要註冊 NYTimes API 金鑰,請按照 https://developer.nytimes.com/get-started 上的說明進行操作。
- 讓我們為文章搜尋 API 請求一個金鑰——建立一個新應用,選擇此 API 作為你要使用的 API(填寫名稱和描述,將“文章搜尋 API”下的開關切換到開啟位置,然後點選“建立”)。
- 從結果頁面獲取 API 金鑰。
- 現在,要開始示例,請複製 nytimes/start 目錄中的所有檔案。如果你已經 克隆了示例儲存庫,那麼你已經擁有這些檔案的副本,你可以在 javascript/apis/third-party-apis/nytimes/start 目錄中找到它們。最初,
script.js檔案包含示例設定所需的一些變數;下面我們將填寫所需的功能。
該應用最終將允許你輸入搜尋詞和可選的開始和結束日期,然後它將使用這些詞來查詢文章搜尋 API 並顯示搜尋結果。
將 API 連線到你的應用
首先,你需要在 API 和你的應用之間建立連線。在此 API 的情況下,你需要在每次從正確 URL 請求服務資料時都將 API 金鑰作為 get 引數包含在內。
- 找到以下行將現有的 API 金鑰替換為你在上一步中獲取的實際 API 金鑰。js
const key = "INSERT-YOUR-API-KEY-HERE"; - 將以下行新增到你的 JavaScript 中,“
// 事件監聽器控制功能”註釋下方。當提交表單(按下按鈕)時,此行執行名為submitSearch()的函式。jssearchForm.addEventListener("submit", submitSearch); - 現在新增
submitSearch()和fetchResults()函式定義,在上一行下方jsfunction submitSearch(e) { pageNumber = 0; fetchResults(e); } function fetchResults(e) { // Use preventDefault() to stop the form submitting e.preventDefault(); // Assemble the full URL let url = `${baseURL}?api-key=${key}&page=${pageNumber}&q=${searchTerm.value}&fq=document_type:("article")`; if (startDate.value !== "") { url = `${url}&begin_date=${startDate.value}`; } if (endDate.value !== "") { url = `${url}&end_date=${endDate.value}`; } }
submitSearch() 函式首先將頁面編號重置為 0 以開始,然後呼叫 fetchResults() 函式。後者首先對事件物件呼叫 preventDefault(),以阻止表單實際提交(這會破壞示例)。接下來,我們使用一些字串操作來組裝我們將傳送請求的完整 URL。我們首先組裝我們認為此演示必需的部分
- 基本 URL(取自
baseURL變數)。 - API 金鑰,必須在
api-keyURL 引數中指定(值取自key變數)。 - 頁面編號,必須在
pageURL 引數中指定(值取自pageNumber變數)。 - 搜尋詞,必須在
qURL 引數中指定(值取自searchTerm文字<input>的值)。 - 要返回結果的文件型別,如透過
fqURL 引數傳遞的表示式中指定。在本例中,我們希望返回文章。
接下來,我們使用幾個 if () 語句來檢查 startDate 和 endDate 元素是否已填充值。如果已填充,我們將它們的值附加到 URL,分別在 begin_date 和 end_date URL 引數中指定。
因此,完整的 URL 最終看起來像這樣
https:/svc/search/v2/articlesearch.json?api-key=YOUR-API-KEY-HERE&page=0&q=cats&fq=document_type:("article")&begin_date=20170301&end_date=20170312
注意: 您可以在 NYTimes 開發者文件 中找到有關可以包含哪些 URL 引數的更多詳細資訊。
注意: 該示例具有基本的表單資料驗證——必須填寫搜尋詞欄位才能提交表單(使用 required 屬性實現),並且日期欄位已指定 pattern 屬性,這意味著除非它們的值包含 8 個數字(pattern="[0-9]{8}"),否則不會提交。有關這些工作原理的更多詳細資訊,請參閱 表單資料驗證。
從 API 請求資料
現在我們已經構建了 URL,讓我們向其傳送請求。我們將使用 Fetch API 來實現。
在 fetchResults() 函式中,緊靠結束花括號上方新增以下程式碼塊
// Use fetch() to make the request to the API
fetch(url)
.then((response) => response.json())
.then((json) => displayResults(json))
.catch((error) => console.error(`Error fetching data: ${error.message}`));
在這裡,我們透過將 url 變數傳遞給 fetch() 來執行請求,使用 json() 函式將響應主體轉換為 JSON,然後將生成的 JSON 傳遞給 displayResults() 函式,以便可以在我們的 UI 中顯示資料。我們還捕獲並記錄可能丟擲的任何錯誤。
顯示資料
好的,讓我們看看我們將如何顯示資料。在 fetchResults() 函式下方新增以下函式。
function displayResults(json) {
while (section.firstChild) {
section.removeChild(section.firstChild);
}
const articles = json.response.docs;
nav.style.display = articles.length === 10 ? "block" : "none";
if (articles.length === 0) {
const para = document.createElement("p");
para.textContent = "No results returned.";
section.appendChild(para);
} else {
for (const current of articles) {
const article = document.createElement("article");
const heading = document.createElement("h2");
const link = document.createElement("a");
const img = document.createElement("img");
const para1 = document.createElement("p");
const keywordPara = document.createElement("p");
keywordPara.classList.add("keywords");
console.log(current);
link.href = current.web_url;
link.textContent = current.headline.main;
para1.textContent = current.snippet;
keywordPara.textContent = "Keywords: ";
for (const keyword of current.keywords) {
const span = document.createElement("span");
span.textContent = `${keyword.value} `;
keywordPara.appendChild(span);
}
if (current.multimedia.length > 0) {
img.src = `http://www.nytimes.com/${current.multimedia[0].url}`;
img.alt = current.headline.main;
}
article.appendChild(heading);
heading.appendChild(link);
article.appendChild(img);
article.appendChild(para1);
article.appendChild(keywordPara);
section.appendChild(article);
}
}
}
這裡有很多程式碼;讓我們一步一步地解釋它
while迴圈是一種常用的模式,用於刪除 DOM 元素(在本例中為<section>元素)的所有內容。我們不斷檢查<section>是否有第一個子元素,如果有,則刪除第一個子元素。當<section>不再有任何子元素時,迴圈結束。- 接下來,我們將
articles變數設定為等於json.response.docs——這是一個包含所有表示搜尋返回的文章的物件的陣列。這樣做純粹是為了使後面的程式碼更簡單。 - 第一個
if ()塊檢查是否返回了 10 篇文章(API 每次最多返回 10 篇文章)。如果是,則顯示包含“前 10 個”/“後 10 個”分頁按鈕的<nav>。如果返回的文章少於 10 篇,則它們都將適合一頁,因此我們不需要顯示分頁按鈕。我們將在下一節中連線分頁功能。 - 下一個
if ()塊檢查是否沒有返回任何文章。如果是,我們不嘗試顯示任何內容——我們建立一個包含文字“沒有返回結果”的<p>並將其插入<section>中。 - 如果返回了一些文章,我們首先建立所有我們想要用於顯示每個新聞故事的元素,將正確的內容插入到每個元素中,然後將它們插入到 DOM 的適當位置。為了確定文章物件中哪些屬性包含要顯示的正確資料,我們查閱了文章搜尋 API 參考(請參閱 NYTimes API)。大多數這些操作都相當明顯,但有一些值得一提
連線分頁按鈕
為了使分頁按鈕正常工作,我們將增加(或減少)pageNumber 變數的值,然後使用新的值包含在頁面 URL 引數中重新執行提取請求。這是因為 NYTimes API 每次只返回 10 個結果——如果有多於 10 個結果可用,如果 page URL 引數設定為 0(或根本不包含——0 是預設值),它將返回前 10 個(0-9),如果設定為 1,則返回接下來的 10 個(10-19),依此類推。
這使我們能夠編寫一個簡單的分頁函式。
- 在現有的
addEventListener()呼叫下方,新增這兩個新的呼叫,當單擊相關按鈕時,這些呼叫會導致nextPage()和previousPage()函式被呼叫jsnextBtn.addEventListener("click", nextPage); previousBtn.addEventListener("click", previousPage); - 在您之前的新增內容下方,讓我們定義這兩個函式——現在新增此程式碼第一個函式增加js
function nextPage(e) { pageNumber++; fetchResults(e); } function previousPage(e) { if (pageNumber > 0) { pageNumber--; } else { return; } fetchResults(e); }pageNumber變數,然後再次執行fetchResults()函式以顯示下一頁的結果。第二個函式以相反的方式工作,幾乎完全相同,但我們還必須額外檢查pageNumber是否尚未為零,然後再遞減它——如果提取請求使用負pageURL 引數執行,可能會導致錯誤。如果pageNumber已經為 0,我們return出函式——如果我們已經在第一頁,則無需再次載入相同的結果。
注意: 您可以在 GitHub 上找到我們的 完成的 NYTimes API 示例程式碼(也可以 在這裡檢視其執行情況)。
YouTube 示例
我們還為您構建了另一個示例供您學習和參考——請參閱我們的 YouTube 影片搜尋示例。它使用了兩個相關的 API
- YouTube 資料 API 用於搜尋 YouTube 影片並返回結果。
- YouTube IFrame 播放器 API 用於在 IFrame 影片播放器中顯示返回的影片示例,以便您可以觀看它們。
此示例很有趣,因為它展示了兩個相關的第三方 API 如何一起用於構建應用程式。第一個是 RESTful API,而第二個更像 Mapquest(具有 API 特定的方法等)。但是,值得注意的是,這兩個 API 都需要將 JavaScript 庫應用到頁面上。RESTful API 提供了可用於處理發出 HTTP 請求和返回結果的函式。
我們不會在本文中過多討論此示例——原始碼 中已插入詳細的註釋以解釋其工作原理。
要使其執行,您需要
- 閱讀 YouTube 資料 API 概述 文件。
- 確保您訪問了 已啟用 API 頁面,並在 API 列表中,確保 YouTube 資料 API v3 的狀態為“已啟用”。
- 從 Google Cloud 獲取 API 金鑰。
- 在原始碼中找到字串
ENTER-API-KEY-HERE,並將其替換為您的 API 金鑰。 - 透過 Web 伺服器執行示例。如果您只是在瀏覽器中直接執行它(即透過
file://URL),則它將無法工作。