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 响应对象的最灵活方式。它接受三个参数:

  1. $object_type:对象的名称,作为字符串,或者是正在注册字段的对象名称数组。这可能是核心类型,如"post"、"terms"、"meta"、"user"或"comment",也可以是自定义文章类型的字符串名称。
  2. $attribute:字段的名称。此名称将用于定义响应对象中的键。
  3. $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。