图式

概述

模式是告诉我们数据结构的元数据。大多数数据库都实现某种形式的模式,使我们能够以更结构化的方式推理我们的数据。WordPress REST API 使用JSON Schema来处理其数据的结构化。您可以在不使用模式的情况下实现端点,但您会错过很多东西。由您决定什么最适合您。

JSON

首先,让我们稍微谈谈 JSON。JSON 是一种类似于 JavaScript 对象的人类可读数据格式。JSON 代表 JavaScript 对象表示法。JSON 越来越受欢迎,似乎正在席卷数据结构的世界。WordPress REST API 使用一种特殊的 JSON 规范,称为 JSON 模式。要了解有关 JSON Schema 的更多信息,请查看JSON Schema 网站和这个更容易理解的 JSON Schema 介绍。模式为我们提供了许多好处:改进的测试、可发现性和整体更好的结构。让我们看一下 JSON 数据块。

{
    "shouldBeArray": 'LOL definitely not an array',
    "shouldBeInteger": ['lolz', 'you', 'need', 'schema'],
    "shouldBeString": 123456789
}

JSON 解析器将毫无问题地处理该数据并且不会抱怨任何事情,因为它是有效的 JSON。客户端和服务器对数据一无所知,也不知道他们只看到 JSON 会发生什么。通过实施模式,我们实际上可以简化我们的代码库。架构将有助于更好地构建我们的数据,以便我们的应用程序可以更轻松地推断我们与 WordPress REST API 的交互。WordPress REST API 不强制您使用模式,但鼓励您使用。有两种方法可以将模式数据合并到 API 中;资源模式和我们注册参数的模式。

资源模式

资源的模式指示特定对象存在哪些字段。当我们注册我们的路由时,我们还可以为路由指定资源模式。让我们看看一个简单的注释模式在 JSON 模式的 PHP 表示中可能是什么样子。

// Register our routes.
function prefix_register_my_comment_route() {
    register_rest_route( 'my-namespace/v1', '/comments', array(
        // Notice how we are registering multiple endpoints the 'schema' equates to an OPTIONS request.
        array(
            'methods'  => 'GET',
            'callback' => 'prefix_get_comment_sample',
        ),
        // Register our schema callback.
        'schema' => 'prefix_get_comment_schema',
    ) );
}

add_action( 'rest_api_init', 'prefix_register_my_comment_route' );

/**
 * Grabs the five most recent comments and outputs them as a rest response.
 *
 * @param WP_REST_Request $request Current request.
 */
function prefix_get_comment_sample( $request ) {
    $args = array(
        'number' => 5,
    );
    $comments = get_comments( $args );

    $data = array();

    if ( empty( $comments ) ) {
        return rest_ensure_response( $data );
    }

    foreach ( $comments as $comment ) {
        $response = prefix_rest_prepare_comment( $comment, $request );
        $data[] = prefix_prepare_for_collection( $response );
    }

    // Return all of our comment response data.
    return rest_ensure_response( $data );
}

/**
 * Matches the comment data to the schema we want.
 *
 * @param WP_Comment $comment The comment object whose response is being prepared.
 */
function prefix_rest_prepare_comment( $comment, $request ) {
    $comment_data = array();

    $schema = prefix_get_comment_schema();

    // We are also renaming the fields to more understandable names.
    if ( isset( $schema['properties']['id'] ) ) {
        $comment_data['id'] = (int) $comment->comment_ID;
    }

    if ( isset( $schema['properties']['author'] ) ) {
        $comment_data['author'] = (int) $comment->user_id;
    }

    if ( isset( $schema['properties']['content'] ) ) {
        $comment_data['content'] = apply_filters( 'comment_text', $comment->comment_content, $comment );
    }

    return rest_ensure_response( $comment_data );
}

/**
 * Prepare a response for inserting into a collection of responses.
 *
 * This is copied from WP_REST_Controller class in the WP REST API v2 plugin.
 *
 * @param WP_REST_Response $response Response object.
 * @return array Response data, ready for insertion into collection data.
 */
