有了前面两篇内容的铺垫,我们来聊聊 WordPress 作为 CMS / BaaS 服务使用时绕不开的问题,API 调用。

这篇内容同样的,会尽量少贴代码,简单的讲清楚一件事,降低阅读负担。

写在前面

首先,我们需要进行清晰的名词定义,这里指的 “API 调用”是能够通过外部程序访问的 WordPress API 可编程接口,而非 WordPress 暴露给内部生态系统中的主题、插件工具开发者使用的 “内部 API”。

WordPress 团队主要提供过两种 WordPress 公开 API 调用方案。

WordPress 命令行工具

2011 年末,官方推出了 WP-CLI,一个用于与 WordPress 网站交互和进行管理的命令行工具。十年过去,我们可以在 WordPress 项目中的 WP CLI 页面看到它不仅仅还在更新,支持了各种各样的功能,还有持续围绕它的活动(Hack Day、极客日)。官方更是为它单独制作了一个 WP CLI 官方网站,希望让更多的人了解、安装和使用它。当然,GitHub 上的开源项目 wp-cli/wp-cli 的更新也非常的规范和持续,显得项目相对比较可靠。

WordPress REST API

另外一种,则是 REST API,使用通用的 JSON 格式来与 WordPress 应用进行数据交互。自 2017 年发布的 WordPress 4.7.1 版本引入,被持续更新到 WordPress 5.6 版本。有许多知名的网站使用了这种方式,包括 WordPress.com、Wired、TechCrunch、The New York Times。其实,国内也有不少基于 WordPress API 重新封装的内容网站,或者内部发布系统,还有一些网站基于它的架子从小做到了上市,毕竟它是全球使用量最大的 CMS 方案。

项目同样在 GitHub 上进行了开源 wp-cli/restful,隶属于上面 WP CLI 分组中。可惜的是,在 2022 年,有一个社区的用户向官方提问这个项目是否还在维护,得到的答复是否定的,项目不再积极维护,不过官方说社区用户依旧可以提交 PR 来做一些细微的改进。

与此同时,项目的官方网站 https://wp-api.org 网站证书过期,网站被设置自动重定向到 WordPress 文档页面,原始网站仅留存了同样证书过期的日语版网站

不过,庆幸的是,在 WordPress 最新发布的 6.5.0 版本变更记录中,依旧对 REST API 功能做了保留和支持。社区中也依旧有许多方便的可以调用的 SDK 方案和资料参考。

让我们分别来聊聊这两种 API 方案的使用方法和细节注意事项。

方案一:WP CLI 的使用

WP CLI 官方网站 中,我们能够得到 WP CLI 的下载、安装方法。

# 下载 CLI 程序
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar

# 验证程序正确
php wp-cli.phar --info

# 赋予执行权限,完成设置
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp

接着,我们使用 wp cli 就能够简明扼要的看到能使用它来干什么了:

# wp cli
usage: wp cli alias <command>
   or: wp cli cache <command>
   or: wp cli check-update [--patch] [--minor] [--major] [--field=<field>] [--fields=<fields>] [--format=<format>]
   or: wp cli cmd-dump 
   or: wp cli completions --line=<line> --point=<point>
   or: wp cli has-command <command_name>...
   or: wp cli info [--format=<format>]
   or: wp cli param-dump [--with-values] [--format=<format>]
   or: wp cli update [--patch] [--minor] [--major] [--stable] [--nightly] [--yes] [--insecure]
   or: wp cli version 

See 'wp help cli <command>' for more information on a specific command.

不过,倘若你想要在一般的容器环境(非 Rootless Docker)运行,那么你将会收到类似下面的错误日志。

Error: YIKES! It looks like you're running this as root. You probably meant to run this as the user that your WordPress installation exists under.

If you REALLY mean to run this as root, we won't stop you, but just bear in mind that any code on this site will then have full control of your server, making it quite DANGEROUS.

If you'd like to continue as root, please run this again, adding this flag:  --allow-root

