架构
最后更新于:2021-11-29 13:22:37
结构
概述
结构是元数据,告诉我们数据的结构。大多数数据库都实现了某种形式的结构,使我们能够以更有条理的方式推理我们的数据。GeChiUI REST API使用JSON结构来处理其数据的结构。您可以在不使用结构的情况下实现端点,但您将错过许多事情。由你来决定什么最适合你。
JSON
首先,让我们谈谈JSON。JSON是一种类似于JavaScript对象的人类可读数据格式。JSON代表JavaScript对象符号。JSON的受欢迎程度正在大幅增长,似乎正在席卷数据结构的世界。GeChiUI REST API对JSON使用一种称为JSON结构的特殊规范。要了解有关JSON结构的更多信息,请查看JSON结构网站,这更容易理解JSON结构的介绍。架构为我们提供了许多好处:改进测试、可发现性和整体更好的结构。让我们看看JSON数据块。
{
"shouldBeArray": 'LOL definitely not an array',
"shouldBeInteger": ['lolz', 'you', 'need', 'schema'],
"shouldBeString": 123456789
}
JSON解析器将毫无问题地浏览这些数据,也不会抱怨任何事情,因为它是有效的JSON。客户端和服务器对数据一无所知,也不知道他们只看到JSON。通过实现结构,我们实际上可以简化代码库。架构将有助于更好地构建我们的数据,以便我们的应用程序可以更轻松地推理我们与GeChiUI REST API的交互。GeChiUI 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 GC_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 GC_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 GC_REST_Controller class in the GC REST API v2 plugin.
*
* @param GC_REST_Response $response Response object.
* @return array Response data, ready for insertion into collection data.
*/
function prefix_prepare_for_collection( $response ) {
if ( ! ( $response instanceof GC_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 结构 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 结构 you can specify object properties in the properties attribute.
'properties' => array(
'id' => array(
'description' => esc_html__( '对象的唯一标识符。', '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__( '对象的内容。', 'my-textdomain' ),
'type' => 'string',
),
),
);
return $schema;
}
如果您注意到,每个评论资源现在都与我们指定的结构匹配。我们在prefix_rest_prepare_comment()
中进行了此切换。通过为我们的资源创建结构,我们现在可以通过提出OPTIONS
请求来查看此结构。为什么这有用?如果我们想要其他语言,例如JavaScript来解释我们的数据并从我们的端点验证数据,JavaScript需要知道我们的数据是如何构建的。当我们提供结构时,我们为其他作者和我们自己打开了大门,以一致的方式在我们的端点之上构建。
架构提供了机器可读数据,因此任何可以读取JSON的东西都可能理解它正在查看的数据类型。当我们通过向https://ourawesomesite.com/gc-json/
提出GET
请求来查看API索引时,我们会返回API的结构,使其他人能够编写客户端库来解释我们的数据。这种读取结构数据的过程被称为发现。当我们为资源提供结构后,我们通过OPTIONS
请求到该路径可以发现该资源。公开资源结构只是我们结构谜题的一部分。我们还想对注册的参数使用结构。
争论结构
当我们为端点注册请求参数时,我们还可以使用JSON结构向我们提供有关参数应该是什么的数据。这使我们能够编写验证库,随着端点的扩展,这些库可以重用。结构是前期工作,但如果您要编写一个将增长的生产应用程序,您绝对应该考虑使用结构。让我们看看使用参数结构和验证的示例。
// 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 GC_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 结构.
$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 GC_REST_Request $request Current request object.
* @param string $param The name of the parameter in this case, 'my-arg'.
* @return true|GC_Error True if the data is valid, GC_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 GC_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 GC_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 GC_REST_Request $request Current request object.
* @param string $param The name of the parameter in this case, 'my-arg'.
* @return mixed|GC_Error The sanitize value, or a GC_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 GC_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 GC_Error( 'rest_api_sad', esc_html__( 'Something went terribly wrong.', 'my-textdomain' ), array( 'status' => 500 ) );
}
在上面的示例中,我们抽象了使用'my-arg'
名称。我们可以将这些验证和清理函数用于任何其他参数,这些参数应该是我们指定的结构的字符串。随着代码库和端点的增长,结构将有助于保持代码的轻量级和可维护性。没有结构,您可以验证和清理,但更难跟踪哪些函数应该验证什么。通过将结构添加到请求参数中,我们还可以将参数结构暴露给客户端,以便构建客户端验证库,这可以通过防止无效请求被发送到API来帮助性能。
注意:如果您对使用结构感到不舒服,仍然可以对每个参数进行验证/清理回调,在某些情况下,进行自定义验证最有意义。
摘要
结构在点上可能看起来很愚蠢,可能像不必要的工作一样,但如果您想要可维护、可发现和易于扩展的端点,使用结构至关重要。架构还有助于自动数据人类和计算机的端点!
JSON结构基础知识
GeChiUI实现了一个验证器,该验证器使用JSON结构版本4规范的子集。建议阅读RFC,以更深入地了解JSON结构的工作原理,但本文将描述JSON结构的基础知识以及GeChiUI支持的功能。
API
REST API定义了使用JSON架构的两个主要函数:rest_validate_value_from_schema
和rest_sanitize_value_from_schema
。两个函数都接受请求数据作为第一个参数,接受参数的结构定义作为第二个参数,并可选地接受参数的名称作为第三个参数。验证函数返回true
或GC_Error
实例,具体取决于数据是否根据结构成功验证。清理函数返回传递给函数的数据的清理形式,如果数据无法安全清理,则返回GC_Error
实例。
调用这些函数时,您应该始终注意首先使用rest_validate_value_from_schema
验证数据,然后如果该函数返回true
,请使用rest_sanitize_value_from_schema
对数据进行清理。不同时使用这两个都可以打开您的端点到安全漏洞。
如果您的端点使用GC_REST_Controller
的子类实现,GC_REST_Controller::get_endpoint_args_for_item_schema
方法将自动将您的参数标记为使用内置验证和清理回调。因此,无需手动指定回调。
如果您的端点没有遵循控制器类结构、从GC_REST_Controller::get_collection_params()
返回的args或任何其他未指定回调的实例,GC_REST_Request
对象将使用rest_parse_request_arg
函数应用清理和验证。重要的是,只有在未定义sanitize_callback
时才应用此方法。因此,如果您为参数定义指定自定义sanitize_callback
,则内置的JSON结构验证将不适用。如果您需要此验证,您应该在参数定义中手动指定rest_validate_request_arg
为validate_callback
。
架构文档
基本结构文档由几个属性组成。
$schema
对描述文档所用规范版本的元结构的引用。title
结构的标题。通常这是一个人类可读标签,但在GeChiUI中,这是一个机器可读字符串。例如,文章端点的标题是“文章”。评论的标题是“评论”。type
这指的是所描述的值的类型。这可能是七种原始类型中的任何一种。在GeChiUI中,顶级类型几乎总是一个object
,即使是返回对象数组的集合端点。properties
对象及其定义中包含的已知属性的列表。每个属性定义本身也是一个结构,但没有$schema顶级属性,则更准确地描述为子结构。
原始类型
JSON结构定义了一个包含七个允许的原始类型的列表。
string
字符串值。null
一个null
值。number
任何数字。允许小数。相当于PHP中的float
。integer
数字,但不允许使用小数或指数。boolean
true
或false
值。array
值列表。这相当于JavaScript数组。在PHP中,这被称为数字数组,或没有定义键的数组。object
值键映射。这相当于JavaScript对象。在PHP中,这被称为关联数组,或具有定义键的数组。
A value’s primitive type is specified using the type
keyword. For example, this is how you would define a string
value with JSON 结构.
array(
'type' => 'string',
);
JSON结构允许定义可以是多种类型的值。例如,这就是定义string
或boolean
值的方式。
array(
'type' => array( 'boolean', 'string' ),
);
类型杂耍
由于GeChiUI REST API接受POST
正文或URL查询部分的URL表单编码数据,许多原始类型执行类型杂耍,将这些字符串值转换为适当的本机类型。
string
只允许根据is_string
。null
只接受正确键入的null
。这意味着无法在URL中提交空值或作为URL表单编码的文章主体提交,因此必须使用JSON请求主体。number
允许传递is_numeric
的浮点、整数和字符串。值将被转换为float
。integer
可以转换为小数部分等于0的float
的整数或字符串。boolean
布尔值、整数0
和1或字符串"0"
"1"
"false"
和"true"
0
被视为false
,1
被视为true
。array
根据gc_is_numeric_array
或字符串的数字数组。如果字符串是逗号分隔的,它将被拆分为数组,否则它将是一个包含字符串值的数组。例如"red,yellow"
变成aray( "red", "yellow" )
"blue"
变成array( "blue" )
object
数组、stdClass
对象、实现JsonSerializable
的对象或空字符串。值将转换为本机PHP数组。
使用多种类型时,类型将按指定的顺序进行评估。这可能会对REST API端点收到的经过清理的数据产生影响。例如,在前面的示例中,如果提交的值是"1"
它将被清理为布尔true
值。然而,如果顺序被翻转,该值将保留为字符串"1"
注意:JSON结构规范允许在没有type
字段的情况下定义结构。然而,GeChiUI实现需要定义type
,如果省略了类型,将发出_doing_it_wrong
通知。
格式
这七种原始类型就是这样,那么您如何定义更复杂的值类型呢?方法之一是使用format
关键字。format
关键字允许对结构定义良好的值进行额外的语义级验证。
例如,要要求日期值,您将使用date-time
格式。
array(
'type' => 'string',
'format' => 'date-time',
);
GeChiUI支持以下格式:
date-time
根据RFC3339格式化的日期。uri
根据esc_url_raw
uri。email
根据is_email
提供的电子邮件地址。ip
v4或v6 ip地址。uuid
任何版本的Uuid。hex-color
带有前导#
3或6个字符的十六进制颜色。
即使值是空字符串,该值也必须与其格式匹配。如果需要允许“空”值,请添加null
作为可能的类型。
例如,以下结构将允许127.0.0.1
或null
作为可能的值。
array(
'type' => array( 'string', 'null' ),
'format' => 'ip',
);
Strings
string
类型支持三个额外的关键字。
最小长度和最大长度
minLength
和maxLength
关键字可用于限制字符串的可接受长度。重要的是,多字节字符算作单个字符,边界是包容性的。
例如,给定以下结构,ab
、abc
和abcd
有效,而a
和abcde
无效。
array(
'type' => 'string',
'minLength' => 2,
'maxLength' => 4,
);
exclusiveMinimum
和exclusiveMaximum
关键字不适用,它们仅适用于数字。
结构
JSON架构关键字pattern
可用于验证字符串字段是否与正则表达式匹配。
例如,根据以下结构,#123
将有效,但#abc
无效。
array(
'type' => 'string',
'pattern' => '#[0-9]+',
);
正则表达式不会自动锚定。不支持正则表达式标志,例如/i
使匹配大小写不敏感。
JSON结构RFC建议将自己限制在以下正则表达式功能上,以便该结构可以在尽可能多的不同编程语言之间互操作。
- 单个Unicode字符,由JSON规范[RFC4627]定义。
- 简单的字符类
[abc]
,范围字符类[a-z]
。 - 补充字符类
[^abc]
,[^a-z]
。 - 简单的量词:
+
(一个或多个),*
(零或更多),?
(零或一个),以及他们的懒惰版本+?
,*?
,??
。 - 范围量词:
{x}
(正好x出现)、{x,y}
(至少x,最多y,发生),{x,}
(x出现次数或更多)及其惰性版本。 - 输入开始
^
和输入结束$
锚。 - 简单的分组
(...)
和交替|
。
根据ECMA 262正则表达式方言,该结构应该有效。
数字
number
和integer
类型支持另外四个关键字。
最小和最大
minimum
和maximum
关键字允许限制可接受的数字范围。例如,根据此结构,2
是有效的,但0
和4
无效。
array(
'type' => 'integer',
'minimum' => 1,
'maximum' => 3,
);
JSON结构还允许使用exclusiveMinimum
和exclusiveMaximum
关键字来表示该值不能分别等于定义的minimum
或maximum
。例如,在这种情况下,只有2
是可接受的值。
array(
'type' => 'integer',
'minimum' => 1,
'exclusiveMinimum' => true,
'maximum' => 3,
'exclusiveMaximum' => true,
);
multipleOf
multipleOf
关键字允许断言整数或数字类型是给定数字的倍数。例如,此结构将只接受偶数整数。
array(
'type' => 'integer',
'multipleOf' => 2,
);
multipleOf
也支持小数。例如,此结构可用于接受最大小数点后1的百分比。
array(
'type' => 'number',
'minimum' => 0,
'maximum' => 100,
'multipleOf' => 0.1,
);
数组
指定array
type
需要数据为数组,但这仅为验证故事的一半。您还需要强制执行数组中每个项目的格式。这是通过指定每个数组项必须使用项关键字符合的JSON结构来实现的。
例如,以下结构需要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
minItems
和maxItems
关键字可用于限制数组中包含的可接受项数量。
例如,给定以下结构,[ "a" ]
和[ "a", "b" ]
有效,而[]
和[ "a", "b", "c" ]
无效。
array(
'type' => 'array',
'minItems' => 1,
'maxItems' => 2,
'items' => array(
'type' => 'string',
),
);
exclusiveMinimum
和exclusiveMaximum
关键字不适用,它们仅适用于number
和integer
类型。
Unique Items
uniqueItems
关键字可用于要求数组中的所有项目都是唯一的。
例如,给定以下结构,[ "a", "b" ]
有效,而[ "a", "a" ]
无效。
array(
'type' => 'array',
'uniqueItems' => true,
'items' => array(
'type' => 'string',
),
);
“>独特性”>
不同类型的项目被认为是唯一的,例如"1"
1
和1.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
将返回错误。因此,您应该始终验证和清理参数。
对象
指定object
type
需要数据是对象,但这仅是验证故事的一半。您还需要强制执行每个对象属性的格式。这是通过指定对象成员必须使用properties
关键字遵守的JSON结构的属性名称映射来实现的。
例如,以下结构需要一个属性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 语法”>
虽然GeChiUI主要遵循JSON结构版本4,但它没有遵循的一种方法是使用语法来定义所需的属性。主要方法是通过在每个属性的定义中添加required
关键字来使用JSON架构版本3语法。
array(
'type' => 'object',
'properties' => array(
'name' => array(
'type' => 'string',
'required' => true,
),
'color' => array(
'type' => 'string',
'format' => 'hex-color',
'required' => true,
),
),
);
“>版本4语法”>
GeChiUI还支持JSON结构版本4所需的属性语法,其中对象所需的属性列表定义为属性名称数组。当指定元值具有所需属性列表时,这可能特别有帮助。
给定以下元字段。
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
元字段,则不会产生错误。定义所需属性列表的对象不表示对象本身需要提交。只是,如果包含对象,则列出的属性也必须包含在内。
GC_REST_Controller::get_item_schema()
中端点的顶级结构不支持版本4语法。根据给定以下结构,用户可以在没有标题或内容属性的情况下成功提交请求。这是因为结构文档本身不用于验证,而是转换为参数定义列表。
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结构还允许提供结构中未指定的其他属性。因此,以下内容将通过验证。
{
"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
以禁止不匹配的属性。
minProperties和maxProperties
minItems
和maxItems
关键字可用于array
类型。minProperties
和maxProperties
为object
类型引入了相同的功能。当使用additionalProperties
来拥有具有唯一键的对象列表时,这可能会很有帮助。
array(
'type' => 'object',
'additionalProperties' => array(
'type' => 'string',
'format' => 'hex-color',
),
'minProperties' => 1,
'maxProperties' => 3,
);
这将通过验证。
{
"primary": "#52accc",
"secondary": "#096484"
}
虽然这将无法验证。
{
"primary": "#52accc",
"secondary": "#096484",
"tertiary": "#07526c"
}
exclusiveMinimum
和exclusiveMaximum
关键字不适用,它们仅适用于number
和integer
类型。
键入不可知论关键词
oneOf 和 anyOf
这些是高级关键字,允许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
数组中指定的每个结构,并查找匹配项。如果正好有一个结构匹配,那么验证将成功。如果多个结构匹配,验证将失败。如果没有结构匹配,则验证器将尝试找到最接近的匹配结构并返回适当的错误消息。
操作[0]不是有效的轮岗。原因:操作[0][度数]必须在0(含)到360(含)之间
为了生成更有用的错误消息,强烈建议为每个oneOf
或anyOf
一个title
属性。