阐述 WordPress `register_rest_route()` 函数的源码:如何注册自定义 REST API 路由。

各位码农朋友们,早上好!今天咱们来聊聊 WordPress 里那些让人又爱又恨的 REST API 路由。说“爱”,是因为它让我们能用各种前端框架,甚至其他语言的程序,轻松跟 WordPress 后台“眉来眼去”;说“恨”,是因为有时搞不清楚那些参数,注册半天也跑不通。

好啦,废话不多说,今天咱们就深入剖析一下 register_rest_route() 这个函数,看看它到底是怎么工作的,以及怎么用它来注册我们自己的 API 路由。

一、register_rest_route() 函数的基本结构

register_rest_route() 函数,顾名思义,就是用来注册 REST API 路由的。它的基本结构是这样的:

register_rest_route( string $namespace, string $route, array $args = array(), bool $override = false )

咱们一个个参数来解释:

  • $namespace (字符串): 这个参数就像一个“门牌号”,用来区分不同的 API 集合。 比如说,WordPress 内置的 API 路由都在 wp/v2 这个命名空间下。 我们自己的 API 路由,最好也起一个有意义的命名空间,比如 my-plugin/v1。 命名空间必须符合正则表达式 ^[a-zA-Z0-9_-]+$。 简单来说,就是只能包含字母、数字、下划线和短横线。

  • $route (字符串): 这个参数是 API 路由的“路径”。 就像网站的 URL 一样,决定了访问哪个 API 端点。 比如 /posts 就是获取文章列表的路由。 路由必须以斜杠 / 开头。 路由中可以使用正则表达式来匹配参数,比如 /posts/(?P<id>d+), 这里的 (?P<id>d+) 就是一个正则表达式,用来匹配一个数字,并将其命名为 id

  • $args (数组): 这个参数最重要,它定义了 API 路由的行为,包括允许哪些 HTTP 方法、谁可以访问、以及如何处理请求。 $args 数组里的常用键包括:

    • methods (字符串或数组): 指定允许的 HTTP 方法。 常用的方法有 GETPOSTPUTDELETE。 可以是一个字符串,比如 'GET',也可以是一个数组,比如 array( 'GET', 'POST' )
    • callback (回调函数): 指定处理 API 请求的回调函数。 这个函数必须接收一个 WP_REST_Request 对象作为参数,并返回一个 WP_REST_Response 对象,或者一个 WordPress 错误对象 WP_Error
    • permission_callback (回调函数): 指定权限验证的回调函数。 这个函数必须返回 true 如果用户有权限访问,否则返回 false 或者一个 WP_Error 对象。 如果没有指定 permission_callback, 默认情况下,只有登录用户才能访问。
    • args (数组): 定义 API 请求的参数。 可以指定参数的类型、是否必须、默认值等等。
  • $override (布尔值): 这个参数决定了是否覆盖已存在的路由。 默认值是 false,表示不覆盖。 如果设置为 true, 就会覆盖同命名空间和路由下的已存在路由。 一般情况下,我们都设置为 false,以免不小心覆盖了其他插件或主题的路由。

二、一个简单的例子:获取文章标题

咱们先来一个最简单的例子,创建一个 API 路由,用来获取指定文章的标题。

add_action( 'rest_api_init', function () {
  register_rest_route( 'my-plugin/v1', '/posts/(?P<id>d+)/title', array(
    'methods'  => 'GET',
    'callback' => 'my_plugin_get_post_title',
    'permission_callback' => '__return_true', // 允许所有人访问
  ) );
} );

function my_plugin_get_post_title( WP_REST_Request $request ) {
  $id = $request['id']; // 获取文章 ID

  $post = get_post( $id );

  if ( empty( $post ) ) {
    return new WP_Error( 'post_not_found', '找不到该文章', array( 'status' => 404 ) );
  }

  return rest_ensure_response( array( 'title' => get_the_title( $id ) ) );
}