function prefix_prepare_for_collection( $response ) {
    if ( ! ( $response instanceof WP_REST_Response ) ) {
        return $response;
    }

    $data  = (array) $response->get_data();
    $links = rest_get_server()::get_compact_response_links( $response );

    if ( ! empty( $links ) ) {
        $data['_links'] = $links;
    }

    return $data;
}

/**
 * Get our sample schema for comments.
 */
function prefix_get_comment_schema() {
    $schema = array(
        // This tells the spec of JSON Schema we are using which is draft 4.
        '$schema'              => 'http://json-schema.org/draft-04/schema#',
        // The title property marks the identity of the resource.
        'title'                => 'comment',
        'type'                 => 'object',
        // In JSON Schema you can specify object properties in the properties attribute.
        'properties'           => array(
            'id' => array(
                'description'  => esc_html__( 'Unique identifier for the object.', 'my-textdomain' ),
                'type'         => 'integer',
                'context'      => array( 'view', 'edit', 'embed' ),
                'readonly'     => true,
            ),
            'author' => array(
                'description'  => esc_html__( 'The id of the user object, if author was a user.', 'my-textdomain' ),
                'type'         => 'integer',
            ),
            'content' => array(
                'description'  => esc_html__( 'The content for the object.', 'my-textdomain' ),
                'type'         => 'string',
            ),
        ),
    );

    return $schema;
}

如果您注意到,每个评论资源现在都与我们指定的架构相匹配。我们在prefix_rest_prepare_comment(). 通过为我们的资源创建模式,我们现在可以通过发出请求来查看该模式OPTIONS。为什么这有用?如果我们想要其他语言(例如 JavaScript)来解释我们的数据并验证来自我们端点的数据,JavaScript 需要知道我们的数据是如何构建的。当我们提供模式时,我们为其他作者和我们自己打开了大门,以一致的方式在我们的端点之上构建。

Schema 提供了机器可读的数据,因此任何可以读取 JSON 的东西都可以理解它正在查看的数据类型。当我们通过向该路由发出GET对 OPTIONS 请求的请求来查看 API 索引时。https://ourawesomesite.com/wp-json/, we are returned the schema of our API, enabling others to write client libraries to interpret our data. This process of reading schema data is known as discovery. When we have provided schema for a resource we make that resource discoverable via`公开资源模式只是我们模式难题的一部分。我们还想为我们注册的参数使用模式。

参数模式

当我们为端点注册请求参数时,我们还可以使用 JSON Schema 为我们提供有关参数应该是什么的数据。这使我们能够编写可在端点扩展时重复使用的验证库。Schema 需要更多的前期工作,但如果您要编写一个会增长的生产应用程序,您绝对应该考虑使用 schema。让我们看一个使用参数模式和验证的示例。

// Register our routes.
function prefix_register_my_arg_route() {
    register_rest_route( 'my-namespace/v1', '/schema-arg', array(
        // Here we register our endpoint.
        array(
            'methods'  => 'GET',
            'callback' => 'prefix_get_item',
            'args' => prefix_get_endpoint_args(),
        ),
    ) );
}

// Hook registration into 'rest_api_init' hook.
add_action( 'rest_api_init', 'prefix_register_my_arg_route' );

/**
 * Returns the request argument `my-arg` as a rest response.
 *
 * @param WP_REST_Request $request Current request.
 */
function prefix_get_item( $request ) {
    // If we didn't use required in the schema this would throw an error when my arg is not set.
    return rest_ensure_response( $request['my-arg'] );
}

/**
 * Get the argument schema for this example endpoint.
 */
function prefix_get_endpoint_args() {
    $args = array();

    // Here we add our PHP representation of JSON Schema.
    $args['my-arg'] = array(
        'description'       => esc_html__( 'This is the argument our endpoint returns.', 'my-textdomain' ),
        'type'              => 'string',
        'validate_callback' => 'prefix_validate_my_arg',
        'sanitize_callback' => 'prefix_sanitize_my_arg',
        'required'          => true,
    );

    return $args;
}

/**
 * Our validation callback for `my-arg` parameter.
 *
 * @param mixed           $value   Value of the my-arg parameter.
 * @param WP_REST_Request $request Current request object.
 * @param string          $param   The name of the parameter in this case, 'my-arg'.
 * @return true|WP_Error True if the data is valid, WP_Error otherwise.
 */
