WordPress REST API不僅僅是一組默認路由。 它也是創建自定義路由和端點的工具。 WordPress前端提供了一組默認的URL映射,但是用于創建它們的工具(例如Rewrites API以及查詢類:WP_Query,WP_User等)也可用于創建自己的URL映射,或 自定義查詢。
本文檔詳細介紹了如何使用自己的端點創建完全自定義的路由。 我們將首先通過一個簡短的例子,然后將其擴展到內部使用的完全控制器模式。
## 基礎不錯
那么你要添加自定義端點到API? 太棒了! 讓我們開始一個簡單的例子。
我們從一個簡單的函數開始,看起來像這樣:
```
<?php
/**
* Grab latest post title by an author!
*
* @param array $data Options for the function.
* @return string|null Post title for the latest,? * or null if none.
*/
function my_awesome_func( $data ) {
$posts = get_posts( array(
'author' => $data['id'],
) );
if ( empty( $posts ) ) {
return null;
}
return $posts[0]->post_title;
}
```
要通過API提供此功能,我們需要注冊一個路由。 這告訴API使用我們的函數來響應給定的請求。 我們通過一個名為register_rest_route的函數來執行此操作,該函數應該在rest_api_init的回調中調用,以避免在API未加載時執行額外的工作。
我們需要將三件事傳遞給register_rest_route:命名空間,我們想要的路由和選項。 我們稍后會回到命名空間,但現在我們來選擇myplugin / v1。 我們將路由與/ author / {id}匹配,其中{id}是一個整數。
```
<?php
add_action( 'rest_api_init', function () {
register_rest_route( 'myplugin/v1', '/author/(?P<id>\d+)', array(
'methods' => 'GET',
'callback' => 'my_awesome_func',
) );
} );
```
現在,我們只注冊路由的一個端點。 (“Route”是URL,而“endpoint”是與之相對應的方法和URL),如果您的站點域是example.com,并且您保留了API路徑的wp-json,那么完整的URL將是http://example.com/wp-json/myplugin/v1/author/(?P\d+)。每個路由都可以有任意數量的端點,對于每個端點,您可以定義允許的HTTP方法,用于響應該請求的回調函數和用于創建自定義權限的權限回調。此外,您可以在請求中定義允許的字段,并且每個字段指定默認值,清理回調,驗證回調以及該字段是否必需。
## 命名空間
命名空間是端點的URL的第一部分。它們應該用作供應商/包前綴,以防止自定義路由之間的沖突。命名空間允許兩個插件添加相同名稱的路由,具有不同的功能。
命名空間一般應遵循供應商/ v1的模式,供應商通常是您的插件或主題,而v1代表API的第一個版本。如果您需要破壞與新端點的兼容性,那么您可以將其限制到v2。
上述情況,兩個具有相同名稱的路由,來自兩個不同的插件,要求所有供應商使用唯一的命名空間。如果沒有這樣做的話,就不會在主題或插件中使用供應商功能前綴,類前綴和/或類名稱空間,這是非常_doing_it_wrong。
使用命名空間的另一個好處是客戶端可以檢測到對您的自定義API的支持。 API索引列出了一個站點上可用的命名空間:
```
{
"name": "WordPress Site",
"description": "Just another WordPress site",
"url": "http://example.com/",
"namespaces": [
"wp/v2",
"vendor/v1",
"myplugin/v1",
"myplugin/v2",
]
}
```
如果客戶端想要檢查您的API是否存在于站點上,則可以檢查該列表。 (有關詳細信息,請參閱“發現指南”。)
## 參數
默認情況下,路由接收從請求傳入的所有參數。 這些被合并到一組參數中,然后被添加到Request對象中,該對象作為第一個參數傳遞給您的端點:
```
<?php
function my_awesome_func( WP_REST_Request $request ) {
// You can access parameters via direct array access on the object:
$param = $request['some_param'];
// Or via the helper method:
$param = $request->get_param( 'some_param' );
// You can get the combined, merged set of parameters:
$parameters = $request->get_params();
// The individual sets of parameters are also available, if needed:
$parameters = $request->get_url_params();
$parameters = $request->get_query_params();
$parameters = $request->get_body_params();
$parameters = $request->get_json_params();
$parameters = $request->get_default_params();
// Uploads aren't merged in, but can be accessed separately:
$parameters = $request->get_file_params();
}
```
(要準確了解參數的合并方式,請檢查WP_REST_Request :: get_parameter_order()的源代碼;基本順序是正文,查詢,URL,然后默認)。
通常,您將獲得所有參數不變。但是,您可以在注冊路由時注冊自己的參數,從而可以對其進行清理和驗證。
如果請求具有Content-type:application / json標頭集合和正文中的有效JSON,則get_json_params()將返回已解析的JSON主體作為關聯數組。
參數被定義為每個端點的鍵參數中的映射(在回調選項旁邊)。此映射使用該鍵的參數的名稱,其值為該參數的選項的映射。該數組可以包含默認的key,必需的,sanitize_callback和validate_callback。
- default:用作參數的默認值,如果沒有提供參數。
- required:如果定義為true,并且沒有為該參數傳遞值,則會返回錯誤。如果設置默認值,則不起作用,因為參數始終具有值。
- validate_callback:用于傳遞將傳遞參數值的函數。如果值有效,該函數應返回true,否則返回false。
- sanitize_callback:用于傳遞一個函數,用于在將參數值傳遞給主回調之前對其進行消毒。
使用sanitize_callback和validate_callback允許主回調僅用于處理請求,并使用WP_REST_Response類準備要返回的數據。通過使用這兩個回調,您將能夠安全地假設您的輸入在處理時是有效和安全的。
以我們前面的例子,我們可以確保傳入的參數總是一個數字:
```
<?php
add_action( 'rest_api_init', function () {
register_rest_route( 'myplugin/v1', '/author/(?P<id>\d+)', array(
'methods' => 'GET',
'callback' => 'my_awesome_func',
'args' => array(
'id' => array(
'validate_callback' => function($param, $request, $key) {
return is_numeric( $param );
}
),
),
) );
} );
```
您還可以將函數名稱傳遞給validate_callback,但是直接傳遞諸如is_numeric之類的某些函數將不僅會傳遞有關傳遞額外參數的警告,還會返回NULL,從而使用無效數據調用回調函數。我們希望最終在WordPress核心解決這個問題。
我們也可以使用像'sanitize_callback'=>'absint'這樣的東西,但驗證會拋出一個錯誤,讓客戶了解他們做錯了什么。當您寧愿更改正在輸入的數據而不是拋出錯誤(例如無效的HTML)時,消毒是有用的。
## 返回值
調用回調后,返回值將被轉換為JSON,并返回給客戶端。這允許您基本返回任何形式的數據。在我們上面的例子中,我們返回一個字符串或者null,它們被API自動處理并轉換成JSON。
像任何其他WordPress功能一樣,您也可以返回WP_Error實例。該錯誤信息將傳遞給客戶端,以及500個內部服務錯誤狀態代碼。您可以通過將WP_Error實例數據中的狀態選項設置為代碼來進一步自定義錯誤,例如對于錯誤的輸入數據為400。
以前的例子,我們現在可以返回一個錯誤實例:
```
<?php
/**
* Grab latest post title by an author!
*
* @param array $data Options for the function.
* @return string|null Post title for the latest,
* or null if none.
*/
function my_awesome_func( $data ) {
$posts = get_posts( array(
'author' => $data['id'],
) );
if ( empty( $posts ) ) {
return new WP_Error( 'awesome_no_author', 'Invalid author', array( 'status' => 404 ) );
}
return $posts[0]->post_title;
}
```
當作者沒有任何屬于他們的帖子時,這將向客戶端返回404 Not Found錯誤:
HTTP/1.1 404 Not Found
```
[{
"code": "no_author",
"message": "Invalid author",
"data": { "status": 404 }
}]
```
要獲得更高級的使用,您可以返回一個WP_REST_Response對象。 此對象“包裹”正常的身體數據,但允許您返回自定義狀態代碼或自定義標題。 您還可以添加鏈接到您的回復。 最快的方法是通過構造函數:
```
<?php
$data = array( 'some', 'response', 'data' );
// Create the response object
$response = new WP_REST_Response( $data );
// Add a custom status code
$response->set_status( 201 );
// Add a custom header
$response->header( 'Location', 'http://example.com/' );
```
當包裝現有的回調時,應該始終在返回值上使用rest_ensure_response()。這將從端點返回原始數據,并自動將其轉換為WP_REST_Response。 (請注意,WP_Error不會轉換為WP_REST_Response以允許正確的錯誤處理。)
## 權限回調
您還可以注冊端點的權限回調。這是一個功能,它檢查用戶是否可以在調用真正的回調之前執行動作(讀取,更新等)。這樣就可以讓API告知客戶他們可以在給定的URL上執行什么操作,而無需首先嘗試該請求。
此回調可以注冊為permission_callback,再次在您的回調選項旁邊的端點選項中。此回調應返回一個布爾值或WP_Error實例。如果此函數返回true,則響應將被處理。如果返回false,將返回默認錯誤消息,并且請求不會繼續處理。如果它返回一個WP_Error,該錯誤將返回給客戶端。
權限回調在遠程身份驗證之后運行,這將設置當前用戶。這意味著您可以使用current_user_can來檢查已驗證的用戶是否具有適當的操作能力,或者基于當前用戶ID的任何其他檢查。在可能的情況下,應始終使用current_user_can;而不是檢查用戶是否登錄(驗證),檢查它們是否可以執行操作(授權)。
繼續我們以前的例子,我們可以使它只有編輯或以上才能查看作者的數據。我們可以在這里檢查一些不同的功能,但最好的是edit_others_posts,這是編輯器的核心。為此,我們只需要一個回調:
```
<?php
add_action( 'rest_api_init', function () {
register_rest_route( 'myplugin/v1', '/author/(?P<id>\d+)', array(
'methods' => 'GET',
'callback' => 'my_awesome_func',
'args' => array(
'id' => array(
'validate_callback' => 'is_numeric'
),
),
'permission_callback' => function () {
return current_user_can( 'edit_others_posts' );
}
) );
} );
```
>[warning] 請注意,權限回調也接收到Request對象作為第一個參數,因此如果需要,您可以根據請求參數進行檢查。
##控制器模式
控制器模式是使用API??處理復雜端點的最佳做法。
在閱讀本節之前,建議您閱讀“擴展內部類”。這樣做會使您熟悉默認路由使用的模式,這是最佳做法。雖然不需要您用于處理請求的類擴展了WP_REST_Controller類或擴展它的類,但這樣做允許您繼承在這些類中完成的工作。此外,您可以放心,您是根據您使用的控制器方法遵循最佳做法。
在他們的核心,控制器只不過是一組通用命名的方法來配合REST約定,還有一些方便的幫手。控制器以register_routes方法注冊其路由,使用get_items,get_item,create_item,update_item和delete_item響應請求,并具有類似的命名權限檢查方法。遵循此模式將確保您不會錯過端點中的任何步驟或功能。
要使用控制器,您首先需要對基本控制器進行子類化。這為您提供了一套基本的方法,可以為您添加自己的行為。
一旦我們對控制器進行了子類化,我們需要實例化該類以使其工作。這應該在掛接到rest_api_init的回調內部完成,這確保我們只需要在我們需要時實例化類。正常控制器模式是在此回調內調用$ controller-> register_routes(),然后該類可以注冊其端點。
## 例子
以下是“起始”定制路線:
```
<?php
class Slug_Custom_Route extends WP_REST_Controller {
/**
* Register the routes for the objects of the controller.
*/
public function register_routes() {
$version = '1';
$namespace = 'vendor/v' . $version;
$base = 'route';
register_rest_route( $namespace, '/' . $base, array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_items' ),
'permission_callback' => array( $this, 'get_items_permissions_check' ),
'args' => array(
),
),
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array( $this, 'create_item' ),
'permission_callback' => array( $this, 'create_item_permissions_check' ),
'args' => $this->get_endpoint_args_for_item_schema( true ),
),
) );
register_rest_route( $namespace, '/' . $base . '/(?P<id>[\d]+)', array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_item' ),
'permission_callback' => array( $this, 'get_item_permissions_check' ),
'args' => array(
'context' => array(
'default' => 'view',
),
),
),
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'update_item' ),
'permission_callback' => array( $this, 'update_item_permissions_check' ),
'args' => $this->get_endpoint_args_for_item_schema( false ),
),
array(
'methods' => WP_REST_Server::DELETABLE,
'callback' => array( $this, 'delete_item' ),
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
'args' => array(
'force' => array(
'default' => false,
),
),
),
) );
register_rest_route( $namespace, '/' . $base . '/schema', array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_public_item_schema' ),
) );
}
/**
* Get a collection of items
*
* @param WP_REST_Request $request Full data about the request.
* @return WP_Error|WP_REST_Response
*/
public function get_items( $request ) {
$items = array(); //do a query, call another class, etc
$data = array();
foreach( $items as $item ) {
$itemdata = $this->prepare_item_for_response( $item, $request );
$data[] = $this->prepare_response_for_collection( $itemdata );
}
return new WP_REST_Response( $data, 200 );
}
/**
* Get one item from the collection
*
* @param WP_REST_Request $request Full data about the request.
* @return WP_Error|WP_REST_Response
*/
public function get_item( $request ) {
//get parameters from request
$params = $request->get_params();
$item = array();//do a query, call another class, etc
$data = $this->prepare_item_for_response( $item, $request );
//return a response or error based on some conditional
if ( 1 == 1 ) {
return new WP_REST_Response( $data, 200 );
}else{
return new WP_Error( 'code', __( 'message', 'text-domain' ) );
}
}
/**
* Create one item from the collection
*
* @param WP_REST_Request $request Full data about the request.
* @return WP_Error|WP_REST_Request
*/
public function create_item( $request ) {
$item = $this->prepare_item_for_database( $request );
if ( function_exists( 'slug_some_function_to_create_item') ) {
$data = slug_some_function_to_create_item( $item );
if ( is_array( $data ) ) {
return new WP_REST_Response( $data, 200 );
}
}
return new WP_Error( 'cant-create', __( 'message', 'text-domain'), array( 'status' => 500 ) );
}
/**
* Update one item from the collection
*
* @param WP_REST_Request $request Full data about the request.
* @return WP_Error|WP_REST_Request
*/
public function update_item( $request ) {
$item = $this->prepare_item_for_database( $request );
if ( function_exists( 'slug_some_function_to_update_item') ) {
$data = slug_some_function_to_update_item( $item );
if ( is_array( $data ) ) {
return new WP_REST_Response( $data, 200 );
}
}
return new WP_Error( 'cant-update', __( 'message', 'text-domain'), array( 'status' => 500 ) );
}
/**
* Delete one item from the collection
*
* @param WP_REST_Request $request Full data about the request.
* @return WP_Error|WP_REST_Request
*/
public function delete_item( $request ) {
$item = $this->prepare_item_for_database( $request );
if ( function_exists( 'slug_some_function_to_delete_item') ) {
$deleted = slug_some_function_to_delete_item( $item );
if ( $deleted ) {
return new WP_REST_Response( true, 200 );
}
}
return new WP_Error( 'cant-delete', __( 'message', 'text-domain'), array( 'status' => 500 ) );
}
/**
* Check if a given request has access to get items
*
* @param WP_REST_Request $request Full data about the request.
* @return WP_Error|bool
*/
public function get_items_permissions_check( $request ) {
//return true; <--use to make readable by all
return current_user_can( 'edit_something' );
}
/**
* Check if a given request has access to get a specific item
*
* @param WP_REST_Request $request Full data about the request.
* @return WP_Error|bool
*/
public function get_item_permissions_check( $request ) {
return $this->get_items_permissions_check( $request );
}
/**
* Check if a given request has access to create items
*
* @param WP_REST_Request $request Full data about the request.
* @return WP_Error|bool
*/
public function create_item_permissions_check( $request ) {
return current_user_can( 'edit_something' );
}
/**
* Check if a given request has access to update a specific item
*
* @param WP_REST_Request $request Full data about the request.
* @return WP_Error|bool
*/
public function update_item_permissions_check( $request ) {
return $this->create_item_permissions_check( $request );
}
/**
* Check if a given request has access to delete a specific item
*
* @param WP_REST_Request $request Full data about the request.
* @return WP_Error|bool
*/
public function delete_item_permissions_check( $request ) {
return $this->create_item_permissions_check( $request );
}
/**
* Prepare the item for create or update operation
*
* @param WP_REST_Request $request Request object
* @return WP_Error|object $prepared_item
*/
protected function prepare_item_for_database( $request ) {
return array();
}
/**
* Prepare the item for the REST response
*
* @param mixed $item WordPress representation of the item.
* @param WP_REST_Request $request Request object.
* @return mixed
*/
public function prepare_item_for_response( $item, $request ) {
return array();
}
/**
* Get the query params for collections
*
* @return array
*/
public function get_collection_params() {
return array(
'page' => array(
'description' => 'Current page of the collection.',
'type' => 'integer',
'default' => 1,
'sanitize_callback' => 'absint',
),
'per_page' => array(
'description' => 'Maximum number of items to be returned in result set.',
'type' => 'integer',
'default' => 10,
'sanitize_callback' => 'absint',
),
'search' => array(
'description' => 'Limit results to those matching a string.',
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
),
);
}
}
```
- 簡介
- 主題開發
- 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
- 添加自定義端點
- 自定義內容類型
- 修改回應
- 模式
- 詞匯表
- 路由和端點
- 控制器類