修改回复

最后更新于:2021-11-29 12:19:39

Modifying Responses

GeChiUI REST API的默认端点旨在默认返回数据,这些数据为大多数网站和用例提供数据,但通常情况下,您需要访问或公开各种对象类型的响应中的其他数据。

与GeChiUI的其余部分一样,REST API旨在高度可扩展,以满足这些需求。本指南详细介绍了如何使用register_rest_fieldregister_meta函数向默认端点的响应添加其他数据。您可以使用这些函数向REST API支持的任何对象类型添加字段。这些自定义字段可以同时支持获取和更新操作。

关于更改回复的重要提示

警报:
从核心REST API端点响应中更改或删除数据可能会破坏插件或GeChiUI核心行为,应尽可能避免。

API公开了API响应上的许多字段,包括您可能不需要或可能与您的网站工作方式不匹配的字段。虽然修改或删除REST API响应中的字段很诱人,但这会导致期望标准响应的API客户端出现问题。这包括移动客户端、帮助您管理网站的第三方工具或gc管理员本身。

您可能只需要少量数据,但重要的是要记住,API是关于向所有客户端公开接口的,而不仅仅是您正在开发的功能。改变反应是危险的。

添加字段并不危险,因此,如果您需要修改数据,最好用修改后的数据复制字段。永远不要鼓励删除字段;如果您需要返回较小的子集数据,请使用_fields参数或处理上下文。

如果您必须从现有上下文中删除字段,则应确保该行为是选择加入的,例如提供自定义查询参数来触发字段删除。

API无法阻止您更改响应,但代码的结构强烈阻止您这样做。在内部,字段注册由过滤器提供动力,如果您绝对没有其他选择,可以使用这些过滤器。

Using register_rest_field vs register_meta

有两种方法可用于向GeChiUI REST API响应添加数据,register_rest_fieldregister_meta

register_rest_field可用于向任何REST API响应添加任意字段,并可用于使用API读取和写入数据。要注册新的REST字段,您必须提供自己的回调函数来获取或设置字段的值,并手动为该字段指定自己的结构定义。

register_meta用于将现有自定义元值列入白名单,以便通过REST API访问。通过将元字段的show_in_rest参数设置为true,该字段的值将暴露在端点响应中的.meta键上,GeChiUI将处理设置对该元键的读写回调。这比register_rest_field简单得多,只有一个注意事项:

警报:
Prior to GeChiUI 4.9.8, meta fields set to show_in_rest using register_meta are registered for all objects of a given type. If one custom post type shows a meta field, all custom post types will show that meta field. As of GeChiUI 4.9.8 it’s possible to use register_meta with the object_subtypeargument that allows one to reduce the usage of the meta key to a particular post type.

在GeChiUI 5.3之前,register_meta只能支持标量值(stringintegernumberboolean)。GeChiUI 5.3增加了对object和数array类型的支持。

向API响应添加自定义字段

Using register_rest_field

register_rest_field函数是向REST API响应对象添加字段的最灵活方式。它接受三个参数:

  1. $object_type:对象的名称,作为字符串,或注册字段的对象名称数组。这可能是“文章”、“术语”、“元”、“用户”或“评论”等核心类型,也可以是自定义文章类型的字符串名称。
  2. $attribute:字段的名称。此名称将用于定义响应对象中的密钥。
  3. $args:一个包含键的数组,该键定义了用于检索字段值(“get_callback”)、更新字段值(“update_callback”)和定义其结构(“结构”)的回调函数。

$args数组的每个键都是可选的,但如果不使用,将不会添加该功能。这意味着您可以指定一个回调函数来读取值,并省略更新回调,以便在需要时使该字段
只读。

字段应在rest_api_init操作中注册。使用此操作而不是init将防止在向GeChiUI提出不使用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 = gc_update_comment( array(
                'comment_ID'    => $comment_obj->comment_ID,
                'comment_karma' => $karma
            ) );
            if ( false === $ret ) {
                return new GC_Error(
                  'rest_comment_karma_failed',
                  __( 'Failed to update comment karma.' ),
                  array( 'status' => 500 )
                );
            }
            return true;
        },
        'schema' => array(
            'description' => __( 'Comment karma.' ),
            'type'        => 'integer'
        ),
    ) );
} );

This example illustrates adding a field called karma to the response for posts. It works because the comment_karma field exists, but is unused by core. Note that an actual implementation of comment karma would need to use a separate endpoint.

