深入剖析WordPress REST请求生命周期从register_rest_route到响应输出

好的,我们开始吧。

今天我们来深入探讨 WordPress REST API 请求的生命周期,从注册路由 register_rest_route 开始,一直到最终的响应输出。 这其中涉及的组件繁多,流程复杂,理解这个过程对开发高质量的 REST API 接口至关重要。

一、register_rest_route: 路由注册的起点

在 WordPress 中,要使一个自定义的函数能够通过 REST API 访问,首先需要注册一个路由。这个注册的过程由 register_rest_route 函数完成。

register_rest_route(
    string   $namespace,
    string   $route,
    array    $args   = array(),
    bool     $override = false
);
  • $namespace: API 的命名空间,通常是你的插件或主题的名字。 避免与其他插件冲突。 例如: my-plugin/v1
  • $route: API 的路由,定义了访问该 API 的 URL 结构。 例如: /books/books/(?P<id>d+)。 后者使用了正则表达式,(?P<id>d+) 表示一个名为 id 的参数,其值为一个或多个数字。
  • $args: 一个数组,包含了路由的处理方法和其他配置信息。 这是最关键的部分,我们稍后会详细讲解。
  • $override: 一个布尔值,默认为 false。 如果设置为 true,则允许覆盖已存在的同名路由(不建议这样做,除非你明确知道自己在做什么)。

$args 数组的结构

$args 数组是配置路由行为的核心。 它通常包含以下几个关键的键:

  • methods: 指定允许的 HTTP 方法。 可以是 GET, POST, PUT, DELETE, PATCH 中的一个或多个。 使用 WP_REST_Server::READABLE (等同于 GET), WP_REST_Server::EDITABLE (等同于 POST, PUT, PATCH), WP_REST_Server::DELETABLE (等同于 DELETE) 可以提高代码可读性。
  • callback: 指定处理该路由请求的回调函数。 这个函数负责接收请求数据,执行相应的逻辑,并返回响应数据。
  • permission_callback: 指定一个回调函数,用于检查当前用户是否有权限访问该路由。 如果返回 true,则允许访问;否则,拒绝访问。
  • args: 一个数组,用于定义路由参数的验证和清理规则。

示例

add_action( 'rest_api_init', function () {
    register_rest_route( 'my-plugin/v1', '/books', array(
        'methods'  => 'GET',
        'callback' => 'my_plugin_get_books',
        'permission_callback' => '__return_true', // 允许所有用户访问
    ) );

    register_rest_route( 'my-plugin/v1', '/books/(?P<id>d+)', array(
        'methods'  => 'GET',
        'callback' => 'my_plugin_get_book',
        'permission_callback' => 'my_plugin_check_permission',
        'args' => array(
            'id' => array(
                'validate_callback' => 'is_numeric',
                'sanitize_callback' => 'absint',
                'required'          => true,
                'description'       => 'Book ID.'
            ),
        ),
    ) );
} );

function my_plugin_get_books( WP_REST_Request $request ) {
    // 从数据库获取所有书籍
    $books = get_posts( array( 'post_type' => 'book', 'posts_per_page' => -1 ) );
    $data = array();
    foreach ( $books as $book ) {
        $data[] = array(
            'id'    => $book->ID,
            'title' => $book->post_title,
            'content' => $book->post_content,
        );
    }
    return $data;
}

function my_plugin_get_book( WP_REST_Request $request ) {
    $id = $request['id'];
    $book = get_post( $id );

    if ( empty( $book ) ) {
        return new WP_Error( 'book_not_found', 'Book not found.', array( 'status' => 404 ) );
    }

    return array(
        'id'    => $book->ID,
        'title' => $book->post_title,
        'content' => $book->post_content,
    );
}

function my_plugin_check_permission(WP_REST_Request $request){
    // 检查用户是否具有 'edit_posts' 权限
    if ( ! current_user_can( 'edit_posts' ) ) {
        return new WP_Error( 'rest_forbidden', 'You do not have permission to access this resource.', array( 'status' => 401 ) );
    }

    return true;
}

二、请求接收与路由匹配

当一个 REST API 请求到达 WordPress 时,它会经过以下几个关键步骤:

  1. URL 解析: WordPress 会解析请求的 URL,提取出命名空间和路由信息。
  2. 路由匹配: WordPress REST API 核心会遍历已注册的路由,尝试找到与请求 URL 匹配的路由。
  3. 参数提取: 如果找到匹配的路由,WordPress 会根据路由定义,从 URL 和请求体中提取参数。
  4. 权限检查: 在执行回调函数之前,WordPress 会调用 permission_callback 函数,检查当前用户是否有权限访问该路由。
  5. 回调函数执行: 如果权限检查通过,WordPress 会调用该路由的 callback 函数,并将请求参数传递给该函数。

