各位观众老爷们,大家好!
今天咱们聊聊WordPress REST API的基石:WP_REST_Controller
。 这玩意儿听起来玄乎,其实就是个用来偷懒的工具。 程序员嘛,天生就喜欢偷懒,能复用的绝不重写,能抽象的绝不手撸。 WP_REST_Controller
就是为了让咱们在构建REST API的时候,少写重复代码,更优雅地处理各种请求。
咱们先来缕缕思路,搞清楚WP_REST_Controller
要解决什么问题。
一、REST API 的常见套路
构建 REST API,无非就是这几个步骤:
- 注册路由 (Routes): 告诉 WordPress,当收到特定 URL 的请求时,应该调用哪个函数来处理。
- 验证权限 (Permissions): 判断用户是否有权访问这个 API。 比如,不是管理员就不能删除文章,不是作者就不能编辑别人的文章。
- 处理请求 (Requests): 接收请求参数,进行数据处理,比如查询数据库、更新数据等。
- 序列化响应 (Responses): 将处理结果转换成 JSON 格式,返回给客户端。
每个 API 接口都要重复这些步骤,想想就头疼。 WP_REST_Controller
就是为了把这些通用逻辑抽出来,让咱们专注于业务逻辑。
二、WP_REST_Controller
登场:架构设计概览
WP_REST_Controller
是一个抽象类,咱们不能直接实例化它。 必须创建一个继承自它的子类,并重写一些方法,才能实现具体的 API 功能。
它的核心思想是:把 API 的通用逻辑封装在基类中,把特定的业务逻辑留给子类来实现。 这样,咱们只需要关注 API 的具体功能,而不用操心那些重复的、繁琐的步骤。
WP_REST_Controller
类本身提供了一些常用的方法和属性,主要包括:
$namespace
(string): API 的命名空间,用于区分不同的 API 集合。 比如wp/v2
是 WordPress 核心的 API 命名空间。$rest_base
(string): API 的基本路由,用于构建 API 的 URL。 比如posts
是文章的 API 基本路由。register_routes()
: 注册 API 路由的核心方法,需要在子类中重写。get_item()
: 获取单个数据项的方法,需要在子类中重写。get_items()
: 获取数据项列表的方法,需要在子类中重写。create_item()
: 创建数据项的方法,需要在子类中重写。update_item()
: 更新数据项的方法,需要在子类中重写。delete_item()
: 删除数据项的方法,需要在子类中重写。get_item_schema()
: 获取数据项的 schema,用于验证请求参数和生成 API 文档。
这些方法都是抽象方法,也就是说,咱们必须在子类中实现它们,才能让 API 正常工作。
三、实战演练:创建一个简单的 API
光说不练假把式,咱们来创建一个简单的 API,演示一下 WP_REST_Controller
的用法。
假设我们要创建一个 API,用于获取自定义文章类型 book
的列表。
1. 定义文章类型:
首先,注册一个自定义文章类型 book
。
add_action( 'init', 'register_book_post_type' );
function register_book_post_type() {
$args = array(
'public' => true,
'label' => 'Books',
'supports' => array( 'title', 'editor', 'custom-fields' ),
'show_in_rest' => true, // 确保在 REST API 中显示
);
register_post_type( 'book', $args );
}
show_in_rest
设置为 true ,表示这个文章类型应该出现在 REST API 中。 WordPress 会自动创建一个基本的 API 接口,用于获取 book
类型的文章。但是,如果我们想自定义 API 的行为,就需要用到 WP_REST_Controller
。
2. 创建控制器类:
创建一个名为 Book_REST_Controller
的类,继承自 WP_REST_Controller
。
class Book_REST_Controller extends WP_REST_Controller {
/**
* 构造函数
*/
public function __construct() {
$this->namespace = 'my-plugin/v1';
$this->rest_base = 'books';
}
/**
* 注册路由
*/
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' ),
),
'schema' => array( $this, 'get_item_schema' ),
) );
register_rest_route( $this->namespace, '/' . $this->rest_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(
'id' => array(
'validate_callback' => function( $param, $request, $key ) {
return is_numeric( $param );
}
),
),
),
'schema' => array( $this, 'get_item_schema' ),
) );
}
/**
* 检查是否有权限获取数据项列表
*
* @param WP_REST_Request $request 请求对象
* @return WP_Error|bool
*/
public function get_items_permissions_check( $request ) {
// 例如:只有登录用户才能访问
//if ( ! is_user_logged_in() ) {
// return new WP_Error( 'rest_forbidden', '需要登录才能访问', array( 'status' => 401 ) );
//}
return true; // 允许所有用户访问
}
/**
* 获取数据项列表
*
* @param WP_REST_Request $request 请求对象
* @return WP_REST_Response|WP_Error
*/
public function get_items( $request ) {
$args = array(
'post_type' => 'book',
'posts_per_page' => 10, // 可以通过请求参数来控制
);
$query = new WP_Query( $args );
$data = array();
foreach ( $query->posts as $post ) {
$item = $this->prepare_item_for_response( $post, $request );
$data[] = $this->prepare_response_for_collection( $item );
}
return new WP_REST_Response( $data, 200 );
}
/**
* 检查是否有权限获取单个数据项
*
* @param WP_REST_Request $request 请求对象
* @return WP_Error|bool
*/
public function get_item_permissions_check( $request ) {
return $this->get_items_permissions_check( $request );
}
/**
* 获取单个数据项
*
* @param WP_REST_Request $request 请求对象
* @return WP_REST_Response|WP_Error
*/
public function get_item( $request ) {
$id = (int) $request['id'];
$post = get_post( $id );
if ( empty( $post ) || $post->post_type !== 'book' ) {
return new WP_Error( 'rest_not_found', '找不到该书', array( 'status' => 404 ) );
}
$data = $this->prepare_item_for_response( $post, $request );
return new WP_REST_Response( $data, 200 );
}
/**
* 准备数据项的响应
*
* @param WP_Post $post 文章对象
* @param WP_REST_Request $request 请求对象
* @return WP_REST_Response
*/
public function prepare_item_for_response( $post, $request ) {
$data = array(
'id' => $post->ID,
'title' => $post->post_title,
'content' => apply_filters( 'the_content', $post->post_content ),
'author' => get_the_author_meta( 'display_name', $post->post_author ),
'meta' => get_post_meta( $post->ID ),
);
$response = new WP_REST_Response( $data, 200 );
$response->add_link( 'self', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $post->ID ) ) );
return $response;
}
/**
* 获取数据项的 schema
*
* @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' => 'The ID of the book.',
'type' => 'integer',
'context' => array( 'view', 'edit', 'embed' ),
'readonly' => true,
),
'title' => array(
'description' => 'The title of the book.',
'type' => 'string',
'context' => array( 'view', 'edit', 'embed' ),
),
'content' => array(
'description' => 'The content of the book.',
'type' => 'string',
'context' => array( 'view', 'edit' ),
),
'author' => array(
'description' => 'The author of the book.',
'type' => 'string',
'context' => array( 'view', 'edit', 'embed' ),
),
),
);
return $schema;
}
}
3. 注册控制器:
在 WordPress 初始化时,注册这个控制器。
add_action( 'rest_api_init', 'register_book_rest_controller' );
function register_book_rest_controller() {
$controller = new Book_REST_Controller();
$controller->register_routes();
}
4. 代码解析:
__construct()
: 构造函数,设置 API 的命名空间和基本路由。$this->namespace
设置为my-plugin/v1
,$this->rest_base
设置为books
。 这意味着我们的 API 的 URL 将会是my-plugin/v1/books
。register_routes()
: 注册 API 路由。register_rest_route()
函数用于注册路由。 第一个参数是命名空间,第二个参数是路由规则,第三个参数是路由的处理方式。 我们注册了两个路由:my-plugin/v1/books
: 用于获取book
类型的文章列表。methods
设置为WP_REST_Server::READABLE
,表示只允许 GET 请求。callback
设置为$this->get_items
,表示当收到 GET 请求时,调用get_items()
方法来处理。permission_callback
设置为$this->get_items_permissions_check
,表示在调用get_items()
方法之前,先调用get_items_permissions_check()
方法来检查用户是否有权限访问。my-plugin/v1/books/(?P<id>[d]+)
: 用于获取单个book
类型的文章。(?P<id>[d]+)
是一个正则表达式,用于匹配文章的 ID。id
是一个参数,可以在get_item()
方法中通过$request['id']
来获取。
get_items_permissions_check()
: 检查是否有权限获取数据项列表。 这里为了简单起见,直接返回true
,表示允许所有用户访问。 在实际项目中,应该根据用户的角色和权限来判断是否允许访问。get_items()
: 获取数据项列表。 这个方法使用WP_Query
来查询book
类型的文章。 然后,遍历查询结果,调用prepare_item_for_response()
方法将文章对象转换成 API 响应的格式。get_item_permissions_check()
: 检查是否有权限获取单个数据项。get_item()
: 获取单个数据项。 这个方法通过文章 ID 来获取文章对象。 如果找不到文章,或者文章类型不是book
,则返回一个错误。 否则,调用prepare_item_for_response()
方法将文章对象转换成 API 响应的格式。prepare_item_for_response()
: 准备数据项的响应。 这个方法将文章对象转换成 API 响应的格式。 它创建一个包含文章 ID、标题、内容和作者信息的数组。 然后,创建一个WP_REST_Response
对象,并将数据数组设置成响应的内容。 最后,添加一个self
链接,指向该文章的 API 接口。get_item_schema()
: 获取数据项的 schema。 Schema 用于描述 API 响应的数据结构。 它可以用于验证请求参数和生成 API 文档。
5. 测试 API:
现在,就可以通过 REST API 来访问 book
类型的文章了。
- 获取文章列表: 访问
your-website.com/wp-json/my-plugin/v1/books
可以获取book
类型的文章列表。 - 获取单个文章: 访问
your-website.com/wp-json/my-plugin/v1/books/123
可以获取 ID 为 123 的book
类型的文章。
四、WP_REST_Controller
的优势
使用 WP_REST_Controller
有以下几个优势:
- 代码复用: 将 API 的通用逻辑封装在基类中,减少重复代码。
- 代码组织: 将 API 的代码组织成类,提高代码的可读性和可维护性。
- 权限控制: 提供了一套标准的权限控制机制,方便进行权限验证。
- 数据验证: 通过 schema 验证请求参数,防止非法数据。
- API 文档: 通过 schema 生成 API 文档,方便开发者使用。
五、高级技巧:定制化与扩展
WP_REST_Controller
虽然提供了很多便利,但有时候也需要进行定制化和扩展,以满足特定的需求。
- 自定义路由: 除了标准的 CRUD 操作之外,还可以注册自定义的路由。
- 自定义权限验证: 可以根据用户的角色和权限,实现更复杂的权限验证逻辑。
- 自定义数据处理: 可以对请求参数进行预处理,或者对响应数据进行后处理。
- 自定义序列化: 可以使用不同的序列化方式,比如 XML 或 YAML。
六、WP_REST_Controller
方法详解
方法名 | 作用 | 是否需要在子类中重写 |
---|---|---|
register_routes() |
注册 API 路由。这是最核心的方法,定义了 API 的 URL 和处理方式。 | 是 |
get_item() |
获取单个数据项。 | 是 |
get_items() |
获取数据项列表。 | 是 |
create_item() |
创建数据项。 | 是 (如果需要) |
update_item() |
更新数据项。 | 是 (如果需要) |
delete_item() |
删除数据项。 | 是 (如果需要) |
get_item_schema() |
获取数据项的 schema,用于验证请求参数和生成 API 文档。 | 是 (建议) |
get_item_permissions_check() |
检查是否有权限获取单个数据项。 | 否 (可以重写) |
get_items_permissions_check() |
检查是否有权限获取数据项列表。 | 否 (可以重写) |
prepare_item_for_response() |
准备数据项的响应。将数据项对象转换成 API 响应的格式。 | 否 (可以重写) |
prepare_response_for_collection() |
准备集合的响应。将单个数据项的响应包装成集合的格式。 | 否 (一般不需要) |
get_endpoint_args_for_schema() |
获取 endpoint 的参数,用于验证请求参数。 | 否 (可以重写) |
七、总结
WP_REST_Controller
是构建 WordPress REST API 的利器。 掌握了它的用法,就可以更高效、更优雅地构建 API 接口。 当然,这只是个开始, REST API 的世界还有很多值得探索的地方。 希望今天的讲解能帮助大家入门,以后有机会咱们再深入研究。
今天的讲座就到这里,谢谢大家!