简介

HTTP 代表超文本传输协议,是整个互联网的基础通信协议。即使这是你第一次接触 HTTP,你可能也比自己意识到的理解得更多。在最基本的层面上,HTTP 的工作原理如下:

  • “你好服务器 XYZ,请问我可以获取文件 abc.html 吗?”
  • “你好啊小客户端,当然可以,给你”

在 PHP 中有许多不同的方法来发送 HTTP 请求。WordPress HTTP API 的目的是尽可能支持这些方法,并使用最适合特定请求的方法。

WordPress HTTP API 也可用于与其他 API(如 Twitter API 或 Google Maps API)进行通信和交互。

HTTP 方法

HTTP 有几种方法(或动词),用于描述特定类型的操作。虽然还有更多,但 WordPress 为最常见的三种预建了函数。每当发出 HTTP 请求时,都会附带一个方法以帮助服务器确定客户端正在请求何种操作。

GET

GET 用于检索数据。这是迄今为止最常用的动词。每次你查看网站或从 API 拉取数据时,你看到的都是 GET 请求的结果。事实上,你的浏览器向你所阅读此文的服务器发送了一个 GET 请求,并请求了构建本文所需的数据。

POST

POST 用于向服务器发送数据,以便服务器以某种方式进行处理。例如,联系表单。当你将数据输入表单字段并点击提交按钮时,浏览器会获取数据并向服务器发送一个 POST 请求,其中包含你在表单中输入的文本。然后服务器将处理该联系请求。

HEAD

HEAD 不如其他两种方法知名。HEAD 本质上与 GET 请求相同,但它不检索数据,只检索关于数据的信息。这些数据描述了诸如数据最后更新时间、客户端是否应缓存数据、数据类型等信息。现代浏览器通常会向以前访问过的页面发送 HEAD 请求,以确定是否有更新。如果没有,你可能实际上看到的是之前下载的页面副本,而不是浪费带宽拉取相同的副本。

所有良好的 API 客户端在执行 GET 请求之前都会使用 HEAD 以节省带宽。虽然如果 HEAD 表示有新数据则需要两个单独的 HTTP 请求,但带有 GET 请求的数据大小可能非常大。仅在 HEAD 表示数据是新的或不应缓存时使用 GET 将有助于节省昂贵的带宽和加载时间。

自定义方法

还有其他 HTTP 方法,如 PUT、DELETE、TRACE 和 CONNECT。这些方法不会在本文中进行介绍,因为 WordPress 中没有预建的方法可供使用,而且 API 实现它们也不常见。

根据服务器的配置方式,你也可以实现自己的额外 HTTP 方法。偏离标准方法总是有风险,并对其他开发人员创建客户端以消耗你的网站或 API 施加了巨大的潜在限制,但使用 WordPress 可以使用任何你希望的方法。我们将在本文中简要介绍如何实现这一点。

响应代码

HTTP 使用数字和字符串响应代码。而不是对每个进行冗长的解释,以下是标准响应代码。在创建 API 时,你可以定义自己的响应代码,但除非你需要支持特定类型的响应,否则最好坚持使用标准代码。自定义代码通常在 1xx 范围内。

代码类别

可以通过三位代码的最左位数字快速看到响应的类型。

Status CodeDescription
2xx请求成功
3xx请求被重定向到另一个 URL
4xx由于客户端错误导致请求失败。通常是无效的认证或缺失数据
5xx由于服务器错误导致请求失败。常见于缺失或配置错误的配置文件

常用代码

这些是你最常遇到的代码。

Status CodeDescription
200OK – 请求成功
301资源已永久移动
302资源已暂时移动
403Forbidden – 通常由于无效的认证
404资源未找到
500内部服务器错误
503服务不可用

从 API 获取数据

GitHub 提供了一个优秀的 API,对于许多公开方面不需要应用程序注册,因此为了演示这些方法,示例将针对 GitHub API。

在 WordPress 中,通过 wp_remote_get() 函数使获取数据变得极其简单。该函数接受以下两个参数:

  1. $url – 要检索数据的资源。这必须是标准 HTTP 格式
  2. $args – 可选 – 你可以在这里传递一个参数数组以更改行为和标头,例如 cookie、跟随重定向等。

