以下是由 Eduardo Bouças 撰写的客座文章。 我们都知道 WordPress 是一个 CMS,但 Eduardo 认为将它**仅用作内容的 API**。 根本没有前端,只有返回 JSON 的 URL 端点,可用于其他任何地方。 这并没有详细说明一个完整的解决方案来做到这一点,它只是一些想法,以及一些示例代码,可以让你开始创建自定义解决方案。 如果你想立即开始在这样的系统上进行开发,WP REST API 是最强大的项目,拥有最大的发展势头。
我最近被要求为一家数字代理商选择和实现一个 CMS 解决方案,以便在一个安装中管理多个网站。 出于很多原因,主要候选人是 WordPress。 它是免费的、开源的,拥有庞大的用户社区,易于使用,并且具有 多站点功能。 它无疑是经过商业验证的成熟产品。 但是,我对此有所保留。
以传统方式使用 WordPress 意味着进行安装、创建主题(或修改现有主题),并接受所有进一步的自定义必须在 CMS 创建的生态系统中找到自己的位置:编程语言和技术(PHP 和 MySQL)以及巧妙的(但相当复杂)的插件、主题、操作、过滤器等等的世界。
我一直将内容视为网站上一切的核心。 每个项目的性质和创意理念都应该决定什么媒介和技术能够最好地传递内容,而不是 CMS。
我不想让项目的技术选择仅仅局限于 PHP,因为 WordPress 是基于它构建的,我希望开发人员能够自由选择他们认为合适的任何技术栈来创建独立且自包含的网站,而不仅仅是 WordPress 插件和主题。
创建中间人
一个 API-First 解决方案,通过一个位于网站和 CMS 之间的中间层,可以解放 WordPress 不用处理任何前端业务,并将它只用于管理和交付内容。
这个“中间人”层能够使用通用的语言(我更喜欢 JSON),不同的终端平台可以理解它,并以适合项目的方式对其进行处理。
我正在考虑类似这样的东西

调整 WordPress
在正常的 WordPress 世界中,人们会通过友好的域名访问网站,内容会从数据库中提取,然后主题会将其格式化并显示在 HTML 页面中。 该页面也很可能会有一个可视化界面,供用户浏览帖子和页面,并根据类别、标签或任何其他分类法过滤内容。
我们的 API-First WordPress 不会有任何这些东西。 我们将接受来自用户的唯一输入将来自他们发送的请求的 URL,我们必须解析它才能提取我们需要交付的数据类型、格式以及传递它的过滤器。
构建插件
在 WordPress 中添加和更改功能有好的方法,也有不好的方法。 简而言之,修改核心代码库不是好兆头,你应该创建插件。
但是我们的插件究竟是如何工作的呢? 它如何改变 CMS 遵循的默认事件链,以读取请求,从数据库中获取内容,然后发送回内容? 这可以通过一个钩子(更确切地说是一个操作)来实现,它允许我们在工作中插入一个扳手,拦截请求,完全控制从那时起发生的事情。
所以让我们开始为我们的插件奠定基础。
class API {
public function __construct() {
add_action('template_redirect', array($this, 'hijackRequests'), -100);
}
public function hijackRequests() {
$entries = get_posts($_GET['filter']);
$this->writeJsonResponse($entries);
}
protected function writeJsonResponse($message, $status = 200) {
header('content-type: application/json; charset=utf-8', true, $status);
echo(json_encode($message) . "\n");
exit;
}
}
new API();
将插件包装在类构造中是一个好习惯,以避免在全局命名空间中使用松散函数,这可能会导致命名冲突。
然后,我们首先使用template_redirect
操作注册一个函数,该操作在初始化例程执行后立即执行,并在 WordPress 决定使用哪个模板来呈现页面之前执行。
然后我们执行get_posts()
,它接受一个过滤器数组作为参数,并返回一个匹配项数组(函数名可能具有误导性;它可以返回帖子和页面)。
所以,在保存文件并激活插件后,访问http://your-WP/index.php?filter[post_type]=post&filter[posts_per_page]=1
应该可以获得最新帖子的 JSON 表示形式。 真是太棒了!
多路复用请求
在这一点上,我们有一个非常基本的 API,它允许我们根据一组过滤器从 WordPress 中提取条目,这对于非常简单的项目可能就足够了。 但是,当我们需要获取多组数据来呈现页面上的不同元素时会发生什么? 发送多个 HTTP 请求似乎并不合理。
以 CSS-Tricks 上的 论坛页面 为例。 除了我们可能需要的一些元数据之外,至少有三种不同的内容集需要从 CMS 中提取:导航栏上的项目、最新帖子和技巧。

