第三方 API

您正在阅读此内容的英文版本,因为该语系尚未翻译。 帮助我们翻译此文章吧!

到目前为止我们已经介绍的API是内置在浏览器中的,但并不是所有的API都是。许多大型网站和服务(例如Google地图,Twitter,Facebook,PayPal等)提供的API允许开发者使用他们的数据(例如在博客上显示您的Twitter流)或服务(例如在您的网站上显示自定义Google地图,或者使用Facebook登录来登录你的用户)。本文着眼于浏览器API和第三方API的区别,并展示了后者的一些典型用途。

先决条件: JavaScript 基础知识 (see first steps, building blocks, JavaScript objects), the basics of Client-side APIs
目的:  学习了解第三方API的运作方式,以及如何运用它们来提高你的网站性能

什么是第三方API?

第三方API是由第三方(通常是Facebook,Twitter或Google等公司)提供的API,允许您通过JavaScript访问其功能,并在您自己的站点上使用它。 正如我们在 API介绍章节 所展示的, 最显著的例子就是运用 Google Maps APIs 在你的网页上展示自定义地图。

让我们再来瞧一眼这个地图的例子 (see the source code on GitHub; see it live also), 从这里可以知道第三方API和浏览器API的区别是怎么样的。

Note: 您可能想要一次获得所有的代码示例,在这种情况下,您可以搜索repo来获取每个部分中需要的示例文件。

它们植根于第三方服务器

浏览器API在浏览器构建之初就存在 — 用JavaScript就可以立即访问它们。例如, 例子中所使用的 Geolocation API 就是通过使用 Navigator 对象的属性 geolocation 来访问的, 它返回一个名为 Geolocation 的对象。 这个例子使用了这个对象的方法 getCurrentPosition() 来请求当前设备所处的地点:

navigator.geolocation.getCurrentPosition(function(position) { ... });

第三方API,从某种角度讲,植根于第三方服务器上。要通过 JavaScript获取它们,您首先需要链接到其功能接口上并使其在您的页面上生效。通常来说,这首先需要您通过一个 <script> 元素连接到第三方服务器所开放的JavaScript库,如下所示:

<script type="text/javascript" src="https://maps.google.com/maps/api/js?key=AIzaSyDDuGt0E5IEGkcE6ZfrKfUtE9Ko_de66pA"></script>

然后您便可使用该库中可用的对象了,如:

var latlng = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);
var myOptions = {
  zoom: 8,
  center: latlng,
  mapTypeId: google.maps.MapTypeId.TERRAIN,
  disableDefaultUI: true
}

var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);

代码中我们用 google.maps.LatLng() 构造器创建了一个新的 LatLng 对象,它包含了我们想展示的地址的纬度和经度,作为一个Geolocation API返回。然后,我们创建了包含这个对象,和其他有关地图显示信息的选项对象(myOptions) 。最后,用 google.maps.Map() 构造器创建了map对象,它接受网页元素(地图展示处)和选项对象两个参数。

以上就是用 Google Maps API 建立一个简单地图所需要的所有信息。所有复杂的工作都全由你所连接的第三方服务器处理,包括展示正确地理位置的地图块,等等。

Note: 一些api处理对其功能的访问略有不同,相反,它们要求开发人员向特定的URL模式发出HTTP请求(参见从服务器获取数据),以检索特定的数据。这些被称为RESTful api,稍后我们将在文章中展示这个示例。

权限的不同处理方式

正如我们在第一篇文章中所讨论的,浏览器api的安全性通常是通过权限提示。这样做的目的是为了让用户知道他们访问的网站上发生了什么,并且不太可能成为恶意使用API的人的受害者。

第三方API有一个稍微不同的权限系统——它们倾向于使用关键代码来允许开发人员访问API功能。再次查看我们链接到的谷歌地图API库的URL:

https://maps.google.com/maps/api/js?key=AIzaSyDDuGt0E5IEGkcE6ZfrKfUtE9Ko_de66pA

