使用范围:这些片段需要在 WordPress 已加载的插件等合适上下文中使用,分别演示不同注册方式,并不是应全部拼接执行的单一插件。不要重复定义同名函数,或把演示同一字段不同结构的代码同时启用。公开 REST 字段前,应确认数据适合公开,并按业务要求处理读取上下文、写入权限和校验。
WordPress REST API 的默认端点所返回的数据,旨在满足大多数站点和使用场景。不过,你经常仍会需要在各类对象的响应中访问或公开额外数据。
与 WordPress 的其他部分一样,REST API 也具有很强的可扩展性,以满足这些需求。本指南介绍如何使用 register_rest_field 和 register_meta 函数,向默认端点的响应添加额外数据。你可以借助它们,为 REST API 支持的任意对象类型添加字段。这些自定义字段可以同时支持读取和更新操作。
修改响应的重要注意事项
修改或删除核心 REST API 端点响应中的数据,可能破坏插件或 WordPress 核心的行为,应尽可能避免。
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 之前,将元数据字段设置为
show_in_rest 并使用 register_meta 注册时,会对指定对象类型中的所有对象生效。如果一种自定义文章类型显示某个元数据字段,其他自定义文章类型也会显示它。从 WordPress 4.9.8 开始,可以为 register_meta 指定 object_subtype 参数,将元数据键限制在特定文章类型中使用。在 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 的 WordPress 请求中执行字段注册。
示例
读写评论响应中的额外字段
<?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'
),
) );
} );
对象说明:原文紧接示例的解释把对象写成 posts,但代码实际注册的是 comment,本段应按评论响应理解。wp_update_comment() 返回 0 也可能只是数据未改变;原例使用 false === $ret 判断失败,不应随意改为把所有假值都视作失败。详见 函数返回值说明。
此示例向评论响应添加一个名为 karma 的字段。之所以能够实现,是因为 comment_karma 字段已经存在,但核心没有使用它。请注意,真正实现评论 karma 功能时,需要使用单独的端点。
这是一个基础示例。请根据具体字段,认真考虑所需的权限检查和错误处理。
register_rest_field 的工作方式
REST API 基础设施在内部使用全局变量 $wp_rest_additional_fields,保存要添加到各类对象中的响应字段。REST API 提供了 register_rest_field 辅助函数,用于向这个全局变量添加定义。为了保持向前兼容,应避免直接修改该全局变量。
对于每一种对象类型——文章、用户、分类项、评论等——$wp_rest_additional_fields 都包含一个字段定义数组,其中保存了读取或更新字段值的回调。
在 REST API 中使用已注册的元数据
register_meta 函数简化了为特定对象类型定义元数据字段的过程。注册新元数据键时,设置 'show_in_rest' => true,即可通过 REST API 访问该键。
在文章响应中读写文章元数据字段
<?php
// The object type. For custom post types, this is 'post';
// for custom comment types, this is 'comment'. For user meta,
// this is 'user'.
$object_type = 'post';
$meta_args = array( // Validate and sanitize the meta value.
// Note: currently (4.7) one of 'string', 'boolean', 'integer',
// 'number' must be used as 'type'. The default is 'string'.
'type' => 'string',
// Shown in the schema for the meta key.
'description' => 'A meta key associated with a string meta value.',
// Return a single value of the type.
'single' => true,
// Show in the WP REST API response. Default: 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' => 'A meta key associated with a string meta value.',
'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',
),
),
),
)
);
校注:原文这里注册的是 version,前面的 JSON 却提交 release。两者需统一;文末“修正示例”给出使用 release 的版本,其他属性模式保持原样。
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',
)
);
校注:此原例展示元数据默认值,但没有启用 show_in_rest,单独照抄不会因此公开 REST 字段。文末补充该选项;product 类型本身仍须启用 REST API 并支持 custom-fields。默认值是字符串 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() 方法向响应添加自定义链接。该方法接受三个参数:链接关系、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,在生成响应时通过 CURIE 转换为 wp:term。
add_link() 的第三个参数是自定义属性列表。embeddable 属性用于允许把链接资源放入响应的 _embedded 部分,前提是请求使用了 _embed 查询参数。如果同一种关系添加了多个链接,嵌入响应的顺序与链接添加顺序一致。
<?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(CURIE)的支持。这样就可以使用简短得多的标识符引用链接,而不必使用可能很长的完整 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;
}
校注:这里仅定义回调,尚未将它挂到 rest_response_link_curies。文末给出带钩子注册的版本;两份同名函数只保留一份。
这会在 API 响应中把链接从 https://api.mypluginurl.com/my_link 转换为 my_plugin:my_link。使用 WP_REST_Response::add_link() 时仍须使用完整 URL。
修正示例:统一字段名并补全注册条件
以下三个片段是针对前述问题的单独修正,不属于原文代码。它们保留原接口和数据结构,只修改已指出的差异;PHP 与真实 WordPress 请求的联调仍需在隔离测试站完成。
额外属性:统一使用 release 字段
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',
),
),
'additionalProperties' => array(
'type' => 'number',
),
),
),
)
);
默认值:同时启用 REST 暴露
register_post_meta(
'product',
'price',
array(
'single' => true,
'type' => 'string',
'default' => '0.00',
'show_in_rest' => true,
)
);
CURIE:挂载已定义的回调
<?php
function my_plugin_prefix_register_curie( $curies ) {
$curies[] = array(
'name' => 'my_plugin',
'href' => 'https://api.mypluginurl.com/{rel}',
'templated' => true,
);
return $curies;
}
add_filter( 'rest_response_link_curies', 'my_plugin_prefix_register_curie' );
相关接口:元数据注册与公开范围、CURIE 注册钩子。后者还提醒:有些客户端不能正确展开 CURIE,新增缩写可能影响兼容性,应检查消费方。











暂无评论内容