function prefix_validate_my_arg( $value, $request, $param ) {
    $attributes = $request->get_attributes();

    if ( isset( $attributes['args'][ $param ] ) ) {
        $argument = $attributes['args'][ $param ];
        // Check to make sure our argument is a string.
        if ( 'string' === $argument['type'] && ! is_string( $value ) ) {
            return new WP_Error( 'rest_invalid_param', sprintf( esc_html__( '%1$s is not of type %2$s', 'my-textdomain' ), $param, 'string' ), array( 'status' => 400 ) );
        }
    } else {
        // This code won't execute because we have specified this argument as required.
        // If we reused this validation callback and did not have required args then this would fire.
        return new WP_Error( 'rest_invalid_param', sprintf( esc_html__( '%s was not registered as a request argument.', 'my-textdomain' ), $param ), array( 'status' => 400 ) );
    }

    // If we got this far then the data is valid.
    return true;
}

/**
 * Our sanitization callback for `my-arg` parameter.
 *
 * @param mixed           $value   Value of the my-arg parameter.
 * @param WP_REST_Request $request Current request object.
 * @param string          $param   The name of the parameter in this case, 'my-arg'.
 * @return mixed|WP_Error The sanitize value, or a WP_Error if the data could not be sanitized.
 */
function prefix_sanitize_my_arg( $value, $request, $param ) {
    $attributes = $request->get_attributes();

    if ( isset( $attributes['args'][ $param ] ) ) {
        $argument = $attributes['args'][ $param ];
        // Check to make sure our argument is a string.
        if ( 'string' === $argument['type'] ) {
            return sanitize_text_field( $value );
        }
    } else {
        // This code won't execute because we have specified this argument as required.
        // If we reused this validation callback and did not have required args then this would fire.
        return new WP_Error( 'rest_invalid_param', sprintf( esc_html__( '%s was not registered as a request argument.', 'my-textdomain' ), $param ), array( 'status' => 400 ) );
    }

    // If we got this far then something went wrong don't use user input.
    return new WP_Error( 'rest_api_sad', esc_html__( 'Something went terribly wrong.', 'my-textdomain' ), array( 'status' => 500 ) );
}

在上面的示例中,我们已经抽象掉使用名称'my-arg'。我们可以将这些验证和清理函数用于任何其他参数,这些参数应该是我们已为其指定模式的字符串。随着您的代码库和端点的增长,模式将有助于保持您的代码轻量级和可维护性。如果没有模式,您可以验证和清理,但是跟踪哪些函数应该验证什么会更加困难。通过向请求参数添加模式,我们还可以向客户端公开我们的参数模式,因此可以在客户端构建验证库,这可以通过防止将无效请求发送到 API 来提高性能。

笔记:如果您对使用模式感到不舒服,仍然可以对每个参数进行验证/清理回调,在某些情况下,进行自定义验证是最有意义的。

概括

Schema 有时看起来很愚蠢,并且可能喜欢不必要的工作,但如果您想要可维护、可发现且易于扩展的端点,那么使用 schema 是必不可少的。模式还有助于为人类和计算机自我记录您的端点!

JSON 模式基础

WordPress 实现了一个使用JSON Schema Version 4 规范子集的验证器。建议阅读 RFC 以更深入地了解 JSON Schema 的工作原理,但本文将介绍 JSON Schema 的基础知识以及 WordPress 支持的功能。

应用程序接口

REST API 定义了两个使用 JSON Schema 的主要函数:rest_validate_value_from_schemarest_sanitize_value_from_schema. 这两个函数都接受请求数据作为第一个参数,参数的架构定义作为第二个参数,以及可选的参数名称作为第三个参数。验证函数返回一个实例true或一个WP_Error实例,具体取决于数据是否成功验证了模式。sanitize 函数返回传递给该函数的数据的净化形式,或者WP_Error如果数据无法安全净化则返回一个实例。

调用这些函数时,您应该_始终_首先使用 验证数据rest_validate_value_from_schema,然后如果该函数返回true,则使用 清理数据rest_sanitize_value_from_schema。不同时使用两者会使您的端点面临安全漏洞。