这段代码做了什么呢?

  1. add_action( 'rest_api_init', ... ) 这个函数将一个匿名函数绑定到 rest_api_init 钩子上。 rest_api_init 钩子会在 REST API 初始化的时候被触发。 也就是说,这段代码会在 REST API 初始化的时候执行。
  2. register_rest_route( ... ) 这就是注册 REST API 路由的关键函数。
    • 'my-plugin/v1': 命名空间,表示这个 API 属于 my-plugin 插件的 v1 版本。
    • '/posts/(?P<id>d+)/title': 路由。 (?P<id>d+) 是一个正则表达式,用来匹配文章 ID。 (?P<name>pattern) 是 PHP 的命名捕获组语法,它匹配 pattern 并将匹配到的内容存储到 name 变量中。
    • array(...): 参数数组。
      • 'methods' => 'GET': 只允许 GET 方法访问。
      • 'callback' => 'my_plugin_get_post_title': 指定处理请求的回调函数为 my_plugin_get_post_title
      • 'permission_callback' => '__return_true': 允许所有人访问。 __return_true 是一个 WordPress 内置的函数,总是返回 true
  3. my_plugin_get_post_title( WP_REST_Request $request ) 这个函数是处理 API 请求的回调函数。
    • $id = $request['id']: 从请求对象中获取文章 ID。 注意,这里的 'id' 就是路由中正则表达式 (?P<id>d+) 中的 id
    • $post = get_post( $id ): 根据文章 ID 获取文章对象。
    • if ( empty( $post ) ) { ... }: 如果文章不存在,返回一个 WP_Error 对象。
    • return rest_ensure_response( array( 'title' => get_the_title( $id ) ) ): 如果文章存在,返回一个包含文章标题的 WP_REST_Response 对象。 rest_ensure_response() 函数确保返回值是一个 WP_REST_Response 对象。

将这段代码放到你的主题的 functions.php 文件或者你的插件里,然后你就可以通过以下 URL 访问这个 API 路由了:

http://your-wordpress-site.com/wp-json/my-plugin/v1/posts/123/title

其中 123 是文章 ID。 访问这个 URL,你将会得到一个 JSON 格式的响应,包含文章的标题:

{
  "title": "文章标题"
}

三、更复杂的例子:创建文章

接下来,咱们来一个更复杂的例子,创建一个 API 路由,用来创建文章。

add_action( 'rest_api_init', function () {
  register_rest_route( 'my-plugin/v1', '/posts', array(
    'methods'  => 'POST',
    'callback' => 'my_plugin_create_post',
    'permission_callback' => 'my_plugin_check_permission', // 检查权限
    'args' => array(
      'title' => array(
        'required' => true,
        'type' => 'string',
        'description' => '文章标题',
      ),
      'content' => array(
        'required' => true,
        'type' => 'string',
        'description' => '文章内容',
      ),
      'status' => array(
        'required' => false,
        'type' => 'string',
        'default' => 'draft',
        'enum' => array( 'draft', 'publish', 'pending', 'private' ),
        'description' => '文章状态',
      ),
    ),
  ) );
} );

function my_plugin_create_post( WP_REST_Request $request ) {
  $title = $request['title'];
  $content = $request['content'];
  $status = $request['status'];

  $post_id = wp_insert_post( array(
    'post_title'   => $title,
    'post_content' => $content,
    'post_status'  => $status,
  ) );

  if ( is_wp_error( $post_id ) ) {
    return $post_id; // 返回 WP_Error 对象
  }

  return rest_ensure_response( array( 'id' => $post_id ) );
}

function my_plugin_check_permission( WP_REST_Request $request ) {
  if ( ! current_user_can( 'publish_posts' ) ) {
    return new WP_Error( 'rest_forbidden', '你没有权限创建文章', array( 'status' => 401 ) );
  }

  return true;
}