If you'd like to run it as the user that this site is under, you can run the following to become the respective user:

    sudo -u USER -i -- wp <command>

在 GitHub 社区中,曾经有过关于它的讨论,从 2017 年开始,社区就在推荐大家使用 alias 功能重写 wp 命令,来将 --allow-root 参数添加到真实运行的命令中。

不过,其实在 2020 年的一个提交中,就有人支持了从环境变量设置这个参数,解决了在 Docker 容器环境中的使用体验问题,毕竟在 Docker 环境中,每次都额外指定 --allow-root 还是很麻烦的。

所以,我们可以在封装镜像 Dockerfile 的时候,指定下面的环境变量,来让 wp cli 始终丝滑可用。

ENV WP_CLI_ALLOW_ROOT=1

有了 wp cli 之后,我们就能够对 WordPress 资源进行 CRUD 了。比如,你想列举所有的文章,只需要执行下面的命令:

# wp post list

+----+--------------+-------------+---------------------+-------------+
| ID | post_title   | post_name   | post_date           | post_status |
+----+--------------+-------------+---------------------+-------------+
| 5  | 一篇新的内容   | 5           | 2024-04-17 13:58:49 | publish     |
| 1  | 世界,您好!   | hello-world | 2024-04-17 13:53:42 | publish     |
+----+--------------+-------------+---------------------+-------------+

至于,增删改,和操作其他的资源,比如图片或者链接,或者更新具体设置,参考这个 WP CLI 命令的在线文档 即可。

方案二:WP REST API

虽然上文中提到了 WP REST API 当前的窘况,但好在目前 6.5.0 版本中,官方还是对它进行了支持,虽然没有明确文档告知用户该如何使用(应该是暂时减少支持工作消耗的开发同学的精力)。

在核心文件 wp-includes/functions.php 中,我们能够看到新增的函数 wp_is_serving_rest_request

function wp_is_serving_rest_request() {
	return defined( 'REST_REQUEST' ) && REST_REQUEST;
}

这里逻辑非常简单,根据用户是否明确定义 REST_REQUEST 常量,并且常量的取值为“真”,来开启 WP REST API 能力。

那么,我们就只需要在 wp-config.phpwp-config-docker.php (如果你使用 Docker 运行)中添加下面的代码即可:

# Enable WP REST API, by @soulteary
if ( ! defined( 'REST_REQUEST' ) ) {
	define('REST_REQUEST', true)
}

当我们添加完毕代码后,访问 http://localhost:8080/?rest_route=/wp/v2/posts 就能够看到以 JSON 格式展示的网站文章数据啦,比如:

