探究 WordPress `rest_api_init` 钩子源码:在 REST API 初始化时如何注册自定义路由。

各位观众老爷,大家好!今天咱们来聊聊 WordPress REST API 的 rest_api_init 钩子,看看它怎么帮咱们注册自定义路由,让 WordPress 的 API 变得更加强大,更加听话。

开场白:REST API,WordPress 的新玩具

话说 WordPress 越来越现代化,不仅仅是个博客系统了,还想做个内容管理平台(CMS),甚至是应用程序的后端。这就要用到 REST API 了。REST API 就像一个翻译官,让不同的程序(比如你的前端应用和 WordPress 后台)能够互相交流,互通有无。

WordPress 已经自带了一套 REST API,但是它提供的路由可能不够你用。这时候,就需要咱们自己动手,注册自定义路由,定制个性化的 API 端点。rest_api_init 钩子,就是咱们定制 API 的入口。

rest_api_init:API 初始化时的秘密通道

rest_api_init 钩子是一个 action hook,它在 REST API 初始化的时候被触发。你可以把它理解成一个“秘密通道”,WordPress 会在特定的时刻打开这个通道,允许你添加自定义的 API 路由。

如何使用 rest_api_init 钩子?

使用 rest_api_init 钩子很简单,只需要以下几步:

  1. 定义一个函数: 这个函数将包含你注册自定义路由的代码。
  2. 将函数挂载到 rest_api_init 钩子上: 使用 add_action() 函数将你的函数挂载到 rest_api_init 钩子上。
// 定义注册自定义路由的函数
function my_register_routes() {
  // 在这里注册你的自定义路由
}

// 将函数挂载到 rest_api_init 钩子上
add_action( 'rest_api_init', 'my_register_routes' );

就这么简单!现在,my_register_routes() 函数会在 REST API 初始化的时候被调用。

注册自定义路由的关键:register_rest_route()

注册自定义路由的核心函数是 register_rest_route()。它接受三个参数:

  1. 命名空间(Namespace): 用于组织你的 API 路由,避免与其他插件的路由冲突。通常使用插件或主题的名称作为命名空间。
  2. 路由(Route): 定义 API 端点的 URL 路径。例如,/my-plugin/v1/items
  3. 参数数组(Arguments Array): 包含路由的配置信息,比如处理请求的回调函数、请求方法、权限验证等。
register_rest_route(
  'my-plugin/v1', // 命名空间
  '/items',       // 路由
  array(          // 参数数组
    'methods'  => 'GET', // 允许的请求方法
    'callback' => 'my_get_items', // 处理请求的回调函数
    'permission_callback' => '__return_true', // 权限验证回调函数
  )
);

让我们详细解释一下参数数组中的几个关键选项:

  • methods 指定允许的 HTTP 请求方法。常用的方法包括 GET(获取数据)、POST(创建数据)、PUT(更新数据)、DELETE(删除数据)。 可以使用单个字符串,例如 'GET',也可以使用数组,例如 array( 'GET', 'POST' )
  • callback 指定处理 API 请求的回调函数。这个函数负责接收请求参数,处理业务逻辑,并返回响应数据。
  • permission_callback 指定权限验证的回调函数。这个函数负责检查用户是否有权限访问该 API 端点。如果用户没有权限,应该返回 false 或一个 WP_Error 对象。如果用户有权限,应该返回 true'__return_true' 表示允许所有用户访问(不建议在生产环境中使用)。

一个完整的例子:获取文章列表

假设我们要创建一个 API 端点,用于获取最新的 10 篇文章列表。 我们可以这样做:

function my_get_latest_posts( $request ) {
  $args = array(
    'posts_per_page' => 10,
    'orderby'        => 'date',
    'order'          => 'DESC',
  );

  $posts = get_posts( $args );

  $data = array();
  foreach ( $posts as $post ) {
    $data[] = array(
      'id'    => $post->ID,
      'title' => $post->post_title,
      'link'  => get_permalink( $post->ID ),
    );
  }

  return $data;
}

function my_register_routes() {
  register_rest_route(
    'my-plugin/v1',
    '/latest-posts',
    array(
      'methods'  => 'GET',
      'callback' => 'my_get_latest_posts',
      'permission_callback' => '__return_true', // 生产环境需要更严格的权限控制
    )
  );
}