如果您的端点是使用 的子类WP_REST_Controller实现的,该WP_REST_Controller::get_endpoint_args_for_item_schema方法将自动将您的参数标记为使用内置的验证和清理回调。因此,无需手动指定回调。

如果您的端点不遵循控制器类模式、从 返回的 argsWP_REST_Controller::get_collection_params()或未指定回调的任何其他实例,则该WP_REST_Request对象将使用该函数应用清理和验证rest_parse_request_argsanitize_callback重要的是,这仅在未定义时应用。因此,如果您sanitize_callback为参数定义指定自定义,则内置的 JSON 模式验证将不适用。如果您需要此验证,您应该在参数定义中手动指定rest_validate_request_arg为。validate_callback

缓存模式

模式可能很复杂,并且可能需要时间来生成。您应该考虑在插件的自定义端点中缓存生成的架构,以避免重复生成相同的架构对象。

如果您使用 的子类WP_REST_Controller定义端点,则可能如下所示:

    /**
     * Retrieves the attachment's schema, conforming to JSON Schema.
     *
     * @return array Item schema as an array.
     */
    public function get_item_schema() {
        // Returned cached copy whenever available.
        if ( $this->schema ) {
            return $this->add_additional_fields_schema( $this->schema );
        }

        $schema = parent::get_item_schema();
        // Add endpoint-specific properties to Schema.
        $schema['properties']['field_name'] = array( /* ... */ );
        $schema['properties']['etcetera'] = array( /* ... */ );

        // Cache generated schema on endpoint instance.
        $this->schema = $schema;

        return $this->add_additional_fields_schema( $this->schema );
    }

该模式在#47871的 5.3 版中引入 WordPress 核心,在生成一些 API 响应时产生了高达 40% 的速度提升。

架构文件

基本模式文档由一些属性组成。

  • $schema对描述文档正在使用的规范版本的元模式的引用。
  • title架构的标题。通常这是一个人类可读的标签,但在 WordPress 中这是机器可读的字符串。例如,posts 端点的标题为“post”。评论的标题为“评论”。
  • type这是指所描述的值的类型。这可以是七种原始类型中的任何一种。在 WordPress 中,顶级类型几乎总是一个object,即使对于返回对象数组的集合端点也是如此。
  • properties对象中包含的已知属性及其定义的列表。每个属性定义本身也是一个架构,但没有 $schema 顶级属性,更准确地描述为子架构。

原始类型

JSON Schema 定义了一个包含七种允许的原始类型的列表。

  • string一个字符串值。
  • null一个null值。
  • number任何数字。允许使用小数。相当于floatPHP 中的a 。
  • integer一个数字,但不允许使用小数或指数。
  • boolean一个truefalse值。
  • array值列表。这相当于一个JavaScript 数组。在 PHP 中,这称为数字数组,或没有定义键的数组。
  • object键到值的映射。这相当于一个JavaScript 对象。在 PHP 中,这称为关联数组,或具有已定义键的数组。

使用关键字指定值的原始类型type。例如,这就是您string使用 JSON Schema 定义值的方式。

array(
    'type' => 'string',
);

JSON Schema 允许定义可以是多种类型的值。例如,这就是您定义可以是 astring或 a的值的方式boolean

array(
    'type' => array( 'boolean', 'string' ),  
);

类型杂耍

因为 WordPress REST API在正文中或作为 URL 的查询部分接受URL 形式的编码数据,所以许多原始类型执行类型杂耍以将这些字符串值转换为其适当的本机类型。POST

  • string``is_string仅允许使用符合的字符串。
  • null``null只接受正确键入的内容。这意味着在 URL 中提交空值或作为 URL 形式编码的帖子主体是不可能的,必须使用 JSON 请求主体。
  • number``is_numeric允许通过的浮点数、整数和字符串。值将被转换为 a float
  • integer可以转换为float小数部分等于 0 的整数或字符串。
  • boolean布尔值、整数01,或字符串"0""1""false""true"0被视为false并被1视为true
  • array根据wp_is_numeric_array或字符串的数值数组。如果字符串以逗号分隔,它将被拆分成一个数组,否则它将是一个包含字符串值的数组。例如:"red,yellow"成为array( "red", "yellow" )"blue"成为array( "blue" )
  • object数组、stdClass对象、对象实现JsonSerializable或空字符串。值将转换为本机 PHP 数组。

