WordPress Plugin Development Tutorial Manual - HTTP API
HTTP 是超文本传输协议,是整个互联网的基础,即使这是我们第一次使用 HTTP,我们知道得也可能比认为得要多。HTTP 的工作原理如下:
- 你好服务器 XYZ,请问有文件 abc.html 吗?
- 你好,朋友,我有这个文件,现在发给你,接好了。
我们有很多方法可以在 PHP 中发送 HTTP 请求,WordPress HTTP API 的目的是支持尽可能多的方法,并在各种情况下使用最合适的方法。WordPress HTTP API 也可用于和其他 API(如 Twitter API 或 Google API)进行通信和交互。
HTTP Methods
HTTP 有几种描述特定操作的方法或动词。WordPress 帮我们预先建立了三种最常见的功能,每次一个 HTTP 请求时,WordPress 也会传递一个方法来帮助服务器确定客户端正在请求什么样的动作。
GET
GET 方法用于从服务器获取数据,这是迄今为止最常用的方法,我们查看网站或者从 API 获取数据的时候,我们都会使用 GET 请求,事实上,我们在阅读这篇文章的时候,我们使用的浏览器就是在帮我们展示从服务器 GET 到的页面。
POST
POST 请求用于把数据发送到服务器,以便服务器进行某种方式的操作,例如,一个联系表单。我们把数据输入到这个表单并点击了提交按钮,浏览器会把我们输入的数据以 POST 的方法发送给服务器,服务器收到数据后,会进行一些处理。
HEAD
HEAD 请求使用得要比上面两个少得多,HEAD 与 GET 请求基本相同,是不过 HEAD 请求只获取关于数据的信息,不获取数据。这些信息描述了数据的最后更新时间,客户端是否应该缓存数据,数据的类型等等。现代浏览器通常会向已经访问过的页面发送 HEAD 请求,以确定是否需要更新。如果不需要,浏览器展示给我们的是缓存在本地的页面副本,而不用浪费宽带获取一个一模一样的页面。
所有优秀的 API 都应该在执行 GET 请求之前先使用 HEAD 请求来尝试节省带宽。虽然在有数据更新的时候,这种处理方式会发送两次 HTTP 请求,但是相对于通过 GET 方法获取有可能很大的重复数据,先使用 GET 请求测试一下还是可以节省很多带宽的。只有在 HEAD 请求表示数据已被更新或者不应该被缓存的情况下直接使用 GET 请求可以节省一次请求的带宽和时间。
自定义方法
除了上面介绍的,还有其他 HTTP 方法,比如 PUT,DELETE,TRACE,和 CONNECT,本文不会涉及到这些方法,因为 WorPress 没有预先构建这些方法,目前为止我们使用的也不多。
根据服务器的配置,我们也可以实现自定义 HTTP 方法,不过使用标准方法之外的自定义方法像是一场赌博,可能会给其他开发者设置一些潜在的限制,因为他们可能会搞不懂我们的自定义方法。虽然如此,我们可以通过 WordPress 来使用任何喜欢的方法,我们将在下面简要的介绍如何实现这一点。
response code
HTTP 使用数字和字符串形式的响应代码,而不是对每一个响应做一个冗长的解释,这里指的是标准的响应代码。我们也可以在创建 API 的时候定义自己的响应代码,但是除非需要支持特定类型的响应,我们最好遵循标准代码,自定义代码通常在 1xx 范围内。
响应代码类型
响应类型可以通过响应代码最左边的数字看到。
状态码 | descriptive |
---|---|
2XX | 请求成功 |
3XX | 请求被重定向到另一个URL |
4XX | 请求失败,由于客户端错误。通常无效的验证或丢失的数据 |
5XX | 由于服务器错误,请求失败。通常丢失或错误配置的配置文件 |
通用代码
下面是我们最常遇到的响应代码。
状态码 | descriptive |
---|---|
200 | 请求成功 |
301 | 资源被永久移动 |
302 | 资源暂时移动 |
403 | 禁止 – 通常由于验证无效 |
404 | 资源未找到 |
500 | 内部服务器错误 |
503 | 暂停服务 |
Getting data from the API
Github 提供了一个非常优秀的 API,不需要注册就可以访问一些公开数据,所以,为了演示上面提到的一些方法,我们将访问 GIthub API 进行演示。
通过 wp_remote_get() 函数获取数据在 WordPress 中非常简单,这个函数接受以下两个参数。
- $url – 资源的 URL 地址,必须使用标准的 HTTP URL 格式
- $args – 可选,我们可以在这里传递一个参数数组来改变请求的行为和 header 信息,例如 cookies,重定向等。
下面是 $args 参数的一些默认值,我们可以通过 $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 将包含有关我们请求的所有 headers 信息,内容和其他元数据。
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 信息
通常我们只需要使用响应的 body 信息,我们可以使用 wp_remote_retrieve_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 信息之外,我们没有其他操作,我们可以把上面的代码压缩为一行。
$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。
获取特定的 Header 信息
有时候,我们需要获取一个特定的 Header 信息,比如数据的最后修改时间,我们可以使用 wp_remote_retrieve_header() 函数来获取,这个函数接受两个参数。
- $response – 来自请求的响应信息
- $header – 要获取 Header 信息的名称
获取最后修改时Header 信息
$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 ) 获取 HTTP 响应的所有 Header 信息。
使用 GET 进行基本身份验证
更安全的 API 会提供一种或多种身份验证,HTTP 基本认证是一种常见但不是很安全的认证方法,我们可以在 WordPress 中使用这种认证方法,把我们的授权信息传递给 wp_remote_get() 函数的第二个参数即可,其他 HTTP 方法也一样。
$args = array(
'headers' => array(
'Authorization' => 'Basic ' . base64_encode( YOUR_USERNAME . ':' . YOUR_PASSWORD )
)
);
wp_remote_get( $url, $args );
Send data to the API
wp_remote_retrieve_body()
可以在所有 HTTP 方法上面以同样的方式使用。
发送数据应该使用 wp_remote_post() 函数完成,该函数的参数和 wp_remote_get() 完全一样,需要注意的是,我们应该把需要发送的所有数据放到第二个参数里面,函数提供了一些基本默认数据,我们只需要关注我们发送的数据即可。
要发送数据到服务器,我们需要建立一个关联数组,该数组将被设置为 ‘body’ 的值,如我们所愿,在接受请求的服务端,这个数组的值将出现在 $_POST 变量中。举个例子,如果 body => array( 'myvar' => 5 )
,在接收服务器上就是 $_POST['myvar'] = 5
The
由于 Github 不允许发送 POST 请求到上面例子中的 API 上,下面的例子是伪造的。通常,如果需要将数据发布到 API,我们需要从 API 提供方获取 API 密钥或其他形式的身份验证令牌。用来证明我们的应用程序可以像用户一样登录到服务器,通过 API 操作服务器上的数据。
比如我们正在提交一个联系表单,其中包含 name, email, subject, comment 这些字段,我们首先需要设置 POST 请求的 body 信息。
$body = array(
'name' => 'Jane Smith',
'email' => 'some@email.com',
'subject' => 'Checkout this API stuff',
'comment' => 'I just read a great tutorial by this Ben Lobaugh. It taught me amazing things about interacting with APIs in WordPress! You gotta check it out!'
);
然后,把我们设置的 $body 信息和其他可选的参数设置为 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 );
完整的代码如下:
$body = array(
'name' => 'Jane Smith',
'email' => 'some@email.com',
'subject' => 'Checkout this API stuff',
'comment' => 'I just read a great tutorial by this Ben Lobaugh. It taught me amazing things about interacting with APIs in WordPress! You gotta check it out!'
);
$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 );
Pay attention to bandwidth usage
在获取资源之前,使用 HEAD 检查资源状态可能非常重要(有些 API 要求这样做),在高流量的 API 上,GET 请求通常会被限制频次,除非 HEAD 请求的返回信息显示 API 上的数据已更新,否则不需要发送 GET 请求。
像上面说的一样,HEAD 包含数据是否已经更新,数据是否应该被缓存,缓存何时过期以及有时对 API 的请求速率限制。
回到上面的 Github 示例,有几个 Header 信息需要注意,这些 Header 信息大部分都是 HTTP 的标准信息,也有一些自定义的 Header 信息,使用之前,我们应该检查 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] =>
)
Send any kind of request
如果我们需要使用和上面介不一样的方法来发送请求,不要怕,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 );
(computing) cache
缓存是一种很常见的实践,我们经常会把比较耗时的常用对象保存到一个方便快速获取的缓存库里面,以便在以后的请求中快速获取。这样可以避免服务器耗费不必要的时间重新生成重复数据。缓存是一个比较大的话题,是网站性能优化的一部分,我们在这里不展开讨论。下面的内容只是对缓存的介绍,以及设置 HTTP API 响应缓存的方法。
为什么要缓存 API 响应呢?有时候,外部 API 会降低我们网站的响应速度。我们可能会在某些地方看到,利用外部 API 可以减少连接和处理的数量、以及昂贵的带宽,从而提高网站性能,但在有些情况下,会出现相反的情况。
我们需要在服务器发送数据的速度和请求远程数据并等待其返回所花的时间之间进行平衡。后者显而易见的问题是许多 API 会对一段时间内的请求数量做限制,并且可能会限制一次请求的连接数量。缓存可以把获取到的远程数据副本放到我们自己的服务器上,从而解决这些问题,直到数据需要刷新为止。
应该什么时候进行缓存?
答案是,大多数情况下,我们都应该进行缓存,除非我们需要处理的是实时数据,或者 API 的 HEAD 信息明确指定了不缓存数据。
WordPress 的缓存 API
WordPress 缓存 API 为我们提供了一种方便的方式来存储和使用缓存,缓存有一段时间的有效期,过了有效期或远程 API 中的数据更新了之后,我们需要重新设置缓存,WordPress 的缓存功能可能是最方便使用的缓存系统了,缓存 API 只提供了三个功能来帮助我们完成繁杂的工作。
设置缓存
We use set_transient 函数来缓存一个对象,该函数接受以下三个参数:
- $transient – 缓存的名称,供获取和删除缓存使用
- $value – 缓存的值
- $expiration – 缓存的到期时间
下面的代码把从 Github API 中获取的用户信息缓存了一个小时。
$response = wp_remote_get( 'https://api.github.com/users/blobaugh' );
set_transient( 'blobaugh_github_userinfo', $response, 60*60 );
获取缓存
获取缓存对象要比设置缓存复杂一点,在获取缓存之前,我们需要先检查缓存是否已经过期,如果已过期,我们需要从 API 获取新数据,然后重新设置缓存供下一次获取。通常 set_transient respond in singing get_transient 是一起使用的。以下的是获取 Github 用户信息缓存的示例。
$github_userinfo = get_transient( 'blobaugh_github_userinfo' );
if( false === $github_userinfo ) {
// 缓存已过期,刷新缓存
$response = wp_remote_get( 'https://api.github.com/users/blobaugh' );
set_transient( 'blobaugh_github_userinfo', $response, 60*60 );
}
// Use $github_userinfo as you will
删除缓存
删除缓存就容易得多了,只需要把缓存的名称传递给 delete_transient 函数That's all.
delete_transient( 'blobaugh_github_userinfo' );