add_action( 'rest_api_init', 'my_register_routes' );

在这个例子中:

  • my_get_latest_posts() 函数负责查询最新的 10 篇文章,并将它们转换为一个包含 ID、标题和链接的数组。
  • register_rest_route() 函数将 /my-plugin/v1/latest-posts 路由与 my_get_latest_posts() 函数关联起来。
  • add_action() 函数将 my_register_routes() 函数挂载到 rest_api_init 钩子上。

现在,你可以通过访问 your-wordpress-site.com/wp-json/my-plugin/v1/latest-posts 来获取最新的 10 篇文章列表。

处理请求参数:WP_REST_Request 对象

API 请求通常会包含一些参数,比如搜索关键词、分页信息等。 在你的回调函数中,你可以通过 WP_REST_Request 对象来获取这些参数。

WP_REST_Request 对象包含了请求的所有信息,包括:

  • get_params() 获取所有参数的数组。
  • get_param( $name ) 获取指定名称的参数值。
  • get_query_params() 获取查询字符串参数的数组。
  • get_body_params() 获取请求体参数的数组(通常用于 POSTPUT 等请求)。

例如:

function my_search_posts( $request ) {
  $keyword = $request->get_param( 'keyword' ); // 获取名为 "keyword" 的参数

  $args = array(
    's' => $keyword, // 设置搜索关键词
  );

  $query = new WP_Query( $args );

  $data = array();
  if ( $query->have_posts() ) {
    while ( $query->have_posts() ) {
      $query->the_post();
      $data[] = array(
        'id'    => get_the_ID(),
        'title' => get_the_title(),
        'link'  => get_permalink(),
      );
    }
    wp_reset_postdata(); // 恢复全局文章数据
  }

  return $data;
}

function my_register_routes() {
  register_rest_route(
    'my-plugin/v1',
    '/search',
    array(
      'methods'  => 'GET',
      'callback' => 'my_search_posts',
      'permission_callback' => '__return_true', // 生产环境需要更严格的权限控制
      'args'     => array( // 定义参数
        'keyword' => array(
          'validate_callback' => 'rest_validate_request_arg', // 验证参数是否有效
          'sanitize_callback' => 'sanitize_text_field', // 清理参数
          'required'          => true, // 参数是否必须
          'description'       => '搜索关键词', // 参数描述
        ),
      ),
    )
  );
}

add_action( 'rest_api_init', 'my_register_routes' );

在这个例子中:

  • my_search_posts() 函数从 WP_REST_Request 对象中获取名为 "keyword" 的参数,并使用它来搜索文章。
  • register_rest_route() 函数的 args 数组定义了 "keyword" 参数的验证、清理和描述信息。

返回响应数据:WP_REST_Response 对象

你的回调函数应该返回一个 WP_REST_Response 对象或一个 WP_Error 对象。

  • WP_REST_Response 用于返回成功的响应数据。你可以使用它来设置响应状态码、头部信息和数据。
  • WP_Error 用于返回错误信息。你可以使用它来设置错误代码、错误消息和错误数据。
function my_get_item( $request ) {
  $id = $request->get_param( 'id' );

  $post = get_post( $id );

  if ( ! $post ) {
    return new WP_Error( 'item_not_found', 'Item not found.', array( 'status' => 404 ) );
  }

  $data = array(
    'id'    => $post->ID,
    'title' => $post->post_title,
    'content' => $post->post_content,
  );

  $response = new WP_REST_Response( $data );
  $response->set_status( 200 ); // 设置状态码

  return $response;
}

function my_register_routes() {
  register_rest_route(
    'my-plugin/v1',
    '/items/(?P<id>d+)', // 使用正则表达式定义路由,允许传入id参数
    array(
      'methods'  => 'GET',
      'callback' => 'my_get_item',
      'permission_callback' => '__return_true',
      'args' => array(
        'id' => array(
          'validate_callback' => 'rest_validate_request_arg',
          'sanitize_callback' => 'absint',
          'required'          => true,
          'description'       => 'Item ID',
        ),
      ),
    )
  );
}

add_action( 'rest_api_init', 'my_register_routes' );