当使用多种类型时,类型将按照指定的顺序进行评估。这可能会对您的 REST API 端点接收到的经过清理的数据产生影响。例如,在前面的示例中,如果提交的值为"1",它将被清理为布尔true值。但是,如果顺序被翻转,该值将保留为 string "1"

笔记:JSON Schema 规范允许定义没有type字段的模式。然而,WordPress 实现需要type定义一个类型,_doing_it_wrong如果类型被省略,将会发出通知。

格式

七种原始类型就是这样,primitive,那么如何定义更复杂的值类型呢?其中一种方法是使用关键字format。该format关键字允许为具有明确定义的结构的值定义额外的语义级验证。

例如,要需要一个日期值,您可以使用格式date-time

array(
    'type'   => 'string',
    'format' => 'date-time',
);

WordPress 支持以下格式:

  • date-time根据RFC3339格式化的日期。
  • uri根据esc_url_raw.
  • email根据 的电子邮件地址is_email
  • ipv4 或 v6 的 IP 地址。
  • uuid任何版本的uuid。
  • hex-color带有前导 . 的 3 或 6 个字符的十六进制颜色#

即使值是空字符串,值也必须与其格式匹配。如果需要允许“空”值,请添加null为可能的类型。

例如,以下架构将允许127.0.0.1null作为可能的值。

array(
    'type'   => array( 'string', 'null' ),
    'format' => 'ip',
);

字符串

这些string类型支持三个附加关键字。

最小长度和最大长度

和关键字可用于限制可接受的字符串长度minLengthmaxLength重要的是,多字节字符被算作单个字符并且边界是包含在内的。

例如,给定以下架构,ababcabcd有效,而aabcde无效。

array(
    'type'      => 'string',
    'minLength' => 2,
    'maxLength' => 4,
);

exclusiveMinimum关键字exclusiveMaximum不适用,它们只对数字有效。

图案

JSON Schema 关键字pattern可用于验证字符串字段是否与正则表达式匹配。

例如,给定以下模式,#123将是有效的,但#abc不会。

array(
    'type'    => 'string',
    'pattern' => '#[0-9]+',
);

正则表达式不会自动锚定。不支持正则表达式标志,例如/i使匹配不区分大小写。

JSON Schema RFC 建议限制自己使用以下正则表达式功能,以便模式可以在尽可能多的不同编程语言之间互操作。

  • 单个 Unicode 字符,由 JSON 规范 [RFC4627] 定义。
  • 简单字符类[abc],范围字符类[a-z]
  • 补充字符类[^abc][^a-z].
  • 简单量词:(+一个或多个),*(零个或多个),?(零个或一个),以及它们的惰性版本+?, *?, ??.
  • 范围量词:({x}恰好 x 次出现),{x,y}(至少 x 次,最多 y 次出现),{x,}(x 次或更多次出现),以及它们的惰性版本。
  • 输入开始^和输入结束$锚点。
  • 简单的分组(...)和交替|

根据 ECMA 262 正则表达式方言,该模式应该有效。

数字

number类型integer支持四个附加关键字。

最小值和最大值

andminimum关键字maximum允许限制可接受数字的范围。例如,2根据此架构将有效,但0不会4

array(
    'type' => 'integer',
    'minimum' => 1,
    'maximum' => 3,
);

JSON Schema 还允许使用exclusiveMinimumexclusiveMaximum关键字分别表示值不能等于定义的minimummaximum。例如,在这种情况下,only2将是一个可接受的值。

array(
    'type'             => 'integer',
    'minimum'          => 1,
    'exclusiveMinimum' => true,
    'maximum'          => 3,
    'exclusiveMaximum' => true,
);

倍数

multipleOf关键字允许断言整数或数字类型是给定数字的倍数。例如,此架构将只接受偶数整数。

array(
    'type'       => 'integer',
    'multipleOf' => 2,
);

multipleOf也支持小数。例如,此架构可用于接受最多保留 1 个小数点的百分比。