以下默认值被假定,但可以通过 $args 参数进行更改:

  • method – GET
  • timeout – 5 – 在放弃之前等待多长时间
  • redirection – 5 – 跟随重定向的次数。
  • httpversion – 1.0
  • blocking – true – 页面的其余部分是否应等待此操作完成后再继续加载?
  • headers – array()
  • body – null
  • cookies – array()

让我们使用 GitHub 用户账户的 URL,看看我们可以获取什么信息

$response = wp_remote_get( 'https://api.github.com/users/blobaugh' );

$response 将包含所有标头、内容以及关于我们请求的其他元数据

Array(
	[headers] => Array(
		[server] => nginx
		[date] => Fri, 05 Oct 2012 04:43:50 GMT
		[content-type] => application/json; charset=utf-8
		[connection] => close
		[status] => 200 OK
		[vary] => Accept
		[x-ratelimit-remaining] => 4988
		[content-length] => 594
		[last-modified] => Fri, 05 Oct 2012 04:39:58 GMT
		[etag] => "5d5e6f7a09462d6a2b473fb616a26d2a"
		[x-github-media-type] => github.beta
		[cache-control] => public, s-maxage=60, max-age=60
		[x-content-type-options] => nosniff
		[x-ratelimit-limit] => 5000
	)
	[body] => {"type":"User","login":"blobaugh","gravatar_id":"f25f324a47a1efdf7a745e0b2e3c878f","public_gists":1,"followers":22,"created_at":"2011-05-23T21:38:50Z","public_repos":31,"email":"ben@lobaugh.net","hireable":true,"blog":"http://ben.lobaugh.net","bio":null,"following":30,"name":"Ben Lobaugh","company":null,"avatar_url":"https://secure.gravatar.com/avatar/f25f324a47a1efdf7a745e0b2e3c878f?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png","id":806179,"html_url":"https://github.com/blobaugh","location":null,"url":"https://api.github.com/users/blobaugh"}
	[response] => Array(
		[preserved_text 5237511b45884ac6db1ff9d7e407f225 /] => 200
		[message] => OK
	)
	[cookies] => Array()
	[filename] =>
)

与之前的两个函数一样,所有相同的辅助函数都可以用于此函数。这里的例外是 HEAD 从不返回 body,因此该元素将始终为空。

获取你一直想要的 body

仅可以使用 wp_remote_retrieve_body() 检索 body。该函数只接受一个参数,即任何其他 wp_remote_X 函数的响应,其中 retrieve 不是下一个值。

$response = wp_remote_get( 'https://api.github.com/users/blobaugh' );
$body     = wp_remote_retrieve_body( $response );

仍然使用上一个示例中的 GitHub 资源,$body 将是

{"type":"User","login":"blobaugh","public_repos":31,"gravatar_id":"f25f324a47a1efdf7a745e0b2e3c878f","followers":22,"avatar_url":"https://secure.gravatar.com/avatar/f25f324a47a1efdf7a745e0b2e3c878f?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png","public_gists":1,"created_at":"2011-05-23T21:38:50Z","email":"ben@lobaugh.net","following":30,"name":"Ben Lobaugh","company":null,"hireable":true,"id":806179,"html_url":"https://github.com/blobaugh","blog":"http://ben.lobaugh.net","location":null,"bio":null,"url":"https://api.github.com/users/blobaugh"}

如果您除了获取响应体之外没有其他操作需要执行,可以将代码简化为一行:

$body = wp_remote_retrieve_body( wp_remote_get( 'https://api.github.com/users/blobaugh' ) );

许多这些辅助函数也可以以类似的方式在一行中使用。

获取响应代码

您可能希望检查响应代码以确保检索成功。这可以通过 wp_remote_retrieve_response_code() 函数完成:

$response = wp_remote_get( 'https://api.github.com/users/blobaugh' );
$http_code = wp_remote_retrieve_response_code( $response );

如果成功,$http_code 将包含 200。

获取特定标头

如果您想检索特定标头(例如 last-modified),可以使用 wp_remote_retrieve_header() 函数。该函数接受两个参数:

  1. $response – get 调用的响应
  2. $header – 要检索的标头名称

要检索 last-modified 标头:

$response      = wp_remote_get( 'https://api.github.com/users/blobaugh' );
$last_modified = wp_remote_retrieve_header( $response, 'last-modified' );

