一个模式是元数据,告诉我们我们的数据结构如何。大多数数据库实现某种形式的模式,使我们能够以更结构化的方式推理我们的数据。 WordPress REST API使用JSON模式来处理其数据的结构化。您可以在不使用架构的情况下实现端点,但是您会错过很多事情。你最好决定什么适合你。
## JSON Schema
首先让我们来谈谈JSON。 JSON是类似于JavaScript对象的人类可读数据格式。 JSON代表JavaScript对象表示法。 JSON越来越受欢迎,似乎正在冒着数据结构的世界。 WordPress REST API使用JSON的特殊规范称为JSON模式。要了解有关JSON模式的更多信息,请查看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(
'post_per_page' => 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();
$server = rest_get_server();
if ( method_exists( $server, 'get_compact_response_links' ) ) {
$links = call_user_func( array( $server, 'get_compact_response_links' ), $response );
} else {
$links = call_user_func( array( $server, 'get_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将需要知道我们的数据结构。当我们提供模式时,我们为其他作者和我们自己打开门,以一致的方式建立在我们的端点之上。
模式提供机器可读数据,所以潜在的任何可以读取JSON的内容都可以了解它正在查看什么样的数据。当我们通过向https://ourawesomesite.com/wp-json/发出GET请求来查看API索引时,我们返回了API的模式,使其他人能够编写客户端库来解释我们的数据。读取模式数据的这个过程被称为发现。当我们为资源提供架构时,我们可以通过OPTIONS请求发现该资源到该路由。公开资源模式只是我们模式难题的一部分。我们也想为我们注册的参数使用模式。
## 参数模式
当我们注册一个端点的请求参数时,我们还可以使用JSON 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'.
*/
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 santization 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'.
*/
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来帮助执行性能。
注意:如果您对使用模式感到不舒服,仍然可以对每个参数进行验证/消毒回调,在某些情况下,做出自定义验证将是最有意义的。
## 概述
模式在点上可能看起来很愚蠢,可能是不必要的工作,但是如果您想要可维护,可发现和易于扩展的端点,则必须使用模式。模式还有助于自我记录人类和计算机的端点!
- 简介
- 主题开发
- WordPress许可证
- 什么是主题
- 开发环境
- 主题开发示例
- 主题基础
- 模板文件
- 主样式表(style.css)
- 文章类型
- 规划主题文件
- 模板层级
- 模板标签
- 循环
- 主题函数
- 连接主题文件和目录
- 使用CSS和JavaScript
- 条件标签
- 类别,标签和自定义分类
- 模板文件
- 内容模板文件
- 页面模板文件
- 附件模板文件
- 自定义内容类型
- 部分和其他模板文件
- 评论模板
- 分类模板
- 404页面
- 主题功能
- 核心支持的功能
- 管理菜单
- 自定义Headers
- 自定义Logo
- 文章格式
- 置顶文章
- Sidebars
- Widgets
- 导航菜单
- 分页
- 媒体
- Audio
- Images
- Galleries
- Video
- 精选图片和缩略图
- 国际化
- 本地化
- 辅助功能
- 主题选项 – 自定义API
- 定制对象
- 改进用户体验的工具
- 定制JavaScript API
- JavaScript / Underscore.js渲染的自定义控件
- 高级用法
- 主题安全
- 数据消毒/逃避
- 数据验证
- 使用随机数
- 常见漏洞
- 高级主题
- 子主题
- UI最佳实践
- JavaScript最佳做法
- 主题单元测试
- 验证你的主题
- Plugin API Hooks
- 发布你的主题
- 所需的主题文件
- 测试
- 主题评论指南
- 写文档
- 提交你的主题到WordPress.org
- 参考文献
- 模板标签列表
- 条件标签列表
- 编码标准
- HTML编码标准
- CSS编码标准
- JavaScript编码标准
- PHP编码标准
- 插件开发
- 插件开发简介
- 什么是插件
- 插件基础
- 头部要求
- 包括软件许可证
- 启用 / 停用 Hooks
- 卸载方法
- 最佳做法
- 插件安全
- 检查用户功能
- 数据验证
- 保护输入
- 保护输出
- 随机数
- Hooks
- Actions
- Filters
- 自定义Hooks
- 高级主题
- 管理菜单
- 顶级菜单
- 子菜单
- 短代码
- 基本短码
- 封闭短码
- 带参数的短代码
- TinyMCE增强型短码
- 设置
- 设置API
- 使用设置API
- 选项API
- 自定义设置页面
- 元数据
- 管理帖子元数据
- 自定义元数据
- 渲染元数据
- 自定义文章类型
- 注册自定义文章类型
- 使用自定义文章类型
- 分类
- 使用自定义分类
- 在WP 4.2+中使用“split术语”
- 用户
- 创建和管理用户
- 使用用户元数据
- 角色和功能
- HTTP API
- JavaScript
- jQuery
- Ajax
- 服务器端PHP和入队
- Heartbeat API
- 概要
- 计划任务
- 了解WP-Cron计划
- 安排WP-Cron 事件
- 将WP-Cron挂接到系统任务计划程序中
- WP-Cron简单测试
- 国际化
- 本地化
- 如何国际化您的插件
- 国际化安全
- WordPress.org
- 详细插件指南
- 规划您的插件
- 如何使用Subversion
- 插件开发者常见问题
- 开发工具
- Debug Bar 和附加组件
- 辅助插件
- REST API手册
- 资源
- 文章
- 文章修订
- 文章类型
- 文章状态
- 类别
- 标签
- 页面
- 评论
- 分类
- 媒体
- 用户
- 设置
- 使用REST API
- 全局参数
- 分页
- 链接和嵌入
- 发现
- 认证
- 经常问的问题
- 骨干JavaScript客户端
- 客户端库
- 扩展REST API
- 添加自定义端点
- 自定义内容类型
- 修改回应
- 模式
- 词汇表
- 路由和端点
- 控制器类