这是一个基本示例;仔细考虑您的特定字段可能需要哪些权限检查或错误处理。

register_rest_field 如何工作

REST API基础设施内部使用全局变量$gc_rest_additional_fields来保存要添加到每种对象类型的响应字段。REST API提供register_rest_field作为添加到此全局变量的实用程序函数。应避免直接添加全局变量,以确保前向兼容性。

对于每种对象类型——文章或用户、术语、评论等——$gc_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 GC REST API response. 
默认: false. 'show_in_rest' => true, ); register_meta( $object_type, 'my_meta_key', $meta_args );

这个例子展示了如何允许读取和写入文章元字段。这将允许通过POST请求将该字段更新为gc-json/gc/v2/posts/<post-id>或通过POST请求与文章一起创建到gc-json/gc/v2/posts/

请注意,对于在自定义文章类型上注册的元字段,文章类型必须支持custom-fields。否则,元字段将不会出现在REST API中。

特定于文章类型的元

GeChiUI 4.9.8增加了对使用register_post_metaregister_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 );

对象元类型

GeChiUI 5.3增加了对注册元时使用object类型的支持。重要的是,object引用JSON对象,这相当于PHP中的关联数组。

在注册object元时,将type设置为object是不够的,您还需要通知GeChiUI允许哪些属性。这是通过在注册元数据时编写JSON结构来实现的。

例如,以下代码示例注册一个名为“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',
                    ),
                ),
            ),
        ),
    )
);

Notice that show_in_rest becomes an array instead of true and a Json 结构 is specified for the schema key. Each property is then listed in the properties array. At a minimum, each property must specify a type, but any JSON 结构 keyword that rest_validate_value_from_schemaunderstands can be used as well.

其他属性

默认情况下,属性列表是一个严格的白名单。如果在请求中发送了未列出的属性,REST API将返回错误:your_property is not a valid property of Object.如果您提前不知道属性名称,可以使用additionalProperties关键字。additionalProperties接受用于验证未知属性的JSON结构。例如,为了强制要求任何其他属性是数字,可以使用以下代码。

{
  "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,以允许任何格式的未知属性,但不建议这样做。

数组元类型

GeChiUI 5.3还增加了对使用array类型的支持。重要的是,array指的是JSON数组,这相当于PHP中的数字数组。

在注册array元时,将type设置为数array是不够的,您需要通知GeChiUI数组中项目的预期格式是什么。这是通过在注册元数据时编写JSON结构条目来实现的。

如果您不提供此值,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".

以下代码示例注册了一个名为“项目”的后元字段,该字段包含项目名称列表。它接受给定的JSON数据。

{
  "meta": {
    "projects": [
      "GeChiUI",
      "BuddyPress"
    ]
  }
}
register_post_meta(
    'post',
    'projects',
    array(
        'single'       => true,
        'type'         => 'array',
        'show_in_rest' => array(
            'schema' => array(
                'type'  => 'array',
                'items' => array(
                    'type' => 'string',
                ),
            ),
        ),
    )
);

Notice that again show_in_rest becomes an array instead of true and a JSON 结构 is specified for the schema key.
items关键字用于定义JSON结构,以根据每个数组成员验证每个数组成员。它可以是string等标量类型,也可以是object等复杂类型。

例如,要接受给定的JSON数据,将使用以下元注册。

{
  "meta": {
    "projects": [
      {
        "name": "GeChiUI",
        "website": "https://gechiui.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',
                        ),
                    ),
                ),
            ),
        ),
    )
);

Alert:The array type enforces that the array keys are sequential integers starting from 0. The array will be reindexed using array_values.

非单元数据

非单个元字段每个对象有一个值数组,而不是每个对象一个值。这些值都存储在元表中的一行中。

arrayobject类型也可以与非单个元字段一起使用。例如,如果早期的“发布”元密钥已设置为false,则可以接受以下JSON数据。

{
  "meta": {
    "release": [
      {
        "version": "5.2",
        "artist": "Jaco"
      },
      {
        "version": "5.1",
        "artist": "Betty"
      }
    ]
  }
}

这将导致两行元数据库。第一个包含{ "version": "5.2", "artist": "Jaco" },第二个包含{ "version": "5.1", "artist": "Betty" }

同样,如果将以下数据设置为false,“项目”示例将被接受。

{
  "meta": {
    "projects": [
      [
        "GeChiUI",
        "BuddyPress"
      ],
      [
        "bbPress"
      ]
    ]
  }
}

