各位观众,欢迎来到今天的WordPress REST API开发讲座!我是你们的老朋友,代码界的段子手,今天咱们来聊聊如何优雅地继承WP_REST_Controller
,打造你自己的REST API端点,让你的WordPress项目瞬间高大上起来。
一、WP_REST_Controller
:REST API的基石
首先,咱们得认识一下这位大佬:WP_REST_Controller
。它就像是盖楼的地基,为你提供了一套标准的API结构,让你专注于业务逻辑,而不是重复造轮子。
WP_REST_Controller
定义了一些核心方法,帮助你处理:
- 路由注册: 将你的API端点与特定的URL关联起来。
- 权限验证: 确保只有授权用户才能访问你的API。
- 参数验证和清理: 保证输入数据的有效性和安全性。
- 响应格式化: 以统一的JSON格式返回数据。
二、继承WP_REST_Controller
:你的专属API蓝图
继承WP_REST_Controller
,就像拿到了一张API蓝图,你只需要按照蓝图上的指示,填充自己的业务逻辑,就能快速搭建出一个功能完善的API端点。
下面,咱们用一个简单的例子来说明:创建一个管理书籍的API。
1. 创建你的控制器类
<?php
/**
* 书籍控制器类
*/
class Book_REST_Controller extends WP_REST_Controller {
/**
* 构造函数
*/
public function __construct() {
$this->namespace = 'my-plugin/v1'; // API命名空间,避免冲突
$this->rest_base = 'books'; // API端点基础路径,例如 /wp-json/my-plugin/v1/books
}
/**
* 注册路由
*/
public function register_routes() {
register_rest_route( $this->namespace, '/' . $this->rest_base, array(
array(
'methods' => WP_REST_Server::READABLE, // GET 方法
'callback' => array( $this, 'get_items' ), // 回调函数,处理GET请求
'permission_callback' => array( $this, 'get_items_permissions_check' ), // 权限验证回调函数
),
array(
'methods' => WP_REST_Server::CREATABLE, // POST 方法
'callback' => array( $this, 'create_item' ), // 回调函数,处理POST请求
'permission_callback' => array( $this, 'create_item_permissions_check' ), // 权限验证回调函数
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), // 参数验证
),
) );
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[d]+)', array(
array(
'methods' => WP_REST_Server::READABLE, // GET 方法,根据ID获取
'callback' => array( $this, 'get_item' ), // 回调函数,处理GET请求
'permission_callback' => array( $this, 'get_item_permissions_check' ), // 权限验证回调函数
'args' => array(
'context' => array(
'default' => 'view',
),
),
),
array(
'methods' => WP_REST_Server::EDITABLE, // PUT/PATCH 方法,更新
'callback' => array( $this, 'update_item' ), // 回调函数,处理PUT/PATCH请求
'permission_callback' => array( $this, 'update_item_permissions_check' ), // 权限验证回调函数
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), // 参数验证
),
array(
'methods' => WP_REST_Server::DELETABLE, // DELETE 方法,删除
'callback' => array( $this, 'delete_item' ), // 回调函数,处理DELETE请求
'permission_callback' => array( $this, 'delete_item_permissions_check' ), // 权限验证回调函数
'args' => array(
'force' => array(
'default' => false,
'type' => 'boolean',
'description' => __( '是否强制删除,跳过回收站。', 'my-plugin' ),
),
),
),
) );
}
/**
* 获取书籍列表
*
* @param WP_REST_Request $request Full data about the request.
* @return WP_REST_Response|WP_Error
*/
public function get_items( $request ) {
// 查询数据库,获取书籍列表
$books = $this->get_books();
// 格式化数据
$data = array();
foreach ( $books as $book ) {
$item = $this->prepare_item_for_response( $book, $request );
$data[] = $this->prepare_response_for_collection( $item ); // 确保返回结果是数组
}
// 返回响应
return rest_ensure_response( $data );
}
/**
* 获取单个书籍
*
* @param WP_REST_Request $request Full data about the request.
* @return WP_REST_Response|WP_Error
*/
public function get_item( $request ) {
$id = (int) $request['id'];
$book = $this->get_book( $id );
if ( empty( $book ) ) {
return new WP_Error( 'rest_book_invalid_id', __( '无效的书籍ID。', 'my-plugin' ), array( 'status' => 404 ) );
}
$data = $this->prepare_item_for_response( $book, $request );
return rest_ensure_response( $data );
}
/**
* 创建书籍
*
* @param WP_REST_Request $request Full data about the request.
* @return WP_REST_Response|WP_Error
*/
public function create_item( $request ) {
$book_data = $this->prepare_item_for_database( $request );
// 保存到数据库
$book_id = $this->save_book( $book_data );
if ( is_wp_error( $book_id ) ) {
return $book_id;
}
$book = $this->get_book( $book_id );
$data = $this->prepare_item_for_response( $book, $request );
return new WP_REST_Response( $data, 201 ); // 201 Created
}
/**
* 更新书籍
*
* @param WP_REST_Request $request Full data about the request.
* @return WP_REST_Response|WP_Error
*/
public function update_item( $request ) {
$id = (int) $request['id'];
$book = $this->get_book( $id );
if ( empty( $book ) ) {
return new WP_Error( 'rest_book_invalid_id', __( '无效的书籍ID。', 'my-plugin' ), array( 'status' => 404 ) );
}
$book_data = $this->prepare_item_for_database( $request );
$book_data['id'] = $id; // 确保ID存在
// 更新数据库
$updated = $this->update_book( $book_data );
if ( is_wp_error( $updated ) ) {
return $updated;
}
$book = $this->get_book( $id );
$data = $this->prepare_item_for_response( $book, $request );
return rest_ensure_response( $data );
}
/**
* 删除书籍
*
* @param WP_REST_Request $request Full data about the request.
* @return WP_REST_Response|WP_Error
*/
public function delete_item( $request ) {
$id = (int) $request['id'];
$book = $this->get_book( $id );
if ( empty( $book ) ) {
return new WP_Error( 'rest_book_invalid_id', __( '无效的书籍ID。', 'my-plugin' ), array( 'status' => 404 ) );
}
$force = isset( $request['force'] ) ? (bool) $request['force'] : false;
// 删除数据库记录
$deleted = $this->delete_book( $id, $force );
if ( is_wp_error( $deleted ) ) {
return $deleted;
}
return new WP_REST_Response( array( 'deleted' => true ), 200 );
}
/**
* 检查是否有读取书籍列表的权限
*
* @param WP_REST_Request $request Full data about the request.
* @return true|WP_Error
*/
public function get_items_permissions_check( $request ) {
// 只有登录用户才能读取
if ( ! is_user_logged_in() ) {
return new WP_Error( 'rest_forbidden', __( '您没有权限查看书籍列表。', 'my-plugin' ), array( 'status' => 401 ) );
}
return true;
}
/**
* 检查是否有读取单个书籍的权限
*
* @param WP_REST_Request $request Full data about the request.
* @return true|WP_Error
*/
public function get_item_permissions_check( $request ) {
return $this->get_items_permissions_check( $request ); // 沿用列表的权限
}
/**
* 检查是否有创建书籍的权限
*
* @param WP_REST_Request $request Full data about the request.
* @return true|WP_Error
*/
public function create_item_permissions_check( $request ) {
// 只有管理员才能创建
if ( ! current_user_can( 'manage_options' ) ) {
return new WP_Error( 'rest_forbidden', __( '您没有权限创建书籍。', 'my-plugin' ), array( 'status' => 403 ) );
}
return true;
}
/**
* 检查是否有更新书籍的权限
*
* @param WP_REST_Request $request Full data about the request.
* @return true|WP_Error
*/
public function update_item_permissions_check( $request ) {
return $this->create_item_permissions_check( $request ); // 沿用创建的权限
}
/**
* 检查是否有删除书籍的权限
*
* @param WP_REST_Request $request Full data about the request.
* @return true|WP_Error
*/
public function delete_item_permissions_check( $request ) {
return $this->create_item_permissions_check( $request ); // 沿用创建的权限
}
/**
* 准备用于响应的数据
*
* @param mixed $item WordPress representation of the item.
* @param WP_REST_Request $request Request object.
* @return WP_REST_Response Enriched data.
*/
public function prepare_item_for_response( $item, $request ) {
$data = array(
'id' => (int) $item->id,
'title' => $item->title,
'author' => $item->author,
'publication_date' => $item->publication_date,
);
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
$data = $this->filter_response_by_context( $data, $context );
// 增加链接信息,例如编辑链接
$links = array(
'self' => array(
'href' => rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $item->id ) ),
),
'collection' => array(
'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
),
);
$response = rest_ensure_response( $data );
$response->add_links( $links );
return $response;
}
/**
* 获取项目模式,用于描述API的数据结构
*
* @return array
*/
public function get_item_schema() {
$schema = array(
'$schema' => 'http://json-schema.org/draft-04/schema#',
'title' => 'book',
'type' => 'object',
'properties' => array(
'id' => array(
'description' => __( '唯一标识符', 'my-plugin' ),
'type' => 'integer',
'context' => array( 'view', 'edit', 'embed' ),
'readonly' => true,
),
'title' => array(
'description' => __( '书籍标题', 'my-plugin' ),
'type' => 'string',
'context' => array( 'view', 'edit' ),
'arg_options' => array(
'sanitize_callback' => 'sanitize_text_field',
),
'required' => true,
),
'author' => array(
'description' => __( '书籍作者', 'my-plugin' ),
'type' => 'string',
'context' => array( 'view', 'edit' ),
'arg_options' => array(
'sanitize_callback' => 'sanitize_text_field',
),
),
'publication_date' => array(
'description' => __( '出版日期', 'my-plugin' ),
'type' => 'string', // 使用字符串,或者如果需要可以使用'date'类型
'context' => array( 'view', 'edit' ),
'arg_options' => array(
'validate_callback' => function( $param, $request, $key ) {
return strtotime( $param ); // 简单验证是否是有效日期
},
),
),
),
);
return $this->add_additional_fields_schema( $schema );
}
/**
* 准备用于数据库存储的数据
*
* @param WP_REST_Request $request Request object.
* @return array|WP_Error Properly prepared data.
*/
public function prepare_item_for_database( $request ) {
$prepared_data = array();
if ( isset( $request['title'] ) ) {
$prepared_data['title'] = sanitize_text_field( $request['title'] );
}
if ( isset( $request['author'] ) ) {
$prepared_data['author'] = sanitize_text_field( $request['author'] );
}
if ( isset( $request['publication_date'] ) ) {
$prepared_data['publication_date'] = sanitize_text_field( $request['publication_date'] );
}
return $prepared_data;
}
/**
* 获取书籍列表(示例,需要替换为你的实际数据获取逻辑)
*
* @return array
*/
private function get_books() {
global $wpdb;
$table_name = $wpdb->prefix . 'books'; // 你的书籍表名
$sql = "SELECT * FROM {$table_name}";
$results = $wpdb->get_results( $sql );
return $results;
}
/**
* 获取单个书籍(示例,需要替换为你的实际数据获取逻辑)
*
* @param int $id 书籍ID
* @return object|null
*/
private function get_book( $id ) {
global $wpdb;
$table_name = $wpdb->prefix . 'books'; // 你的书籍表名
$sql = $wpdb->prepare( "SELECT * FROM {$table_name} WHERE id = %d", $id );
$result = $wpdb->get_row( $sql );
return $result;
}
/**
* 保存书籍到数据库(示例,需要替换为你的实际数据保存逻辑)
*
* @param array $data 书籍数据
* @return int|WP_Error 书籍ID或错误对象
*/
private function save_book( $data ) {
global $wpdb;
$table_name = $wpdb->prefix . 'books'; // 你的书籍表名
$result = $wpdb->insert(
$table_name,
$data,
array(
'%s', // title
'%s', // author
'%s', // publication_date
)
);
if ( false === $result ) {
return new WP_Error( 'db_insert_error', __( '数据库插入错误。', 'my-plugin' ), array( 'status' => 500 ) );
}
return $wpdb->insert_id;
}
/**
* 更新书籍到数据库(示例,需要替换为你的实际数据更新逻辑)
*
* @param array $data 书籍数据
* @return int|WP_Error 更新结果或错误对象
*/
private function update_book( $data ) {
global $wpdb;
$table_name = $wpdb->prefix . 'books'; // 你的书籍表名
$id = $data['id'];
unset($data['id']);
$result = $wpdb->update(
$table_name,
$data,
array('id' => $id),
array(
'%s', // title
'%s', // author
'%s', // publication_date
),
array('%d')
);
if ( false === $result ) {
return new WP_Error( 'db_update_error', __( '数据库更新错误。', 'my-plugin' ), array( 'status' => 500 ) );
}
return true; // 返回 true 表示更新成功
}
/**
* 删除书籍从数据库(示例,需要替换为你的实际数据删除逻辑)
*
* @param int $id 书籍ID
* @param bool $force 是否强制删除
* @return bool|WP_Error 删除结果或错误对象
*/
private function delete_book( $id, $force = false ) {
global $wpdb;
$table_name = $wpdb->prefix . 'books'; // 你的书籍表名
$result = $wpdb->delete(
$table_name,
array( 'id' => $id ),
array( '%d' )
);
if ( false === $result ) {
return new WP_Error( 'db_delete_error', __( '数据库删除错误。', 'my-plugin' ), array( 'status' => 500 ) );
}
return true; // 返回 true 表示删除成功
}
}
2. 注册控制器
在你的插件主文件中,或者在init
钩子中,注册你的控制器:
<?php
add_action( 'rest_api_init', 'register_book_rest_controller' );
function register_book_rest_controller() {
$controller = new Book_REST_Controller();
$controller->register_routes();
}
3. 方法详解
-
__construct()
: 构造函数,设置API的命名空间和基础路径。 命名空间就像你的API的身份证,确保它在WordPress的API世界里独一无二。 基础路径定义了你的API端点的入口,例如/wp-json/my-plugin/v1/books
。 -
register_routes()
: 注册API路由。 这里定义了你的API端点,以及它们支持的HTTP方法(GET、POST、PUT、DELETE等),以及对应的回调函数。register_rest_route()
函数是关键,它将你的API端点与WordPress的REST API系统关联起来。methods
:指定HTTP方法,例如WP_REST_Server::READABLE
(GET)、WP_REST_Server::CREATABLE
(POST)、WP_REST_Server::EDITABLE
(PUT/PATCH)、WP_REST_Server::DELETABLE
(DELETE)。callback
:指定处理请求的回调函数。permission_callback
:指定权限验证的回调函数。args
:定义API端点接受的参数,并进行验证和清理。
-
get_items()
: 获取书籍列表。 这个方法负责查询数据库,获取书籍数据,并将数据格式化为REST API可以返回的JSON格式。 记得使用rest_ensure_response()
函数将数据包装成WP_REST_Response
对象。 -
get_item()
: 获取单个书籍。 根据ID查询数据库,获取单个书籍的信息。 -
create_item()
: 创建书籍。 接收POST请求的数据,验证数据,然后将数据保存到数据库。 返回新创建的书籍的信息,并设置HTTP状态码为201(Created)。 -
update_item()
: 更新书籍。 接收PUT/PATCH请求的数据,根据ID更新数据库中的书籍信息。 -
delete_item()
: 删除书籍。 根据ID删除数据库中的书籍信息。 可以选择是否强制删除(跳过回收站)。 -
get_items_permissions_check()
、get_item_permissions_check()
、create_item_permissions_check()
、update_item_permissions_check()
、delete_item_permissions_check()
: 权限验证。 这些方法负责验证当前用户是否有权限访问API端点。 你可以根据用户的角色、权限等进行验证。 如果用户没有权限,返回WP_Error
对象,并设置相应的HTTP状态码(例如401 Unauthorized、403 Forbidden)。 -
prepare_item_for_response()
: 格式化响应数据。 将从数据库获取的数据格式化为REST API可以返回的JSON格式。 你可以根据需要添加额外的字段,例如链接信息。 -
get_item_schema()
: 定义API的数据结构。 这个方法返回一个JSON Schema,描述了API端点返回的数据结构。 它可以用于生成API文档,以及客户端的数据验证。 -
prepare_item_for_database()
: 准备用于数据库存储的数据。 对接收到的数据进行清理和验证,确保数据安全和有效。
4. 实战演练:代码拆解分析
咱们来深入分析一下几个关键方法:
-
register_routes()
public function register_routes() { register_rest_route( $this->namespace, '/' . $this->rest_base, array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_items' ), 'permission_callback' => array( $this, 'get_items_permissions_check' ), ), 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( WP_REST_Server::CREATABLE ), ), ) ); // ... 其他路由 }
register_rest_route()
:这个函数是核心,它将你的API端点注册到WordPress REST API系统中。$this->namespace
:API命名空间,例如my-plugin/v1
。'/' . $this->rest_base
:API端点路径,例如/books
。methods
:HTTP方法,例如WP_REST_Server::READABLE
(GET)、WP_REST_Server::CREATABLE
(POST)。callback
:处理请求的回调函数,例如array( $this, 'get_items' )
。permission_callback
:权限验证回调函数,例如array( $this, 'get_items_permissions_check' )
。args
:API参数,使用$this->get_endpoint_args_for_item_schema()
从schema中获取。
-
get_items()
public function get_items( $request ) { $books = $this->get_books(); // 获取书籍数据 $data = array(); foreach ( $books as $book ) { $item = $this->prepare_item_for_response( $book, $request ); // 格式化数据 $data[] = $this->prepare_response_for_collection( $item ); // 确保返回结果是数组 } return rest_ensure_response( $data ); // 返回响应 }
$this->get_books()
:这个方法负责从数据库或其他数据源获取书籍数据。 你需要根据自己的实际情况来实现这个方法。$this->prepare_item_for_response()
:这个方法负责将书籍数据格式化为REST API可以返回的JSON格式。rest_ensure_response()
:这个函数将数据包装成WP_REST_Response
对象,这是REST API的标准响应格式。$this->prepare_response_for_collection($item)
: 确保返回的数据可以被正确处理为数组类型,这在返回集合数据时非常重要
-
prepare_item_for_response()
public function prepare_item_for_response( $item, $request ) { $data = array( 'id' => (int) $item->id, 'title' => $item->title, 'author' => $item->author, 'publication_date' => $item->publication_date, ); $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; $data = $this->filter_response_by_context( $data, $context ); // 增加链接信息 $links = array( 'self' => array( 'href' => rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $item->id ) ), ), 'collection' => array( 'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ), ), ); $response = rest_ensure_response( $data ); $response->add_links( $links ); return $response; }
- 这个方法负责将书籍数据格式化为REST API可以返回的JSON格式。
- 你可以根据需要添加额外的字段,例如链接信息。
$request['context']
:这个参数可以用于控制返回的数据的详细程度。 例如,context=view
可以返回简要信息,context=edit
可以返回完整信息。$this->filter_response_by_context()
:这个方法可以根据context
参数过滤返回的数据。
-
get_item_schema()
public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'book', 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( '唯一标识符', 'my-plugin' ), 'type' => 'integer', 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, ), 'title' => array( 'description' => __( '书籍标题', 'my-plugin' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), 'required' => true, ), // ... 其他属性 ), ); return $this->add_additional_fields_schema( $schema ); }
- 这个方法定义了API的数据结构。
$schema['properties']
:定义了API端点返回的每个字段的类型、描述、context等信息。arg_options
:定义了API端点接受的参数的验证和清理规则。 例如,sanitize_callback
用于清理数据,validate_callback
用于验证数据。
三、高级技巧:让你的API更上一层楼
- 使用缓存: 对于经常访问的数据,可以使用WordPress的缓存API,减少数据库查询次数,提高API的性能。
- 分页: 对于大量数据,可以使用分页功能,避免一次性返回所有数据,提高API的响应速度。
- 版本控制: 当你的API需要进行重大变更时,可以使用版本控制,保持向后兼容性,避免影响现有客户端。
- 自定义路由: 除了标准的CRUD操作,你还可以定义自定义路由,实现更复杂的功能。
四、总结:你的API之旅才刚刚开始
通过继承WP_REST_Controller
,你可以快速构建出功能完善、安全可靠的REST API端点。 记住,代码只是工具,真正的关键在于理解REST API的设计原则,以及你的业务需求。 希望今天的讲座能帮助你开启你的API之旅,让你的WordPress项目更加强大!
记住,代码就像段子,需要不断练习,才能达到炉火纯青的地步。 祝各位编程愉快!