我们可以为 API 定义自己的自定义语法,以便它可以动态地接受“内容桶”的定义,并以 JSON 数组的形式将它们分隔在响应中返回。
与其将过滤器作为 URL 中的简单数组传递,不如为每个过滤器附加一个标签,说明它们属于某个桶。 回到上面的例子,多路复用请求的 URL 可以是这样的
?navigation:filter[category]=navigation
&latestPosts:filter[type]=post
&tips:filter[slug]=tips
这将返回类似这样的 JSON 结构
{
"navigation": [
{
"ID": 1
(...)
},
{
"ID": 2
(...)
}
],
"latestPosts": [
(...)
],
"tips": [
(...)
]
}
这使 API 消费者能够轻松访问他们需要的不同内容部分,而无需任何额外的努力。
函数hijackRequests
可以修改以实现此功能。
public function hijackRequests() {
$usingBuckets = false;
$buckets = array();
$entries = array();
foreach ($_GET as $variable => $value) {
if (($separator = strpos($variable, ':')) !== false) {
$usingBuckets = true;
$bucket = substr($variable, 0, $separator);
$filter = substr($variable, $separator + 1);
} else {
$bucket = 0;
$filter = $variable;
}
$buckets[$bucket][$filter] = $value;
}
if ($usingBuckets) {
foreach ($buckets as $name => $content) {
$entries[$name] = get_posts($content['filter']);
}
} else {
$entries = get_posts($buckets[0]['filter']);
}
$this->writeJsonResponse($entries);
}
添加画廊和自定义字段
我们对帖子的 JSON 表示形式依赖于get_posts()
返回的信息,但那里缺少一些你可能希望在提要中看到的信息,例如图像画廊和自定义字段。 我们可以借助get_post_galleries_images()
和get_post_meta()
函数将这些信息附加到 JSON 提要中。
for ($i = 0, $numEntries = count($entries); $i < $numEntries; $i++) {
$metaFields = get_post_meta($entries[$i]->ID);
$galleriesImages = get_post_galleries_images($entries[$i]->ID);
$entries[$i]->galleries = $galleriesImages;
foreach ($metaFields as $field => $value) {
// Discarding private meta fields (that begin with an underscore)
if (strpos($field, '_') === 0) {
continue;
}
if (is_array($value) && (count($value) == 1)) {
$entries[$i]->$field = $value[0];
} else {
$entries[$i]->$field = $value;
}
}
}
最终想法
本文中描述的解决方案仅仅触及了构建 API 所涉及内容的皮毛。 我们还没有涉及诸如身份验证、写入访问的请求类型(POST
、PUT
、DELETE
)、不同内容类型的多个端点(用户、类别、设置)、API 版本控制或 JSONP 支持等方面。
此解决方案并不是为了提供一个可用于生产环境的产品,而是为了展示 WordPress API 的内部工作原理,希望能够激发人们创建自定义解决方案,或扩展现有解决方案,以满足他们特定的需求。
说实话,创建定制的 API 解决方案并不适合所有人。 WP REST API 是一款成熟的成熟产品,很快将成为 WordPress 核心的一部分,因此使用这样的东西可能是一个更明智的选择。
最重要的是,本文的目的是探讨将像 WordPress 这样的广泛使用、经商业验证的成熟产品用作 API-First 内容管理系统的想法。 这意味着要剥离 WordPress 的大部分功能,并失去 SEO 插件和简单主题化等功能带来的好处,但你将获得平台无关系统的自由。
你有什么想法?
很棒的文章! 你常用的缓存插件(WP Super Cache、W3 Total Cache)是否也会缓存通过此 API 的请求? 我认为它可能会这样做,因为你使用了
get_posts()
。太有意思了!自从 WP REST API 兴起,我脑海中一直盘旋着类似的想法。虽然你提出的解决方案确实很巧妙,但我感觉同时维护 WordPress 插件、WordPress 安装(可能在前端以外的地方托管)和前端可能会有点让人吃不消。我倾向于认为 REST API 非常适合构建“Web 应用程序”,只需使用一个自定义主题作为前端。这样做的优点是,你可以使用与 Web 应用程序相同的 API 从你的 iPhone 应用程序检索或发布数据。
总之,很棒的东西!
好主意!
我使用 WordPress 作为 CMS 已经好几年了,最初使用 Coldfusion 作为“前端”,最近使用 PHP 作为“前端”。
虽然不像你提议的那样是一个 API(这是一个好主意),但它仍然使用 WordPress 作为数据输入工具,并将页面渲染到非 WordPress 前端。
WordPress 已经提供了一些原生 PHP 函数来实现这一点,但正如你所指出的,你将被绑定到 PHP “前端”。
我对 REST API 感到兴奋,但我不禁想知道 WP REST API 与 Laravel 或其精简版本 Lumen 之间的区别是什么?别误会,我认为 API 对开发者和读者来说都是有好处的,但是 WordPress 将如何使开发与竞争对手区别开来。在我阅读有关该主题的内容时,它总是听起来像这个项目的目的是避开 WP 的内容管理方面,并将其留给其他框架来解决,使其更像一个像 composer 甚至 node.js 这样的组件热交换。但我认为 WordPress 的优点在于它如何处理主题和内容开发,以及强大的用户友好的管理 UI。
有人知道 WordPress 后端/管理 UI 是否以及如何与 WP API 一起更改吗?
我使用 WordPress 来编写我产品的文档。文档在不同的端点提供,所以我不想使用 WP 的前端。我在这里使用 API。
使用 WordPress 后端,我可以创建和管理内容。我无法使用 Laravel 来创建或管理内容。
我最近一直在使用 WordPress 做这个。WordPress 前端然后成为管理内容的界面。我错误地使用了 Toolset 工具(http://wp-types.com/)来创建各种相关的自定义内容类型,以及用于管理数据的表单。API 界面必须理解自定义内容数据库结构。
你说“文章只是触及了构建 API 的皮毛……”,我完全同意。如果你想为不同的输出渠道提供多个 API 密钥怎么办?或者向困难的移动网络环境提供服务?或者根据输出格式重新调整媒体资产的规模?或者捆绑请求以最小化移动网络延迟?或者从某个时间点同步更改的增量?或者通过 API 以编程方式创建内容?或者扩展到每小时数百万个请求而不会破坏 WordPress 数据库瓶颈?
在重新发明轮子之前,你可能想查看一些从头开始设计用于 API 传输的系统,例如 Contentful。
我认为我和克里斯(在引言中)都很好地说明了这只是一个想法,而不是一个完整的实现。
我并没有试图重新发明轮子,但我认为如果你让人们了解 API 如何在这种情况下工作,他们就能更好地了解如何选择现有的解决方案,并且希望通过一些知识来增强他们的能力,以扩展解决方案,并加入他们自己的功能。
我个人觉得这比一篇说“嘿,想要一个 API 优先的解决方案?购买这个”的文章更有用。
@克里斯,你在 Contentful 有多少股份?
作者说这篇文章只是一些关于最佳实践/想法的思考,并且由于它发布在这个网站上,它显然有它的位置,而且值得一读。
编写自己的 API 的全部意义在于学习和理解,所以为了推广付费替代方案而贬低他人的想法,这真的是你的不对。学会热爱社区,克里斯,学会热爱。
如果我触犯了你的神经,我感到抱歉,我的爆发绝不是为了贬低这篇文章。只是一些我最近在代理公司与客户合作的经历,客户坚持使用 WordPress 作为移动后端,引言让我很不舒服。
当你有真正聪明的人花几个月时间教 Drupal 如何说 API 并且很难做到时,当你有一流的前瞻性出版商分享他们的 API 思想并为你提供蓝图时,你最不希望的就是遇到另一个来自地狱的客户坚持使用 WP。
我同意 WordPress 不是构建 API 优先解决方案的最明显平台,并且使用从头开始构建的替代平台的优点是显而易见的。但是,有时这可能不足以抵消像 WordPress 这样广为使用的平台的优势。
如果客户依靠数十个外部编辑来插入和管理内容怎么办?你会向他们提供有关如何使用你的基于 Drupal 的 API 优先解决方案的培训吗?因为大多数人可能知道如何使用 WordPress,而有些人可能从未听说过 Drupal。这只是一个 WordPress 解决方案可能是不错选择的一个例子。
你的观点是这篇文章中提出的实现是简化的。我同意你的观点。但我认为说 WordPress 强制性地是一个糟糕的选择,这更加简化。
这篇文章的全部意义是探讨将 WordPress(及其带来的优势)与 API 优先方法结合起来,这种组合并不明显。它是否有效,以及它是否是正确的技术选择,取决于大量的变量和情况。
不要忘记,WordPress 已经包含一些与 JSON 相关的函数,用于处理清理、验证、输出等。
示例中的 writeJsonResponse() 很大程度上是 wp_send_json() 的重写。
没错!我写一个单独函数的想法是,你可以使用不同的响应代码发送响应。我想我可以使用
wp_send_json_success()
和wp_send_json_error()
,但这感觉更灵活一些。但这是一个很好的建议。谢谢!
我也尝试过一些基本的概念,就像这样。
将这个概念有趣的原因添加到你还可以开发一个与 CMS 无关的前端的事实中。
通过从你选择的任何 CMS 获取结构良好的 API 响应,你的应用程序/浏览器只需调用 API 并接收它需要的 JSON。
这有点像创建 CMSMS,因为这就是 CMS 应该做的事情;与你的数据交互并允许你以你选择的格式显示它。
然而,由于 WordPress(和其他 CMS)构建被用于如此广泛的用例,我相信有些人可能会发现这种额外的层是有用的。
尝试一下 Symfony WordPress Bundle!我们在 HYPEBEAST.com 使用它。
http://github.com/kayue/KayueWordpressBundle/
不错的文章。
我认为总体趋势显然是转向客户端 UI,而不是过时的服务器端 UI。现在是范式转变的时候了,因为几乎所有星星都对齐了(移动设备和 JavaScript 性能、HTML5/CSS3 和 WHATWG 规范支持、ES6 功能、前端工具等)。 :)
我最近开始着手一个项目,并牢记这些想法:https://github.com/dsebastien/midnightLightV2
我在我的博客上写了一些关于它的内容:http://www.dsebastien.net/2015/04/22/web-3-0-is-closer-than-you-might-think/
这应该被称为无头 Drupal,特别是如果你使用 angular.js 作为前端。Drupal 也有类似的开发。许多人不知道的是,你可以像 12-15 年前一样使用开放办公室作为数据存储库。如果你将这称为无头 WordPress,那么极客们将更清楚地看到其中的平行关系。
第一个无头 Drupal 应该是无头 WordPress……借口。
克里斯给出的 Contentful 链接很棒,还有客座作者爱德华多·博卡斯,感谢你抽出时间,看看人们如何解决问题很有趣。对于任何发布“这应该更好”的人来说……当然,它可以更好,它不是文章中的 API,甚至不是文章系列,就像大学和美国人所说的学院不会给你任何东西一样。
那会是什么样子?DBAD,并且理智。这并不是为了代替任何人的工作。
我很想知道这个模型是否已经用在大型网站上?我正在考虑在我的网站上使用它,但仍在寻找相关信息。
据我所知,Bocoup 已经为一个大型客户(他们没有透露是谁)使用 Node.js 来渲染前端。WordPress 只被用作 CMS 和 API。您可以在这里查看他们的演示:http://bocoup.com/community/presentations/wordpress-in-weird-places/
感谢您提供这个链接。非常有趣。
但是,我们真的需要为此使用多站点吗?我的意思是,WP 中的多站点工作方式似乎过于复杂,当你没有使用标准 WP 前端时。在我看来,直接为每个站点创建一个类别(可能还包括子类别),并使用 WP REST API 与前端进行通信,而不激活多站点,会更容易。我在这里遗漏了什么吗?
不,我认为你没有遗漏任何东西。这篇文章的作者碰巧使用了多站点,因为它可能最适合他的需求,但我相信你说的对,对于某些项目来说你不需要它。我不明白为什么你不能根据其类别从不同站点获取帖子。这只是取决于它是否是您项目的一个足够解决方案。
我一直将 WordPress 作为 API 使用,并为它创建了自己的 API 插件。它运行良好,并且减少了很多服务器负载。我遇到了一些身份验证问题,但你可以通过一些修改将其添加到核心系统中,它运行起来很棒。
一个“模块化”的 WordPress 会很棒。想象一下,你可以将 WordPress 的逻辑、后端和前端部分(你可能可以将其分解成更多部分)作为独立的模块。我个人很喜欢 WordPress 的后端。如果我能够简单地将该后端插入我的项目中,而不必使用其他任何 WordPress 部分,那将很棒。或者,也许你已经有了后端,只是喜欢 WordPress 处理逻辑的方式。你可以将逻辑模块插入你的项目中,并使其与你的后端一起工作,而无需安装整个 WordPress。
是的,如果人们可以只选择后台,而无需使用专用于前端/主题/插件的代码,这将非常有用。但这并不是 WordPress 的常见用法(至少现在还不是)。
我以为我有一个天才的想法,就是想到这样做,但当然,有些人比我更早地想到它,比如这里的 Eduardo。我很想和他聊聊这个话题,了解更多。
有趣的文章…我已经使用 WordPress 作为 内容管理系统 进行工作一年了。使用 API 似乎比使用前端更容易。