WP_REST_Request 对象

回调函数接收一个 WP_REST_Request 对象作为参数。 这个对象包含了所有关于请求的信息,包括:

  • get_method(): 返回请求的 HTTP 方法 (例如:’GET’, ‘POST’, ‘PUT’, ‘DELETE’)。
  • get_params(): 返回所有请求参数的数组。
  • get_param( $param ): 返回指定名称的请求参数。
  • get_headers(): 返回所有请求头的数组。
  • get_header( $header ): 返回指定名称的请求头。
  • get_body(): 返回请求体的内容。
  • get_file_params(): 返回上传的文件参数。
  • has_valid_params(): 检查请求参数是否有效(基于路由注册时定义的 args)。

三、参数验证与清理

在回调函数执行之前,REST API 框架会根据 register_rest_routeargs 定义的规则,对请求参数进行验证和清理。

  • validate_callback: 用于验证参数是否符合预期。 如果验证失败,则返回 false 或一个 WP_Error 对象。
  • sanitize_callback: 用于清理参数,使其符合预期格式。 例如,可以使用 absint 将参数转换为正整数。
  • required: 布尔值,指定参数是否是必须的。 如果设置为 true,但请求中缺少该参数,则验证会失败。
  • default: 如果请求中缺少该参数,则使用该默认值。
  • type: 指定参数的类型,例如:string, integer, boolean, array, object. 虽然 WordPress 没有强制类型检查,但可以用于生成 API 文档。
  • description: 参数的描述信息,用于生成 API 文档。

示例

register_rest_route( 'my-plugin/v1', '/items', array(
    'methods'  => 'POST',
    'callback' => 'my_plugin_create_item',
    'args'     => array(
        'title' => array(
            'validate_callback' => 'is_string',
            'sanitize_callback' => 'sanitize_text_field',
            'required'          => true,
            'description'       => 'Item title.'
        ),
        'content' => array(
            'validate_callback' => 'is_string',
            'sanitize_callback' => 'wp_kses_post',
            'description'       => 'Item content.'
        ),
        'status' => array(
            'default'           => 'draft',
            'validate_callback' => function( $param, $request, $key ) {
                return in_array( $param, array( 'draft', 'pending', 'publish' ) );
            },
            'sanitize_callback' => 'sanitize_text_field',
            'description'       => 'Item status.'
        ),
    ),
) );

function my_plugin_create_item( 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;
    }

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

在这个例子中:

  • title 是必须的,必须是字符串,并且会被 sanitize_text_field 函数清理。
  • content 必须是字符串,并且会被 wp_kses_post 函数清理 (用于过滤 HTML 标签)。
  • status 默认为 draft,必须是 draft, pending, publish 中的一个,并且会被 sanitize_text_field 函数清理。

四、响应生成与输出

回调函数负责生成响应数据。 响应数据可以是任何 PHP 数据类型,例如:数组,对象,字符串,数字等。

WP_REST_Response 对象

为了提供更大的灵活性和控制力,建议使用 WP_REST_Response 对象来封装响应数据。

$response = new WP_REST_Response( $data, $status, $headers );
  • $data: 响应数据。
  • $status: HTTP 状态码 (例如:200, 201, 400, 404, 500)。
  • $headers: 响应头。

常用方法

  • set_data( $data ): 设置响应数据。
  • get_data(): 获取响应数据。
  • set_status( $status ): 设置 HTTP 状态码。
  • get_status(): 获取 HTTP 状态码。
  • header( $key, $value ): 设置响应头。
  • get_headers(): 获取所有响应头。
  • link_header( $rel, $link ): 设置链接头 (用于 HATEOAS)。

rest_ensure_response() 函数

为了方便起见,可以使用 rest_ensure_response() 函数来确保响应数据被封装成 WP_REST_Response 对象。

$response = rest_ensure_response( $data );

如果 $data 已经是 WP_REST_Response 对象,则直接返回 $data。 否则,将 $data 封装成一个新的 WP_REST_Response 对象,状态码默认为 200。

错误处理

在 REST API 中,错误处理非常重要。 应该使用 WP_Error 对象来表示错误。

return new WP_Error( $code, $message, $data );
  • $code: 错误代码 (字符串)。
  • $message: 错误消息 (字符串)。
  • $data: 附加数据 (数组)。 通常包含 status 键,用于指定 HTTP 状态码。

示例