array(
    'type'       => 'number',
    'minimum'    => 0,
    'maximum'    => 100,
    'multipleOf' => 0.1,
);

数组

指定一个typeofarray要求数据是一个数组,但这只是验证故事的一半。您还需要强制执行数组中每个项目的格式。这是通过使用关键字指定每个数组项必须符合的 JSON 模式来完成的items

例如,以下架构需要一个 IP 地址数组。

array(
    'type'  => 'array',
    'items' => array(
        'type'   => 'string',
        'format' => 'ip',
    ),
);

这将通过验证。

[ "127.0.0.1", "255.255.255.255" ]

虽然这将无法通过验证。

[ "127.0.0.1", 5 ]

模式items可以是任何模式,它甚至可以是数组本身!

array(
    'type'  => 'array',
    'items' => array(
        'type'  => 'array',
        'items' => array(
            'type'   => 'string',
            'format' => 'hex-color',
        ),
    ),
);

这将通过验证。

[
  [ "#ff6d69", "#fecc50" ],
  [ "#0be7fb" ]
]

虽然这将无法通过验证。

[
  [ "#ff6d69", "#fecc50" ],
  "george"
]

minItems 和 maxItems

和关键字可用于限制可接受的包含在数组中的项数minItemsmaxItems

例如,给定以下模式,[ "a" ][ "a", "b" ]是有效的,而[][ "a", "b", "c" ]是无效的。

array(
    'type'     => 'array',
    'minItems' => 1,
    'maxItems' => 2,
    'items'    => array(
        'type' => 'string',
    ),
);

andexclusiveMinimum关键字exclusiveMaximum不适用,它们仅对numberandinteger类型有效。

独特的物品

关键字uniqueItems可用于要求数组中的所有项都是唯一的。

例如,给定以下模式,[ "a", "b" ]是有效的,而[ "a", "a" ]不是。

array(
    'type'        => 'array',
    'uniqueItems' => true,
    'items'       => array(
        'type' => 'string',
    ),
);

唯一性

不同类型的项目被认为是唯一的,例如 ,"1"并且11.0不同的值。

比较数组时,项目的顺序很重要。因此,给定的数组被认为具有所有唯一项。

[
  [ "a", "b" ],
  [ "b", "a" ]
]

比较对象时,成员出现的顺序无关紧要。所以给定的数组被认为有重复的项目,因为值是相同的,它们只是以不同的顺序出现。

[
  { 
    "a": 1,
    "b": 2
  },
  {
    "b": 2,
    "a": 1
  }
]

rest_validate_value_from_schema和中都检查了唯一性rest_sanitize_value_from_schema。这是为了防止在应用消毒之前项目被认为是唯一的,但在消毒之后项目将收敛到相同的值的情况。

以以下模式为例:

array(
    'type' => 'array',
    'uniqueItems' => true,
    'items' => array(
        'type' => 'string',
        'format' => 'uri',
    ),
);

带有 的请求[ "https://example.org/hello world", "https://example.org/hello%20world" ]将通过验证,因为每个字符串值都不同。但是,esc_url_raw将第一个 url 中的空格转换为%20值后将是相同的。

在这种情况下rest_sanitize_value_from_schema会返回一个错误。因此,您应该始终验证和清理参数。

对象

指定一个typeofobject要求数据是一个对象,但这只是验证故事的一半。您还需要强制执行每个对象属性的格式。这是通过使用关键字指定对象成员必须符合的 JSON 模式的属性名称映射来完成的properties

例如,以下模式需要一个属性name为字符串且color为十六进制颜色的对象。

array(
    'type'       => 'object',
    'properties' => array(
        'name'  => array(
            'type' => 'string',
        ),
        'color' => array(
            'type'   => 'string',
            'format' => 'hex-color',        
        ),
    ),
);

这将通过验证。

{
  "name": "Primary",
  "color": "#ff6d69"
}

虽然这将无法通过验证。

{
  "name": "Primary",
  "color": "orange"
}

必需的属性

默认情况下,为对象列出的任何属性都是可选的,因此虽然可能出乎意料,但以下内容也会通过对先前模式的验证。

{
  "name": "Primary"
}

有两种机制要求提供属性。

版本 3 语法