$last_modified 将包含 [last-modified] => Fri, 05 Oct 2012 04:39:58 GMT
您也可以使用 wp_remote_retrieve_headers( $response ) 以数组形式检索所有标头。

使用基本身份验证进行 GET

安全性更高的 API 提供多种不同类型的身份验证中的一种或多种。HTTP Basic Authentication 是一种常见(尽管安全性不高)的身份验证方法。它可以通过将‘Authorization’传递给 wp_remote_get() 函数的第二个参数,以及其他 HTTP 方法函数,在 WordPress 中使用。

$args = array(
    'headers' => array(
        'Authorization' => 'Basic ' . base64_encode( YOUR_USERNAME . ':' . YOUR_PASSWORD )
    )
);
wp_remote_get( $url, $args );

向 API 发送 POST 数据

所有 HTTP 方法调用都提供相同的辅助方法(wp_remote_retrieve_body() 等),并以相同的方式使用。

POST 数据是使用 wp_remote_post() 函数完成的,它接受与 wp_remote_get() 完全相同的参数。在此需要指出的是,您必须传递第二个参数数组中的所有元素。Codex 提供了默认的可接受值。您现在只需要关心发送的数据,因此其他值将使用默认值。

要向服务器发送数据,您需要构建一个关联数组。该数据将被分配给 'body' 值。从服务器端来看,该值将出现在 $_POST 变量中,正如您所预期的那样。即如果 body => array( 'myvar' => 5 ),则在服务器上 $_POST['myvar'] = 5。

由于 GitHub 不允许向上一示例中使用的 API 发送 POST 请求,本示例将假装它允许这样做。通常,如果您想向 API 发送 POST 数据,您需要联系 API 的维护者并获取 API 密钥或其他形式的身份验证令牌。这仅证明您的应用程序被允许像用户登录网站一样操作 API 上的数据。

假设我们要提交一个包含以下字段的联系表单:name、email、subject、comment。要设置 body,我们执行以下操作:

$body = array(
	'name'    => 'Jane Smith',
	'email'   => 'some@email.com',
	'subject' => 'Checkout this API stuff',
	'comment' => 'I just read a great tutorial. You gotta check it out!',
);

现在我们需要设置将传递给 wp_remote_post() 第二个参数的其余值:

$args = array(
	'body'        => $body,
	'timeout'     => '5',
	'redirection' => '5',
	'httpversion' => '1.0',
	'blocking'    => true,
	'headers'     => array(),
	'cookies'     => array(),
);

然后当然发出调用:

$response = wp_remote_post( 'http://your-contact-form.com', $args );

HEAD 减少带宽使用

在检索资源之前使用 HEAD 检查资源状态可能非常重要,有时 API 也会要求这样做。在高流量 API 上,GET 通常限制为每分钟或每小时的请求数量。除非 HEAD 请求显示 API 上的数据已更新,否则甚至不需要尝试 GET 请求。

如前所述,HEAD 包含有关数据是否已更新、是否应缓存数据、何时使缓存副本过期以及有时对 API 的请求速率限制的数据。

回到 GitHub 示例,以下是需要注意的几个标头。大多数标头是标准的,但您应该始终检查 API 文档以确保您了解哪些标头名称是什么及其用途。

  • x-ratelimit-limit – 允许的时间段内的请求数量
  • x-ratelimit-remaining – 时间段内剩余的可用请求数量
  • content-length – 内容的大小(以字节为单位)。如果内容相当大,这可用于警告用户
  • last-modified – 资源最后修改的时间。对缓存工具非常有用
  • cache-control – 客户端应如何处理缓存

以下将检查我的 GitHub 用户账户的 HEAD 值:

$response = wp_remote_head( 'https://api.github.com/users/blobaugh' );

$response 应该类似于

Array(
	[headers] => Array
		(
		[server] => nginx
		[date] => Fri, 05 Oct 2012 05:21:26 GMT
		[content-type] => application/json; charset=utf-8
		[connection] => close
		[status] => 200 OK
		[vary] => Accept
		[x-ratelimit-remaining] => 4982
		[content-length] => 594
		[last-modified] => Fri, 05 Oct 2012 04:39:58 GMT
		[etag] => "5d5e6f7a09462d6a2b473fb616a26d2a"
		[x-github-media-type] => github.beta
		[cache-control] => public, s-maxage=60, max-age=60
		[x-content-type-options] => nosniff
		[x-ratelimit-limit] => 5000
	)
    [body] =>
    [response] => Array
		(
		[preserved_text 39a8515bd2dce2aa06ee8a2a6656b1de /] => 200
		[message] => OK
	)
    [cookies] => Array(
	)
	[filename] =>
)


