WordPress REST API 的默认端点设计为默认返回适用于大多数站点和用例的数据,但在许多情况下,您可能需要在各种对象类型的响应中访问或暴露额外数据。
与 WordPress 的其他部分一样,REST API 被设计为高度可扩展,以满足这些需求。本指南详细介绍了如何使用 register_rest_field 和 register_meta 函数向默认端点的响应添加额外数据。您可以使用这些函数向 REST API 支持的任何对象类型添加字段。这些自定义字段可以支持获取和更新操作。
关于修改响应的重要说明
更改或删除核心 REST API 端点响应中的数据可能会破坏插件或 WordPress 核心行为,应尽可能避免这样做。
API 在响应中暴露了许多字段,包括您可能不需要或不适合您网站运作方式的字段。虽然修改或删除 REST API 响应中的字段很有诱惑力,但这会导致期望标准响应的 API 客户端出现问题。这包括移动客户端、帮助您管理站点的第三方工具或 wp-admin 本身。
您可能只需要少量数据,但重要的是要记住,API 旨在为所有客户端提供接口,而不仅仅是您正在开发的功能。更改响应是危险的。
添加字段并不危险,因此如果您需要修改数据,最好复制该字段并放入您的修改数据。从不鼓励删除字段;如果您需要获取更小的数据子集,请使用 _fields 参数 或使用上下文。
如果您必须从现有上下文中删除字段,请确保行为是主动选择的,例如通过提供自定义查询参数来触发字段删除。
API 无法阻止您更改响应,但代码结构强烈不鼓励这样做。内部而言,字段注册由过滤器驱动,如果您绝对没有其他选择,可以使用它们。
使用 register_rest_field 与 register_meta
有两种方法可用于向 WordPress REST API 响应添加数据:register_rest_field 和 register_meta。
register_rest_field 可用于向任何 REST API 响应添加任意字段,并且可以用于通过 API 读取和写入数据。要注册新的 REST 字段,您必须提供自己的回调函数来获取或设置字段的值,以及手动指定字段自己的架构定义。
register_meta 用于白名单化现有的自定义元数据值以便通过 REST API 访问。通过将元数据字段的 show_in_rest 参数设置为 true,该字段的值将在端点响应的 .meta 键中暴露,WordPress 将负责设置读取和写入该元数据键的回调。这比 register_rest_field 简单得多,但有一个注意事项:
在 WordPress 4.9.8 之前,使用
register_meta 设置 show_in_rest 的元数据字段会为给定类型的所有对象注册。如果一个自定义文章类型显示元数据字段,所有自定义文章类型都将显示该元数据字段。自 WordPress 4.9.8 起,可以使用带有 object_subtype 参数的 register_meta,允许将元数据键的使用范围缩小到特定的文章类型。在 WordPress 5.3 之前,
register_meta 仅支持标量值(string、integer、number 和 boolean)。WordPress 5.3 增加了对 object 和 array 类型的支持。向 API 响应添加自定义字段
使用 register_rest_field
register_rest_field 函数是将字段添加到 REST API 响应对象的最灵活方式。它接受三个参数:
$object_type:对象的名称,作为字符串,或者是正在注册字段的对象名称数组。这可能是核心类型,如"post"、"terms"、"meta"、"user"或"comment",也可以是自定义文章类型的字符串名称。$attribute:字段的名称。此名称将用于定义响应对象中的键。$args:一个包含键的数组,用于定义用于检索字段值的回调函数('get_callback')、更新字段值的回调函数('update_callback')以及定义其架构('schema')。
$args 数组的每个键都是可选的,但如果未使用,则不会添加该功能。这意味着您可以指定用于读取值的回调函数,并省略更新回调以将字段设为只读(如果需要)。
字段应在 rest_api_init 动作中注册。使用此动作而不是 init 可防止在不使用 REST API 的请求期间进行字段注册。
示例
在评论响应中读写额外字段
<?php
add_action( 'rest_api_init', function () {
register_rest_field( 'comment', 'karma', array(
'get_callback' => function( $comment_arr ) {
$comment_obj = get_comment( $comment_arr['id'] );
return (int) $comment_obj->comment_karma;
},
'update_callback' => function( $karma, $comment_obj ) {
$ret = wp_update_comment( array(
'comment_ID' => $comment_obj->comment_ID,
'comment_karma' => $karma
) );
if ( false === $ret ) {
return new WP_Error(
'rest_comment_karma_failed',
__( 'Failed to update comment karma.' ),
array( 'status' => 500 )
);
}
return true;
},
'schema' => array(
'description' => __( 'Comment karma.' ),
'type' => 'integer'
),
) );
} );
此示例说明了向帖子响应添加名为 karma 的字段。它之所以有效,是因为 comment_karma 字段存在,但未由核心使用。请注意,实际的评论 karma 实现需要使用单独的端点。
这是一个基本示例;请仔细考虑您的特定字段可能需要哪些权限检查或错误处理。
register_rest_field 的工作原理
全局变量 $wp_rest_additional_fields 由 REST API 基础设施内部使用,用于保存要添加到每个对象类型的响应字段。REST API 提供 register_rest_field 作为向此全局变量添加内容的实用函数。应避免直接添加到此全局变量以确保向前兼容性。
对于每种对象类型——文章、用户、术语、评论等——$wp_rest_additional_fields 包含一个字段定义数组,其中包含用于检索或更新字段值的回调。
在 REST API 中使用已注册的元数据
register_meta 函数简化了为特定对象类型定义元数据字段的过程。在注册新元数据键时设置 'show_in_rest' => true,该键将通过 REST API 访问。
在文章响应中读写文章元数据字段
<?php
// 对象类型。对于自定义文章类型,这是'post';
// 对于自定义评论类型,这是'comment'。对于用户元数据,
// 这是'user'。
$object_type = 'post';
$meta_args = array( // 验证和清理元数据值。
// 注意:当前(4.7)必须使用'string'、'boolean'、'integer'、
// 'number'之一作为'type'。默认为'string'。
'type' => 'string',
// 显示在元数据键的架构中。
'description' => '与字符串元数据值关联的元数据键。',
// 返回该类型的单个值。
'single' => true,
// 显示在 WP REST API 响应中。默认值:false。
'show_in_rest' => true,
);
register_meta( $object_type, 'my_meta_key', $meta_args );
此示例展示了如何允许读取和写入文章元数据字段。这将允许通过向 wp-json/wp/v2/posts/<post-id> 发送 POST 请求来更新该字段,或通过向 wp-json/wp/v2/posts/ 发送 POST 请求在创建文章时创建该字段。
请注意,对于在自定义文章类型上注册的元数据字段,文章类型必须具有 custom-fields 支持。否则,元数据字段不会出现在 REST API 中。
特定文章类型的元数据
WordPress 4.9.8 增加了对使用 register_post_meta 和 register_term_meta 函数为特定文章类型或分类法注册元数据的支持。它们遵循与 register_meta 相同的规则,但接受文章类型或分类法作为第一个参数,而不是对象类型。以下代码将注册上述 my_meta_key 示例,但仅针对 page 自定义文章类型。
$meta_args = array(
'type' => 'string',
'description' => '与字符串元数据值关联的元数据键。',
'single' => true,
'show_in_rest' => true,
);
register_post_meta( 'page', 'my_meta_key', $meta_args );
对象元数据类型
WordPress 5.3 增加了对在注册元数据时使用 object 类型 的支持。重要的是,object 指的是 JSON 对象,这在 PHP 中相当于关联数组。
当注册 object 元数据时,仅将 type 设置为 object 是不够的,你还需要告知 WordPress 允许哪些属性。这是通过注册元数据时编写 JSON Schema 来实现的。
例如,以下代码示例注册了一个名为"release"的文章元数据字段,它接受给定的 JSON 数据。
{
"meta": {
"release": {
"version": "5.2",
"artist": "Jaco"
}
}
}
register_post_meta(
'post',
'release',
array(
'single' => true,
'type' => 'object',
'show_in_rest' => array(
'schema' => array(
'type' => 'object',
'properties' => array(
'version' => array(
'type' => 'string',
),
'artist' => array(
'type' => 'string',
),
),
),
),
)
);
请注意,show_in_rest 变成了一个数组而不是 true,并且为 schema 键指定了 Json Schema。然后在 properties 数组中列出每个属性。至少,每个属性必须指定一个 type,但也可以使用任何 rest_validate_value_from_schema 理解的 JSON Schema 关键字。
额外属性
默认情况下,属性列表是一个严格的白名单。如果请求中包含未列出的属性,REST API 将返回错误:your_property is not a valid property of Object.。如果你事先不知道属性名称,可以使用 additionalProperties 关键字。additionalProperties 接受一个用于验证未知属性的 JSON Schema。例如,要强制要求任何额外属性都是数字,可以使用以下代码。
{
"meta": {
"release": {
"version": "5.2",
"artist": "Jaco",
"unknown_field": 5.3
}
}
}
register_post_meta(
'post',
'version',
array(
'single' => true,
'type' => 'object',
'show_in_rest' => array(
'schema' => array(
'type' => 'object',
'properties' => array(
'version' => array(
'type' => 'string',
),
'artist' => array(
'type' => 'string',
),
),
'additionalProperties' => array(
'type' => 'number',
),
),
),
)
);
可以将 additionalProperties 设置为 true 以允许任何格式的未知属性,但这不推荐。
数组元数据类型
WordPress 5.3 还增加了对使用 array 类型 的支持。重要的是,array 指的是 JSON 数组,这在 PHP 中相当于索引数组。
当注册 array 元数据时,仅将 type 设置为 array 是不够的,你需要告知 WordPress 数组中项目的预期格式。这是通过注册元数据时编写 JSON Schema 条目来实现的。
如果你不提供此值,register_meta 将返回 false 并发出以下警告:When registering an "array" meta type to show in the REST API, you must specify the schema for each array item in "show_in_rest.schema.items".
以下代码示例注册了一个名为"projects"的文章元数据字段,其中包含项目名称列表。它接受给定的 JSON 数据。
{
"meta": {
"projects": [
"WordPress",
"BuddyPress"
]
}
}
register_post_meta(
'post',
'projects',
array(
'single' => true,
'type' => 'array',
'show_in_rest' => array(
'schema' => array(
'type' => 'array',
'items' => array(
'type' => 'string',
),
),
),
)
);
请注意,再次地,show_in_rest 变成了一个数组而不是 true,并且为 schema 键指定了 JSON Schema。items 关键字用于定义用于验证每个数组成员的 JSON Schema。它可以是像 string 这样的标量类型,也可以是像 object 这样的复杂类型。
例如,要接受给定的 JSON 数据,将使用以下元数据注册。
{
"meta": {
"projects": [
{
"name": "WordPress",
"website": "https://wordpress.org"
},
{
"name": "BuddyPress",
"website": "https://buddypress.org"
}
]
}
}
register_post_meta(
'post',
'projects',
array(
'single' => true,
'type' => 'array',
'show_in_rest' => array(
'schema' => array(
'items' => array(
'type' => 'object',
'properties' => array(
'name' => array(
'type' => 'string',
),
'website' => array(
'type' => 'string',
'format' => 'uri',
),
),
),
),
),
)
);
array 类型强制执行数组键是从 0 开始的连续整数。该数组将使用 array_values 重新索引。非单值元数据
非单值元数据字段针对每个对象拥有一个值数组,而不是每个对象一个值。这些值中的每一个都存储在元数据表的单独行中。
array 和 object 类型也可与非单值元数据字段一起使用。例如,如果前面的"release"元数据键将 single 设置为 false,则可以接受以下 JSON 数据。
{
"meta": {
"release": [
{
"version": "5.2",
"artist": "Jaco"
},
{
"version": "5.1",
"artist": "Betty"
}
]
}
}
这将导致两个元数据数据库行。第一行包含 { "version": "5.2", "artist": "Jaco" },第二行包含 { "version": "5.1", "artist": "Betty" }。
类似地,如果"projects"示例将 single 设置为 false,则以下数据将被接受。
{
"meta": {
"projects": [
[
"WordPress",
"BuddyPress"
],
[
"bbPress"
]
]
}
}
这将导致两个元数据数据库行。第一行包含 [ "WordPress", "BuddyPress" ],第二行包含 [ "bbPress" ]。
无效的存储值
如果现有元数据字段的值无法与注册的类型和模式进行验证,则该元数据字段的值将返回为 null。如果在执行更新请求时将此 null 值传递回 API,你将收到 rest_invalid_stored_value 错误:The %s property has an invalid stored value, and cannot be updated to null.。要修复此问题,请使用有效值更新元数据键,或从请求中省略该属性。
默认元数据值
WordPress 5.5 增加了对指定元数据默认值的官方支持,即在没有定义值的情况下。例如,使用此代码片段,如果尚未设置值,则 price 元数据字段将在 REST API 响应中设置为 0.00。
register_post_meta(
'product',
'price',
array(
'single' => true,
'type' => 'string',
'default' => '0.00',
)
);
向 API 响应添加链接
WordPress 生成与查询资源相关联的链接列表,以便更容易导航到相关资源。
{
"_links": {
"self": [
{
"href": "https://make.wordpress.org/core/wp-json/wp/v2/posts/28312"
}
],
"collection": [
{
"href": "https://make.wordpress.org/core/wp-json/wp/v2/posts"
}
],
"author": [
{
"embeddable": true,
"href": "https://make.wordpress.org/core/wp-json/wp/v2/users/8670591"
}
],
"replies": [
{
"embeddable": true,
"href": "https://make.wordpress.org/core/wp-json/wp/v2/comments?post=28312"
}
],
"wp:term": [
{
"taxonomy": "category",
"embeddable": true,
"href": "https://make.wordpress.org/core/wp-json/wp/v2/categories?post=28312"
},
{
"taxonomy": "post_tag",
"embeddable": true,
"href": "https://make.wordpress.org/core/wp-json/wp/v2/tags?post=28312"
}
]
}
}
来自 Make.WordPress.org 文章的链接示例
虽然这些链接会出现在 JSON 响应对象的 _links 属性下,但它们不会存储在 WP_REST_Response::$data 中,也无法通过 WP_REST_Response::get_data() 访问。相反,服务器会在回显响应数据之前将链接数据附加到响应中。
可以使用 WP_REST_Response::add_link() 方法向响应添加自定义链接。该方法接受三个参数:链接关系(link relation)、URL,以及可选的链接属性列表。例如,要添加 author 和 wp:term 链接。
<?php
$response->add_link( 'author', rest_url( "/wp/v2/users/{$post->post_author}" ) );
$response->add_link( 'https://api.w.org/term', add_query_arg( 'post', $post->ID, rest_url( "/wp/v2/{$tax_base}" ) ) );
链接关系必须是 IANA 注册的链接关系 之一,或者是您控制的 URI。
author 是一个已注册的链接关系,描述为“上下文的作者”,我们用它来指向撰写该文章的 WordPress 用户。目前没有描述与文章关联术语的链接关系,因此 WordPress 使用 https://api.w.org/term` URL。在使用 CURIE 生成响应时,该 URL 会被转换为wp:term`。
add_link() 的第三个参数是自定义属性列表。embeddable 属性可用于在使用 _embed 查询参数时,使链接的资源出现在响应的 _embedded 部分中。如果添加了多个具有相同关系的链接,则嵌入响应的顺序将与添加链接的顺序一致。
<?php
$response->add_link( 'author', rest_url( "/wp/v2/users/{$post->post_author}" ), array(
'embeddable' => true,
) );
$response->add_link( 'author', rest_url( "/wp/v2/users/{$additional_author}" ), array(
'embeddable' => true,
) );
多作者文章链接的实现示例。
{
"_links": {
"author": [
{
"embeddable": true,
"href": "https://yourwebsite.com/wp-json/wp/v2/users/1"
},
{
"embeddable": true,
"href": "https://yourwebsite.com/wp-json/wp/v2/users/2"
}
]
},
"_embedded": {
"author": [
{
"id": 1,
"name": "Primary Author"
},
{
"id": 2,
"name": "Secondary Author"
}
]
}
}
添加链接的顺序会被保留。
注册 CURIE
WordPress 4.5 版本引入了对紧凑 URI(CURIEs)的支持。这使得可以使用比完整 URL(可能非常长)更简单的标识符来引用链接。
使用 rest_response_link_curies 过滤器注册 CURIE。例如:
<?php
function my_plugin_prefix_register_curie( $curies ) {
$curies[] = array(
'name' => 'my_plugin',
'href' => 'https://api.mypluginurl.com/{rel}',
'templated' => true,
);
return $curies;
}
这会将 API 响应中的链接 URL 从 https://api.mypluginurl.com/my_link` 转换为my_plugin:my_link。在使用WP_REST_Response::add_link() ` 添加链接时,仍需使用完整 URL。