虽然 WordPress 主要遵循 JSON Schema Version 4,但它不遵循的一种方式是使用定义所需属性的语法。主要方法是通过将required关键字添加到每个属性的定义来使用 JSON Schema Version 3 语法。

array(
    'type'       => 'object',
    'properties' => array(
        'name'  => array(
            'type'     => 'string',
            'required' => true,
        ),
        'color' => array(
            'type'     => 'string',
            'format'   => 'hex-color',
            'required' => true,        
        ),
    ),
);

版本 4 语法

WordPress 还支持 JSON Schema Version 4 required property 语法,其中对象的必需属性列表定义为属性名称数组。这在指定元值具有所需属性列表时特别有用。

给定以下元字段。

register_post_meta( 'post', 'fixed_in', array(
    'type'         => 'object',
    'show_in_rest' => array(
        'single' => true,
        'schema' => array(
            'required'   => array( 'revision', 'version' ),
            'type'       => 'object',
            'properties' => array(
                'revision' => array(
                    'type' => 'integer',
                ),
                'version'  => array(
                    'type' => 'string',
                ),
            ),
        ),
    ),
) );

以下请求将无法通过验证。

{
    "title": "Check required properties",
    "content": "We should check that required properties are provided",
    "meta": {
        "fixed_in": {
            "revision": 47089
        }
    }
}

如果fixed_in完全省略元字段,则不会生成错误。定义了所需属性列表的对象并不表示需要提交该对象本身。只是如果包含该对象,则还必须包含列出的属性。

. 中端点的顶级架构不支持版本 4 语法WP_REST_Controller::get_item_schema()。给定以下架构,用户可以成功提交没有 title 或 content 属性的请求。这是因为模式文档本身并不用于验证,而是转换为参数定义列表。

array(
    '$schema'    => 'http://json-schema.org/draft-04/schema#',
    'title'      => 'my-endpoint',
    'type'       => 'object',
    'required'   => array( 'title', 'content' ),
    'properties' => array(
        'title'   => array(
            'type' => 'string',
        ),
        'content' => array(
            'type' => 'string',
        ),
    ),
);

附加属性

也许不直观,默认情况下,JSON Schema 还允许提供未在模式中指定的其他属性。因此,以下将通过验证。

{
  "name": "Primary",
  "color": "#ff6d69",
  "description": "The primary color to use in the theme."
}

这可以使用additionalProperties关键字进行自定义。设置additionalProperties为 false 将拒绝具有未知属性的数据。

array(
    'type'                 => 'object',
    'additionalProperties' => false,
    'properties'           => array(
        'name'  => array(
            'type' => 'string',
        ),
        'color' => array(
            'type'   => 'string',
            'format' => 'hex-color',        
        ),
    ),
);

关键字additionalProperties也可以设置为它自己的模式。这将要求任何未知属性的值都与给定的模式相匹配。

这可能有用的一种方式是当您想要接受一个值列表时,每个值都有自己的唯一键。例如:

array(
    'type'                 => 'object',
    'properties'           => array(),
    'additionalProperties' => array(
        'type'       => 'object',
        'properties' => array(
            'name'  => array(
                'type'     => 'string',
                'required' => true,
            ),
            'color' => array(
                'type'     => 'string',
                'format'   => 'hex-color',
                'required' => true,        
            ),
        ),    
    ),
);

这将通过验证。

{
  "primary": {
    "name": "Primary",
    "color": "#ff6d69"
  },
  "secondary": {
    "name": "Secondary",
    "color": "#fecc50"
  }
}

虽然这将无法通过验证。

{
  "primary": {
    "name": "Primary",
    "color": "#ff6d69"
  },
  "secondary": "#fecc50"
}

图案属性

关键字patternProperties类似于additionalProperties关键字,但允许断言属性匹配正则表达式模式。关键字是一个对象,其中每个属性都是一个正则表达式模式,其值是用于验证与该模式匹配的属性的 JSON 模式。

例如,此模式要求每个值都是十六进制颜色,并且属性必须仅包含“单词”字符。

array(
  'type'                 => 'object',
  'patternProperties'    => array(
    '^\\w+$' => array(
      'type'   => 'string',
      'format' => 'hex-color',
    ),
  ),
  'additionalProperties' => false,
);