这个例子比之前的例子复杂一些,主要增加了以下内容:

  1. 'methods' => 'POST' 只允许 POST 方法访问。 创建文章通常使用 POST 方法。
  2. 'permission_callback' => 'my_plugin_check_permission' 指定权限验证的回调函数为 my_plugin_check_permission
  3. 'args' => array(...) 定义 API 请求的参数。 这里定义了三个参数: titlecontentstatus
    • 'title': 文章标题。
      • 'required' => true: 表示这个参数是必须的。
      • 'type' => 'string': 表示这个参数的类型是字符串。
      • 'description' => '文章标题': 参数描述。 这个描述会显示在 API 文档中。
    • 'content': 文章内容。
      • 'required' => true: 表示这个参数是必须的。
      • 'type' => 'string': 表示这个参数的类型是字符串。
      • 'description' => '文章内容': 参数描述。
    • 'status': 文章状态。
      • 'required' => false: 表示这个参数不是必须的。
      • 'type' => 'string': 表示这个参数的类型是字符串。
      • 'default' => 'draft': 表示这个参数的默认值是 draft
      • 'enum' => array( 'draft', 'publish', 'pending', 'private' ): 表示这个参数的值只能是 draftpublishpendingprivate 中的一个。
      • 'description' => '文章状态': 参数描述。
  4. my_plugin_check_permission( WP_REST_Request $request ) 这个函数是权限验证的回调函数。
    • if ( ! current_user_can( 'publish_posts' ) ) { ... }: 判断当前用户是否有 publish_posts 权限。 如果没有,返回一个 WP_Error 对象。 current_user_can() 是 WordPress 内置的函数,用来判断用户是否具有指定的权限。
  5. my_plugin_create_post( WP_REST_Request $request ) 这个函数是处理 API 请求的回调函数。
    • $title = $request['title']: 从请求对象中获取文章标题。
    • $content = $request['content']: 从请求对象中获取文章内容。
    • $status = $request['status']: 从请求对象中获取文章状态。
    • $post_id = wp_insert_post( ... ): 创建文章。 wp_insert_post() 是 WordPress 内置的函数,用来创建或更新文章。
    • if ( is_wp_error( $post_id ) ) { ... }: 如果创建文章失败,返回一个 WP_Error 对象。
    • return rest_ensure_response( array( 'id' => $post_id ) ): 如果创建文章成功,返回一个包含文章 ID 的 WP_REST_Response 对象。

要测试这个 API 路由,你可以使用 curl 命令:

curl -X POST 
  http://your-wordpress-site.com/wp-json/my-plugin/v1/posts 
  -H 'Content-Type: application/json' 
  -u 'username:password' 
  -d '{
    "title": "新的文章标题",
    "content": "新的文章内容",
    "status": "publish"
  }'

其中 usernamepassword 是你的 WordPress 用户名和密码。 你需要替换 your-wordpress-site.com 为你的 WordPress 网站的域名。

如果创建文章成功,你将会得到一个 JSON 格式的响应,包含文章的 ID:

{
  "id": 124
}

四、$args 数组详解

$args 数组是 register_rest_route() 函数中最关键的参数。 咱们来更详细地了解一下它的常用键:

类型 描述
methods 字符串/数组 指定允许的 HTTP 方法。 常用的方法有 GETPOSTPUTDELETE
callback 回调函数 指定处理 API 请求的回调函数。 这个函数必须接收一个 WP_REST_Request 对象作为参数,并返回一个 WP_REST_Response 对象,或者一个 WordPress 错误对象 WP_Error
permission_callback 回调函数 指定权限验证的回调函数。 这个函数必须返回 true 如果用户有权限访问,否则返回 false 或者一个 WP_Error 对象。 如果没有指定 permission_callback, 默认情况下,只有登录用户才能访问。
args 数组 定义 API 请求的参数。 可以指定参数的类型、是否必须、默认值等等。
schema 数组/回调函数 定义 API 响应的数据结构。 可以用来生成 API 文档,或者进行数据验证。

其中 args 键的值是一个数组,数组的每个元素都定义了一个参数。 参数的常用键包括:

类型 描述
required 布尔值 指定参数是否必须。
type 字符串 指定参数的类型。 常用的类型有 stringintegernumberbooleanarrayobject
description 字符串 参数描述。 这个描述会显示在 API 文档中。
default 任何类型 参数的默认值。
enum 数组 指定参数的值只能是数组中的一个。
validate_callback 回调函数 用于验证参数的回调函数。 这个函数接收参数的值作为输入,如果参数有效,则返回 true,否则返回 falseWP_Error 对象。
sanitize_callback 回调函数 用于清理参数的回调函数。 这个函数接收参数的值作为输入,并返回清理后的值。

五、一些需要注意的地方

  • 命名空间: 选择一个有意义的命名空间,避免与其他插件或主题冲突。
  • 权限验证: 一定要进行权限验证,避免未经授权的访问。
  • 错误处理: 在回调函数中,要处理各种错误情况,并返回合适的 WP_Error 对象。
  • 数据验证和清理: 对 API 请求的参数进行验证和清理,避免安全漏洞。
  • API 文档: 使用 schema 参数定义 API 响应的数据结构,可以用来生成 API 文档。

六、总结

register_rest_route() 函数是 WordPress REST API 的核心。 掌握了它,你就可以轻松地创建自己的 API 路由,并与 WordPress 后台进行交互。 希望今天的讲座对你有所帮助。 记住,实践是检验真理的唯一标准,多写代码,多尝试,才能真正掌握 WordPress REST API。

下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注