这将导致两行元数据库。第一个包含[ "GeChiUI", "BuddyPress" ],第二个包含[ "bbPress" ]

存储值无效

如果元字段的现有值无法根据注册的类型和结构进行验证,则该元字段的值将作为空值返回。如果在进行更新请求时将该空值传回API,您将收到rest_invalid_stored_value错误:The %s property has an invalid stored value, and cannot be updated to null.要解决这个问题,请用有效值更新元密钥,或从请求中省略该属性。

默认元数据值

GeChiUI 5.5增加了官方支持,在没有定义值的情况下为元数据指定默认值。例如,使用此代码片段,如果值尚未确定,则REST API响应中price元字段将设置为0.00

register_post_meta(
     'product',
     'price',
     array(
         'single'  => true,
         'type'    => 'string',
         'default' => '0.00',
     )
 );

GeChiUI生成与查询资源关联的链接列表,以便于导航到相关资源。

{
 "_links": {
    "self": [
      {
        "href": "https://make.gechiui.org/core/gc-json/gc/v2/posts/28312"
      }
    ],
    "collection": [
      {
        "href": "https://make.gechiui.org/core/gc-json/gc/v2/posts"
      }
    ],
    "author": [
      {
        "embeddable": true,
        "href": "https://make.gechiui.org/core/gc-json/gc/v2/users/8670591"
      }
    ],
    "replies": [
      {
        "embeddable": true,
        "href": "https://make.gechiui.org/core/gc-json/gc/v2/comments?post=28312"
      }
    ],
    "gc:term": [
      {
        "taxonomy": "category",
        "embeddable": true,
        "href": "https://make.gechiui.org/core/gc-json/gc/v2/categories?post=28312"
      },
      {
        "taxonomy": "post_tag",
        "embeddable": true,
        "href": "https://make.gechiui.org/core/gc-json/gc/v2/tags?post=28312"
      }
    ]
  }
}

Make.GeChiUI.org文章的链接示例

虽然这些链接将显示在JSON响应对象的_links属性下,但它不存储在GC_REST_Response::$data也不通过GC_REST_Response::get_data()访问。相反,服务器将在响应数据回声之前将链接数据附加到响应中。

可以使用GC_REST_Response::add_link()方法将自定义链接添加到响应中。此方法接受三个参数,链接关系、URL,以及可选的链接属性列表。例如,添加authorgc:term链接。

<?php
$response->add_link( 'author', rest_url( "/gc/v2/users/{$post->post_author}" ) );

$response->add_link( 'https://api.w.org/term', add_query_arg( 'post', $post->ID, rest_url( "/gc/v2/{$tax_base}" ) ) );

链接关系必须是IANA的注册链接关系或您控制的URI。

author is a registered link relation described as “the context’s author”, we use that to point to the GeChiUI user who wrote the post. No link relation exists that describes the terms associated with a post, so GeChiUI uses the https://api.w.org/term URL. This is transformed to gc:term when generating the response by using a CURIE.

The third parameter of add_link() is a list of custom attributes. The embeddable attribute can be used to include the linked resource appears in the _embedded section of the repsonse when using the _embed query parameter. If multiple links are added with the same relation, the embedded responses will be in the same order the links were added in.

<?php
$response->add_link( 'author', rest_url( "/gc/v2/users/{$post->post_author}" ), array(
    'embeddable' => true,
) );
$response->add_link( 'author', rest_url( "/gc/v2/users/{$additional_author}" ), array(
    'embeddable' => true,
) );

链接到多作者文章的示例实现。

{
  "_links": {
    "author": [
      {
        "embeddable": true,
        "href": "https://yourwebsite.com/gc-json/gc/v2/users/1"
      },
      {
        "embeddable": true,
        "href": "https://yourwebsite.com/gc-json/gc/v2/users/2"
      }
    ]
  },
  "_embedded": {
    "author": [
      {
        "id": 1,
        "name": "Primary Author"
      },
      {
        "id": 2,
        "name": "Secondary Author"
      }
    ]
  }
}

添加的订单链接保持不变。

注册CURIE

GeChiUI 4.5版本引入了对紧凑型URI(CURIE)的支持。这使得通过比完整URL简单得多的标识符引用链接成为可能很容易很长的URL。

A CURIE is registered using the rest_response_link_curies filter. For example.

<?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。使用GC_REST_Response::add_link添加链接时,仍然必须使用完整的URL。