function my_plugin_get_book( WP_REST_Request $request ) {
    $id = $request['id'];
    $book = get_post( $id );

    if ( empty( $book ) ) {
        return new WP_Error( 'book_not_found', 'Book not found.', array( 'status' => 404 ) );
    }

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

    return rest_ensure_response( $data );
}

响应输出

当回调函数返回 WP_REST_Response 对象或 WP_Error 对象时,REST API 核心会自动将其转换为 JSON 格式并输出到客户端。 WordPress 会根据 Content-Type 请求头来决定输出的格式。 默认情况下,使用 JSON 格式。

五、中间件与扩展

WordPress REST API 提供了一些钩子,允许开发者在请求处理的不同阶段插入自定义逻辑。 这些钩子可以用于实现各种功能,例如:

  • 身份验证: 自定义身份验证方法。
  • 数据转换: 在数据返回给客户端之前,对其进行转换。
  • 日志记录: 记录 API 请求和响应。
  • 缓存: 缓存 API 响应,提高性能。

常用的钩子

  • rest_authentication_errors: 用于处理身份验证错误。
  • rest_pre_dispatch: 在路由回调函数执行之前执行。
  • rest_post_dispatch: 在路由回调函数执行之后执行。
  • rest_prepare_{$post_type}: 用于修改特定文章类型的 REST API 响应数据。

示例

add_filter( 'rest_prepare_post', 'my_plugin_add_custom_field', 10, 3 );

function my_plugin_add_custom_field( $data, $post, $request ) {
    $data->data['custom_field'] = get_post_meta( $post->ID, 'my_custom_field', true );
    return $data;
}

这个例子展示了如何使用 rest_prepare_post 钩子,在文章的 REST API 响应中添加一个名为 custom_field 的自定义字段。

六、示例表格:关键函数与对象

函数/对象 描述 示例
register_rest_route 注册一个 REST API 路由。 register_rest_route( 'my-plugin/v1', '/books', array( 'methods' => 'GET', 'callback' => 'my_plugin_get_books' ) );
WP_REST_Request 代表一个 REST API 请求。 包含了请求的所有信息,例如:HTTP 方法,参数,头部等。 $request->get_param( 'id' ) 获取名为 ‘id’ 的参数。 $request->get_method() 获取 HTTP 方法。
WP_REST_Response 代表一个 REST API 响应。 包含了响应数据,HTTP 状态码,头部等。 $response = new WP_REST_Response( $data, 200 ); 创建一个 HTTP 状态码为 200 的响应。 $response->set_data( $data ) 设置响应数据。
WP_Error 代表一个错误。 用于在 REST API 中返回错误信息。 return new WP_Error( 'book_not_found', 'Book not found.', array( 'status' => 404 ) ); 创建一个 HTTP 状态码为 404 的错误响应。
rest_ensure_response 确保数据被封装成 WP_REST_Response 对象。 如果数据已经是 WP_REST_Response 对象,则直接返回;否则,将其封装成一个新的 WP_REST_Response 对象。 return rest_ensure_response( $data );
is_numeric PHP 内置函数,检查变量是否为数字或数字字符串。 通常用作 validate_callback 'id' => array( 'validate_callback' => 'is_numeric' )
absint PHP 内置函数,将变量转换为正整数。 通常用作 sanitize_callback 'id' => array( 'sanitize_callback' => 'absint' )
sanitize_text_field WordPress 函数,清理文本字段,移除 HTML 标签和编码特殊字符。 通常用作 sanitize_callback 'title' => array( 'sanitize_callback' => 'sanitize_text_field' )
wp_kses_post WordPress 函数,过滤 HTML 标签,只允许安全的标签和属性。 通常用作 sanitize_callback,用于处理文章内容。 'content' => array( 'sanitize_callback' => 'wp_kses_post' )
current_user_can WordPress 函数,检查当前用户是否具有指定的权限。 通常用在 permission_callback 中。 function my_plugin_check_permission(WP_REST_Request $request){ if ( ! current_user_can( 'edit_posts' ) ) { return new WP_Error( 'rest_forbidden', 'You do not have permission.', array( 'status' => 401 ) ); } return true; }

七、总结:掌握REST API生命周期

至此,我们已经详细地分析了 WordPress REST API 请求的生命周期,从路由注册到响应输出的整个过程。 记住关键的步骤,例如注册路由、验证参数、生成响应以及处理错误,并灵活运用 WordPress 提供的钩子来扩展 API 的功能。 掌握这些知识能够帮助你开发出更健壮、安全和高效的 REST API 接口。

发表回复

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