URL末尾提供的URL参数是一个开发人员密钥—应用程序的开发人员必须应用它来获取一个密钥,然后以一种特定的方式将其包含在代码中,才能允许访问API的功能。对于谷歌映射(以及其他谷歌API),可以在谷歌云平台上申请一个密钥。

其他API的包含方式稍微不同,但是大多数API的模式都非常相似。

需要密钥的原因是:所用使用API功能的人都需要承担责任。当开发者注册一个密钥之后,如果他们开始恶意使用API(例如跟踪位置,试图用大量的垃圾邮件干扰用户正常工作),此时API的提供者可以采取措施。最简单的措施是撤销开发者的API的使用权。

扩展the Google Maps示例

现在我们已经检验了Google Maps API示例以及它的运作方式,让我们添加一些更多的功能来展示如何使用API的其他特性。

  1. 要开始这个部分, 确保你已经在一个新的目录复制 Google Maps启动文件。 如果你已经 克隆了示例存储库,那么你已经拥有了一个这个文件的拷贝,你可以在javascript/apis/third-party-apis/google-maps目录中找到该文件。

  2. 接下来,用以下步骤获取你自己的开发者密钥:

    1. 跳转到 Google Cloud Platform API Manager dashboard.
    2. 如果你还没有新项目,请创建一个新项目。
    3. 单击“启用 API”按钮。
    4. 选择Google Maps JavaScript API.
    5. 单击“启用”按钮。
    6. 单击创建凭据,然后选择API密钥。
    7. 复制你的API密钥并将示例中的第一个<script>元素中的现有密钥替换为你自己的密钥。(位于?key=和属性结束引号标记 (")之间的位置。)

    注意:获取Google相关API密钥可能会有一点困难——Google Cloud Platform API Manager有许多不同的屏幕 ,并且工作流程可能因您是否设置账户而变得细微的不同。如果您在执行此步骤时遇到了困难,我们将很乐意为您提供帮助——联系我们

  3. 打开你的Google Maps起始文件,找到INSERT-YOUR-API-KEY-HERE字符串,然后将其替换为你从Google Cloud Platform API Manager dashboard获取的实际API密钥。

添加自定义标记

添加一个标记在地图上(icon)在某种程度上是很容易的,你只需要创建一个新的标记使用google.maps.Marker()构造函数,传递给它一个包含位置显示标记的选择对象(如LatLng对象),和Map对象来显示它。

  1. var map ... 行下面添加下列代码:

    var marker = new google.maps.Marker({
      position: latlng,
      map: map
    });

    现在如果你刷新你的页面, 你会看到地图中间弹出了一个小小的漂亮标记。这很酷, 但是这并不是一个定制的标记图标 — 它使用了默认的标记图标。

  2. 如果要使用定制化的图标,我们需要在创建标记时通过URL来明确说明。首先,在刚才添加的代码块之后添加下面的代码:

    var iconBase = 'https://maps.google.com/mapfiles/kml/shapes/';

    这定义了所有Google Maps官方图标存储的URL(如果你想的话你也可以使用你自己的图标存储位置)。

  3. 图标的位置应当在选项对象的icon 属性中说明。更新Constructor并添加icon属性,如下:

    var marker = new google.maps.Marker({
      position: latlng,
      icon: iconBase + 'flag_maps.png',
      map: map
    });

    这里我们用iconBase 加上图标的文件名,从而创建完整URL的方式阐明了icon属性。现在尝试重新加载你的例子,你会看到你的地图上显示了一个定制的标记!

Note: 访问 Customizing a Google Map: Custom Markers 以查看更多信息。

Note: 访问 Map marker or Icon names 以找出还有哪些可以的图标,并查看它们的引用名称是什么。它们的文件名应当是当你点击它们时显示出的图标名,最后带有".png"。

单击标记时显示弹出窗口

Google地图的另一个常见用例是在点击其名称或标记时显示有关某个地点的更多信息(弹出式广告在Google Maps API中称为信息窗口)。 这也很容易实现,所以让我们来看看它。

  1. 首先,您需要指定一个包含HTML的JavaScript字符串,该字符串将定义弹出窗口的内容。 这将由API注入弹出窗口,并且可以包含您想要的任何内容。 google.maps.Marker()构造函数定义下面添加以下行:

    var contentString = '<div id="content"><h2 id="firstHeading" class="firstHeading">Custom info window</h2><p>This is a cool custom info window.</p></div>';
  2. 然后,你需要使用google.maps.InfoWindow() 构造器,创建一个新的info window object。在之前的代码下面,添加以下代码:

    var infowindow = new google.maps.InfoWindow({
      content: contentString
    });

    还有其他可用的属性 (查看 Info Windows), 但是在这里我们只具体说明指向内容源的content 属性。

  3. 最后,为了在单击标记(marker)时显示弹出窗口,我们使用了一个简单的点击事件处理器。在google.maps.InfoWindow()构造器代码下面,添加以下代码:

    marker.addListener('click', function() {
      infowindow.open(map, marker);
    });

    在函数中,我们只需调用 infowindow 的 open() 函数,该函数将要显示它的地图和希望它显示在旁边的标记作为参数。

  4. 现在尝试重新加载示例,然后单击标记!

Controlling what map controls are displayed

在原始 google.maps.Map()构造函数中,将看到 disableDefaultUI: true 。这将禁用您通常在 Google 地图上获得的所有标准 UI 控件。

  1. 将其值设置为 false (或完全删除此属性),重新加载示例,将看到地图缩放按钮、 scale indicator等等。

  2. 现在撤销上一次更改。

  3. 通过使用指定单个 UI 功能的其他属性,可以更精细地显示或隐藏控件。尝试在 disableDefaultUI: true 的下面添加代码(请记住在 disableDefaultUI: true 之后输入逗号,否则将收到错误):

    zoomControl: true,
    mapTypeControl: true,
    scaleControl: true,
  4. 现在尝试重新加载示例以查看这些属性的效果。您可以在 MapOptions object reference page找到更多属性。

就是现在 - 看看 Google Maps APIs documentation,发现更多乐趣!

一个RESTful API — NYTimes

现在让我们看看另一个API示例 - New York Times API 此API允许您检索纽约时报的新闻故事信息并将其显示在您的网站上。 这种类型的API称为RESTful API - 我们不像使用Google地图那样使用JavaScript库的功能获取数据,而是通过向特定网址发出HTTP请求来获取数据,其中包含搜索术语和其他属性编码的数据 URL(通常作为URL参数)。 这是您在API中遇到的常见模式。

使用第三方API的方法

下面我们将带您完成练习,向您展示如何使用NYTimes API,它还提供了一组更为通用的步骤,您可以将其用作处理新API的方法。

查找文档

当您想要使用第三方API时,必须找出文档的位置,以便了解API具有哪些功能,以及如何使用它们等等。NYTimes API文档位于 https://developer.nytimes.com/

获取一个开发者密钥

出于安全性和问责制的原因,大多数API都要求您使用某种开发人员密钥。 要注册NYTimes API密钥,您需要访问 https://developer.nytimes.com/signup

  1. Let's request a key for the "Article Search API" — fill in the form, selecting this as the API you want to use.

  2. Next, wait a few minutes, then get the key from your email.

  3. Now, to start the example off, make copies of nytimes_start.html and nytimes.css in a new directory on your computer. If you've already cloned the examples repository, you'll already have a copy of these files, which you can find in the javascript/apis/third-party-apis/nytimes directory. Initially the <script> element contains a number of variables needed for the setup of the example; below we'll fill in the required functionality.

The app will end up allowing you to type in a search term and optional start and end dates, which it will then use to query the Article Search API and display the search results.

将API连接到你的应用

First, you'll need to make a connection between the API, and your app. This is usually done either by connecting to the API's JavaScript (as we did in the Google Maps API), or by making requests to the correct URL(s).

In the case of this API, you need to include the API key as a get parameter every time you request data from it.

  1. Find the following line:

    var key = 'INSERT-YOUR-API-KEY-HERE';

    Replace INSERT-YOUR-API-KEY-HERE with the actual API key you got in the previous section.

  2. Add the following line to your JavaScript, below the "// Event listeners to control the functionality" comment. This runs a function called fetchResults() when the form is submitted (the button is pressed).

    searchForm.addEventListener('submit', fetchResults);
  3. Now add the fetchResults() function definition, below the previous line:

    function fetchResults(e) {
      // Use preventDefault() to stop the form submitting
      e.preventDefault();
    
      // Assemble the full URL
      url = baseURL + '?api-key=' + key + '&page=' + pageNumber + '&q=' + searchTerm.value;
    
      if(startDate.value !== '') {
        url += '&begin_date=' + startDate.value;
      };
    
      if(endDate.value !== '') {
        url += '&end_date=' + endDate.value;
      };
    
    }

This first calls preventDefault() on the event object, to stop the form actually submitting (which would break the example). Next, we use some string manipulation to assemble the full URL that we will make the request to. We start off by assembling the parts we deem as mandatory for this demo:

  • The base URL (taken from the baseURL variable).
  • The API key, which has to be specified in the api-key URL parameter (the value is taken from the key variable).
  • The page number, which has to be specified in the page URL parameter (the value is taken from the pageNumber variable).
  • The search term, which has to be specified in the q URL parameter (the value is taken from the value of the searchTerm text <input>).

Next, we use a couple of if() statements to check whether the startDate and endDate <input>s have had values filled in on them. If they do, we append their values to the URL, specified in begin_date and end_date URL parameters respectively.

So, a complete URL would end up looking something like this:

https://api.nytimes.com/svc/search/v2/articlesearch.json?api-key=4f3c267e125943d79b0a3e679f608a78&page=0&q=cats
&begin_date=20170301&end_date=20170312

Note: You can find more details of what URL parameters can be included at the Article Search API reference.

Note: The example has rudimentary form data validation — the search term field has to be filled in before the form can be submitted (achieved using the required attribute), and the date fields have pattern attributes specified, which means they won't submit unless their values consist of 8 numbers (pattern="[0-9]{8}"). See Form data validation for more details on how these work.

从api请求数据

现在我们已经构造了我们的URL,让我们向它发起请求。我们将通过 Fetch API 完成这项工作。

Add the following code block inside the fetchResults() function, just above the closing curly brace:

// Use fetch() to make the request to the API
fetch(url).then(function(result) {
  return result.json();
}).then(function(json) {
  displayResults(json);
});

Here we run the request by passing our url variable to fetch(), convert the response body to JSON using the json() function, then pass the resulting JSON to the displayResults() function so the data can be displayed in our UI.

Displaying the data

OK, let's look at how we'll display the data. Add the following function below your fetchResults() function.

function displayResults(json) {
  while (section.firstChild) {
      section.removeChild(section.firstChild);
  }

  var articles = json.response.docs;

  if(articles.length === 10) {
    nav.style.display = 'block';
  } else {
    nav.style.display = 'none';
  }

  if(articles.length === 0) {
    var para = document.createElement('p');
    para.textContent = 'No results returned.'
    section.appendChild(para);
  } else {
    for(var i = 0; i < articles.length; i++) {
      var article = document.createElement('article');
      var heading = document.createElement('h2');
      var link = document.createElement('a');
      var img = document.createElement('img');
      var para1 = document.createElement('p');
      var para2 = document.createElement('p');
      var clearfix = document.createElement('div');

      var current = articles[i];
      console.log(current);

      link.href = current.web_url;
      link.textContent = current.headline.main;
      para1.textContent = current.lead_paragraph;
      para2.textContent = 'Keywords: ';
      for(var j = 0; j < current.keywords.length; j++) {
        var span = document.createElement('span');
        span.textContent += current.keywords[j].value + ' ';
        para2.appendChild(span);
      }

      if(current.multimedia.length > 0) {
        img.src = 'http://www.nytimes.com/' + current.multimedia[0].url;
        img.alt = current.headline.main;
      }

      clearfix.setAttribute('class','clearfix');

      article.appendChild(heading);
      heading.appendChild(link);
      article.appendChild(img);
      article.appendChild(para1);
      article.appendChild(para2);
      article.appendChild(clearfix);
      section.appendChild(article);
    }
  }
};

There's a lot of code here; let's explain it step by step:

  • The while loop is a common pattern used to delete all of the contents of a DOM element, in this case the <section> element. We keep checking to see if the <section> has a first child, and if it does, we remove the first child. The loop ends when <section> no longer has any children.
  • Next, we set the articles variable to equal json.response.docs — this is the array holding all the objects that represent the articles returned by the search. This is done purely to make the following code a bit simpler.
  • The first if() block checks to see if 10 articles are returned (the API returns up to 10 articles at a time.) If so, we display the <nav> that contains the Previous 10/Next 10 pagination buttons. If less than 10 articles are returned, they will all fit on one page, so we don't need to show the pagination buttons. We will wire up the pagination functionality in the next section.
  • The next if() block checks to see if no articles are returned. If so, we don't try to display any — we just create a <p> containing the text "No results returned." and insert it into the <section>.
  • If some articles are returned, we first of all create all the elements that we want to use to display each news story, insert the right contents into each one, and then insert them into the DOM at the appropriate places. To work out which properties in the article objects contained the right data to show, we consulted the Article Search API reference. Most of these operations are fairly obvious, but a few are worth calling out:
    • We used a for loop (for(var j = 0; j < current.keywords.length; j++) { ... } ) to loop through all the keywords associated with each article, and insert each one inside its own <span>, inside a <p>. This was done to make it easy to style each one.
    • We used an if() block (if(current.multimedia.length > 0) { ... }) to check whether each article actually has any images associated with it (some stories don't.) We display the first image only if it actually exists (otherwise an error would be thrown).
    • We gave our <div> element a class of "clearfix", so we can easily apply clearing to it (this technique is needed at the time of writing to stop floated layouts from breaking.)

如果你现在尝试这个示例,它应该会工作,虽然分页按钮还不能使用。

添加分页按钮

为了使分页按钮工作,我们将增加(或减少)pageNumber 变量的值,然后用页面url参数中包含的新值重新运行fetch请求。This works because the NYTimes API only returns 10 results at a time — if more than 10 results are available, it will return the first 10 (0-9) if the page URL parameter is set to 0 (or not included at all — 0 is the default value), the next 10 (10-19) if it is set to 1, and so on.

This allows us to easily write a simplistic pagination function.

  1. Below the existing addEventListener() call, add these two new ones, which cause the nextPage() and previousPage() functions to be invoked when the relevant buttons are clicked:

    nextBtn.addEventListener('click', nextPage);
    previousBtn.addEventListener('click', previousPage);
  2. Below your previous addition, let's define the two functions — add this code now:

    function nextPage(e) {
      pageNumber++;
      fetchResults(e);
    };
    
    function previousPage(e) {
      if(pageNumber > 0) {
        pageNumber--;
      } else {
        return;
      }
      fetchResults(e);
    };

    The first function is simple — we increment the pageNumber variable, then run the fetchResults() function again to display the next page's results.

    The second function works nearly exactly the same way in reverse, but we also have to take the extra step of checking that pageNumber is not already zero before decrementing it — if the fetch request runs with a minus page URL parameter, it could cause errors. If the pageNumber is already 0, we simply return out of the function, to avoid wasting processing power (If we are already at the first page, we don't need to load the same results again).

YouTube示例

We also built another example for you to study and learn from — see our YouTube video search example. This uses two related APIs:

This example is interesting because it shows two related third party APIs being used together to build an app. The first one is a RESTful API, while the second one works more like Google Maps (with constructors, etc.). It is worth noting however that both of the APIs require a JavaScript library to be applied to the page. The RESTful API has functions available to handle making the HTTP requests and returning the results, so you don't have to write them out yourself using say fetch or XHR.

We are not going to say too much more about this example in the article — the source code has detailed comments inserted inside it to explain how it works.

小结

本文提供了使用第三方api添加功能到您的网站的有用的介绍。

本章目录