[{"id":1,"date":"2024-04-17T13:53:42","date_gmt":"2024-04-17T05:53:42","guid":{"rendered":"http://localhost:8080/?p=1"},"modified":"2024-04-17T13:53:42","modified_gmt":"2024-04-17T05:53:42","slug":"hello-world","status":"publish","type":"post","link":"http://localhost:8080/?p=1","title":{"rendered":"世界,您好!"},"content":{"rendered":"\\n<p>欢迎使用 WordPress。这是您的第一篇文章。编辑或删除它,然后开始写作吧!</p>\\n","protected":false},"excerpt":{"rendered":"<p>欢迎使用 WordPress。这是您的第一篇文章。编辑或删除它,然后开始写作吧!</p>\\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"_links":{"self":[{"href":"http://localhost:8080/index.php?rest_route=/wp/v2/posts/1"}],"collection":[{"href":"http://localhost:8080/index.php?rest_route=/wp/v2/posts"}],"about":[{"href":"http://localhost:8080/index.php?rest_route=/wp/v2/types/post"}],"author":[{"embeddable":true,"href":"http://localhost:8080/index.php?rest_route=/wp/v2/users/1"}],"replies":[{"embeddable":true,"href":"http://localhost:8080/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1"}],"version-history":[{"count":0,"href":"http://localhost:8080/index.php?rest_route=/wp/v2/posts/1/revisions"}],"wp:attachment":[{"href":"http://localhost:8080/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http://localhost:8080/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1"},{"taxonomy":"post_tag","embeddable":true,"href":"http://localhost:8080/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1"}],"curies":[{"name":"wp","href":"https://api.w.org/{rel}","templated":true}]}}]

当然,如果我们在 WordPress 后台中设置了任意类型的“固定链接”,开启了链接重写功能,我们的访问地址就可以改成更好看一些的 http://localhost:8080/wp-json/wp/v2/posts 啦。

当然,在项目的 GitHub 社区里,我们能够找到许多 SDK 封装,选择你喜欢的语言进行调用封装即可,这样能够比直接进行调用,更简单、也更好维护程序逻辑。

好啦,到这里为止,我们了解了如何使用 API 的方式来访问 WordPress,接下来,我们来开始进阶使用。

保护你的 API 接口

我们分别来针对两种方案来聊聊 API 使用保护的问题。

WP CLI 的安全加固

这个方案的使用场景不论是 “管理员在 WP 安装环境敲命令行” 或者 “远程控制软件执行服务器命令”,都需要 “WordPress 安装环境” 正确安装和配置了 “WP CLI”,并且有权限执行命令。

所以,相对安全的方案策略就是:

  • 只在内部管理环境中进行 WP CLI 的安装,不在生产环境安装部署,确保 wp-config.php 能够访问要管理的数据库即可
  • 如果使用程序封装 WP CLI 为远程可调用命令,做好鉴权
  • 严格控制可执行 WP CLI 命令的用户权限,限制必要用户可访问执行
  • 如果是 SQLite 方案的 WordPress,则控制能够访问 WP 环境的用户即可

因为工具的使用有前置依赖,所以做安全策略的时候也就相对简单。

WP REST API 的安全加固

相比较 WP CLI,因为提供了 HTTP 访问,所以 WP REST API 的安全加固就相对麻烦一些。

不过,有一部分 WP CLI 的策略是可以借鉴的。

如果你是使用容器运行的 WordPress 实例的话,那么其实可以更简单一些,即不直接对外暴露端口,使用容器进行组网,来限制 WordPress 只能够被和他一起虚拟网络中的应用访问,来杜绝一些基础的风险。

提升你的服务性能

既然标题提到了 BaaS 化,那么少不了做为 API 服务被大量调用的场景,其实解决这个问题还是蛮简单的。

对于“读多写少”的场景,最简单的性能优化便是“加缓存”、“加机器(增加程序可用资源和程序进程、线程数)”、“静态化”。相关细节,很早之前在《第三届智源大会背后的那些事》的 “持续抗压”里提到过,就不展开了,都是老生常谈的话题。

当然,如果你没有使用前两篇内容中提到的 SQLite,还是使用了传统的关系型数据库,在当前固态硬盘遍地走的时代,哪怕购入一台 2C4G*2 低配置的支持高并发架构的云数据库(不贴链接了,避免被和谐,关键词“高并发架构”),支持的并发数量也能到 10万+。

除此之外,即便服务还有 CMS 属性,如果我们就是常规程序写入(包括采集)和调用(更新内容版本),做好批量写入队列,不去短时间(秒)内疯狂 JOIN 表查询或更新,想跑挂现在的数据库,还是挺困难的。

而如果你使用前两篇文章中提到的 SQLite 方案,相信你此时一定惊叹过了本地的 WordPress 原来可以这么快。以及在隐隐担忧使用这个方案做并发写入的时候,是否会有风险。

其实,在 2010 年的 SQLite 3.7.0 版本,官方就增加了 WAL 模式,用来支持并发写入。写这篇文章的时候,我正在筹划向官方提交一个新的 PR(#102),用来提供一个选项,支持 WAL 模式的激活,让这个方案下的 WordPress 写入性能变的更强。

最后

好啦,这篇文章里,我们聊完了 API 相关的问题,一款诞生和维护了 21 年的老牌软件摇身一变轻量的可 API 调用的 BaaS 服务。

下一篇相关的文章里,我们聊聊怎么和 AI 工具一起用它。

–EOF