与之前的两个函数一样,所有相同的辅助函数都可以用于此函数。这里的例外是 HEAD 从不返回 body,因此该元素将始终为空。

发出任何类型的请求

如果您需要使用上述任何函数不支持的 HTTP 方法发出请求,请不要惊慌。开发 WordPress 的优秀人士已经想到了这一点,并提供了 wp_remote_request()。该函数接受与 wp_remote_get() 相同的两个参数,并允许您指定 HTTP 方法。您需要传递什么数据取决于您的方法。

要发送 DELETE 方法的示例,您可能拥有类似以下内容:

$args     = array(
	'method' => 'DELETE',
);
$response = wp_remote_request( 'http://some-api.com/object/to/delete', $args );

缓存简介

缓存是一种实践,即将常用对象或需要大量时间构建的对象保存到快速对象存储中,以便在后续请求时快速检索。这避免了再次花费时间获取和构建对象的必要性。缓存是一个广泛的课题,是网站优化的一部分,甚至可以单独成为一系列文章。以下内容仅是缓存的简介以及快速设置 API 响应缓存的一种简单而有效的方法。

为什么要缓存 API 响应?房间里的大象是因为外部 API 会减慢您的网站速度。许多顾问会告诉您,利用外部 API 可以通过减少连接和处理量以及昂贵的带宽来提高网站的性能,但有时这并不完全正确。

这是服务器发送数据的速度与远程服务器处理请求、构建数据并返回所需时间之间的一种微妙平衡。另一个显著的问题是,许多 API 在特定时间段内限制请求数量,并且可能限制应用程序同时建立的连接数。缓存可以通过将数据的副本保存在您的服务器上(直到需要刷新)来帮助解决这些难题。

何时应该使用缓存?

这个问题的简单答案是“总是”,但有时也不应该这样做。如果您处理的是实时数据,或者 API 明确在响应头中指示不要缓存,那么您可能不想缓存;但在所有其他情况下,缓存从 API 检索的任何资源通常都是个好主意。

WordPress Transients

WordPress Transients 提供了一种方便的方式来存储和使用缓存对象。Transients 会存活指定的时间,或者直到您需要在 API 资源更新时使其过期为止。使用 WordPress 中的 transient 功能可能是您遇到的最容易使用的缓存系统。只有三个函数即可完成所有繁重的工作。

缓存对象(设置 Transient)

缓存对象是使用 set_transient() 函数完成的。该函数接受以下三个参数:

  1. $transient – Transient 的名称,供将来参考
  2. $value – Transient 的值
  3. $expiration – 从保存 Transient 到其过期的秒数

缓存上述 GitHub 用户信息响应一小时的一个示例如下:

$response = wp_remote_get( 'https://api.github.com/users/blobaugh' );
set_transient( 'prefix_github_userinfo', $response, 60 * 60 );

获取缓存对象(获取 Transient)

获取缓存对象比设置 Transient 要复杂得多。您需要请求 Transient,然后还需要检查该 Transient 是否已过期,如果是,则获取更新后的数据。通常 set_transient() 调用是在 get_transient() 调用内部进行的。以下是获取 GitHub 用户配置文件 Transient 数据的示例:

$github_userinfo = get_transient( 'prefix_github_userinfo' );
if ( false === $github_userinfo ) {
	// Transient expired, refresh the data
	$response = wp_remote_get( 'https://api.github.com/users/blobaugh' );
	set_transient( 'prefix_github_userinfo', $response, HOUR_IN_SECONDS );
}
// 按您的需要使用 $github_userinfo

删除缓存对象(删除 Transient)

删除缓存对象是所有 Transient 函数中最简单的,只需传递 Transient 的名称作为参数即可完成。

要删除 GitHub 用户信息:

delete_transient( 'blobaugh_github_userinfo' );

有关 Transients 的更多信息,请参见 此处。