这将通过验证。

{
  "primary": "#ff6d69",
  "secondary": "#fecc50"
}

虽然这将无法通过验证。

{
  "primary": "blue",
  "$secondary": "#fecc50"
}

当 REST API 验证patternProperties架构时,如果某个属性与任何模式都不匹配,则该属性将被允许并且不会对其内容应用任何验证。虽然这可能令人困惑,但它的行为与关键字相同properties。如果不需要此逻辑,请添加additionalProperties到架构以禁止不匹配的属性。

最小属性和最大属性

minItems关键字maxItems可用于类型array。和minProperties为该类型maxProperties引入了相同的功能objectadditionalProperties这在使用具有唯一键的对象列表时会很有帮助。

array(
  'type'                 => 'object',
  'additionalProperties' => array(
    'type'   => 'string',
    'format' => 'hex-color',
  ),
  'minProperties'        => 1,
  'maxProperties'        => 3,
);

这将通过验证。

{
  "primary": "#52accc",
  "secondary": "#096484"
}

虽然这将无法通过验证。

{
  "primary": "#52accc",
  "secondary": "#096484",
  "tertiary": "#07526c"
}

andexclusiveMinimum关键字exclusiveMaximum不适用,它们仅对numberandinteger类型有效。

类型不可知的关键字

一个和任何一个

这些是高级关键字,允许 JSON 模式验证器在验证值时选择要使用的许多模式之一。关键字anyOf允许一个值匹配至少一个给定的模式。然而,oneOf关键字要求值与一个模式_完全_匹配。

例如,此架构允许向端点提交一组“操作”。每个操作可以是“裁剪”或“旋转”。

array(
    'type'  => 'array',
    'items' => array(
        'oneOf' => array(
            array(
                'title'      => 'Crop',
                'type'       => 'object',
                'properties' => array(
                    'operation' => array(
                        'type' => 'string',
                        'enum' => array(
                            'crop',
                        ),
                    ),
                    'x'         => array(
                        'type' => 'integer',
                    ),
                    'y'         => array(
                        'type' => 'integer',
                    ),
                ),
            ),
            array(
                'title'      => 'Rotation',
                'type'       => 'object',
                'properties' => array(
                    'operation' => array(
                        'type' => 'string',
                        'enum' => array(
                            'rotate',
                        ),
                    ),
                    'degrees'   => array(
                        'type'    => 'integer',
                        'minimum' => 0,
                        'maximum' => 360,
                    ),
                ),
            ),
        ),
    ),
);

REST API 将遍历oneOf数组中指定的每个模式并查找匹配项。如果恰好有一个模式匹配,则验证将成功。如果有多个模式匹配,验证将失败。如果没有模式匹配,则验证器将尝试找到最接近的匹配模式并返回适当的错误消息。

operations[0] 不是有效的旋转。原因:operations[0][degrees]必须在0(含)到360(含)之间

要生成更有用的错误消息,强烈建议为每个oneOfanyOf模式赋予一个title属性。

变更日志

WordPress 5.6

  • 支持multipleOfJSON Schema 关键字。r49063
  • 支持minPropertiesmaxPropertiesJSON Schema 关键字。r49053
  • 支持patternPropertiesJSON Schema 关键字。r49082
  • 支持anyOfoneOfJSON Schema 关键字。r49246

WordPress 5.5

  • 改进多类型 JSON Schema 支持。r48306
  • 在验证对象时检查是否提供了必需的属性。r47809
  • format仅当type是 时才验证关键字string。r48300
  • 支持uuidJSON Schema 格式。47753
  • 支持hex-colorJSON Schema 格式。r47450
  • 支持patternJSON Schema 关键字。r47810
  • 支持minItemsmaxItemsuniqueItemsJSON Schema 关键字。r47923 r48357
  • 支持minLengthmaxLengthJSON Schema 关键字。r47627

WordPress 5.4

  • 支持将空字符串转换为空对象的类型。r47362

WordPress 5.3

  • 支持null原始类型并实现基本的多类型处理。r46249
  • 支持additionalProperties针对架构进行验证。r45807

WordPress 4.9

  • 支持object原始类型。r41727