在这个例子中:

  • my_get_item() 函数尝试获取指定 ID 的文章。
  • 如果文章不存在,它会返回一个 WP_Error 对象,状态码为 404。
  • 如果文章存在,它会返回一个 WP_REST_Response 对象,包含文章的 ID、标题和内容,状态码为 200。
  • 路由中使用正则表达式 (?P<id>d+) 定义了一个名为 id 的参数,它必须是一个数字。

权限验证:保护你的 API

权限验证是 REST API 安全的关键。 permission_callback 参数允许你定义一个回调函数,用于检查用户是否有权限访问该 API 端点。

以下是一些常见的权限验证方法:

  • 检查用户是否已登录: 使用 is_user_logged_in() 函数。
  • 检查用户是否具有特定的角色或权限: 使用 current_user_can() 函数。
  • 使用 Nonce 验证: Nonce 是一种安全令牌,可以防止跨站请求伪造(CSRF)攻击。
function my_check_permission( $request ) {
  if ( ! is_user_logged_in() ) {
    return new WP_Error( 'rest_not_logged_in', 'You are not currently logged in.', array( 'status' => 401 ) );
  }

  if ( ! current_user_can( 'edit_posts' ) ) {
    return new WP_Error( 'rest_forbidden', 'You do not have permission to edit posts.', array( 'status' => 403 ) );
  }

  return true;
}

function my_register_routes() {
  register_rest_route(
    'my-plugin/v1',
    '/secure-endpoint',
    array(
      'methods'  => 'POST',
      'callback' => 'my_process_data',
      'permission_callback' => 'my_check_permission', // 使用自定义权限验证函数
    )
  );
}

add_action( 'rest_api_init', 'my_register_routes' );

在这个例子中:

  • my_check_permission() 函数检查用户是否已登录,并且是否具有 edit_posts 权限。
  • 如果用户未登录或没有权限,它会返回一个 WP_Error 对象,状态码分别为 401 和 403。
  • register_rest_route() 函数使用 my_check_permission() 函数作为权限验证回调函数。

总结:rest_api_init 钩子,你的 API 定制利器

rest_api_init 钩子是 WordPress REST API 定制的关键。 通过它,你可以注册自定义路由,处理请求参数,返回响应数据,并进行权限验证。 掌握了这些技巧,你就可以创建出功能强大、安全可靠的 WordPress REST API,让你的 WordPress 站点更加灵活和可扩展。

表格总结:register_rest_route() 函数参数

参数 类型 描述
$namespace string 命名空间,用于组织你的 API 路由,避免与其他插件的路由冲突。
$route string 路由,定义 API 端点的 URL 路径。例如,/my-plugin/v1/items。可以使用正则表达式来定义动态路由,例如 /items/(?P<id>d+)
$args array 参数数组,包含路由的配置信息。
$args['methods'] string/array 指定允许的 HTTP 请求方法。常用的方法包括 GETPOSTPUTDELETE
$args['callback'] callable 指定处理 API 请求的回调函数。这个函数负责接收请求参数,处理业务逻辑,并返回响应数据。
$args['permission_callback'] callable 指定权限验证的回调函数。这个函数负责检查用户是否有权限访问该 API 端点。如果用户没有权限,应该返回 false 或一个 WP_Error 对象。如果用户有权限,应该返回 true
$args['args'] array 定义路由参数的验证、清理和描述信息。每个参数都应该是一个数组,包含 validate_callbacksanitize_callbackrequireddescription 等选项。

彩蛋:REST API 调试工具

调试 REST API 可能会比较麻烦。 幸运的是,有很多工具可以帮助你:

  • Postman: 一个流行的 API 客户端,可以发送各种 HTTP 请求,并查看响应结果。
  • Insomnia: 另一个强大的 API 客户端,功能类似于 Postman。
  • 浏览器的开发者工具: 大多数浏览器都提供了开发者工具,可以用来查看网络请求和响应。
  • WordPress REST API 插件: 有一些 WordPress 插件可以帮助你调试 REST API,比如 "REST API Toolbox"。

希望今天的讲座对大家有所帮助! 祝大家玩转 WordPress REST API,创造出更加精彩的应用!

发表回复

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