一個模式是元數據,告訴我們我們的數據結構如何。大多數數據庫實現某種形式的模式,使我們能夠以更結構化的方式推理我們的數據。 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
- 添加自定義端點
- 自定義內容類型
- 修改回應
- 模式
- 詞匯表
- 路由和端點
- 控制器類