探究 WordPress `get_rest_response()` 函数源码:如何统一处理 REST API 的响应格式。

各位观众老爷们,大家好!今天咱们来聊聊 WordPress REST API 里一个挺重要的函数:get_rest_response()。这家伙看起来不起眼,实际上它在统一 WordPress REST API 的响应格式方面,扮演着关键角色。咱们的目标是扒开它的源码,看看它到底做了些什么,以及如何利用它来构建更规范的 API。

一、开场白:为什么需要统一响应格式?

想象一下,你正在做一个前端应用,需要从不同的 API 接口获取数据。如果每个接口返回的数据格式都不一样,那你的代码岂不是要针对每个接口写一堆解析逻辑?这简直是噩梦!

统一的响应格式可以解决这个问题。它可以让你的前端代码更加简洁、易于维护,并且可以减少出错的可能性。

WordPress REST API 就是这样做的。它通过 get_rest_response() 函数,尽可能地将所有 API 响应统一成一种标准格式。

二、get_rest_response() 的基本用法

首先,我们来看看 get_rest_response() 函数的基本用法。

<?php
/**
 * Retrieves a REST response.
 *
 * Converts different data types into a REST response.
 *
 * @since 4.7.0
 *
 * @param mixed $data Response data.
 * @param int   $status Optional. The HTTP status code. Default 200.
 *
 * @return WP_REST_Response REST response.
 */
function get_rest_response( $data, $status = 200 ) {
    return new WP_REST_Response( $data, $status );
}

这个函数接收两个参数:

  • $data:要返回的数据。可以是任何类型,例如数组、对象、字符串等等。
  • $status:HTTP 状态码,默认为 200 (OK)。

它会返回一个 WP_REST_Response 对象。这个对象包含了所有关于响应的信息,例如数据、状态码、头部等等。

三、WP_REST_Response 对象:响应的容器

WP_REST_Response 类是 WordPress REST API 中用于封装响应的核心类。它提供了一系列方法来设置和获取响应的各个部分。

下面是一些常用的方法:

方法名 作用
set_data( $data ) 设置响应的数据。
get_data() 获取响应的数据。
set_status( $status ) 设置 HTTP 状态码。
get_status() 获取 HTTP 状态码。
header( $key, $value ) 设置响应头。
get_headers() 获取所有响应头。
add_link( $rel, $href, $attributes = array() ) 添加一个链接到响应中,用于 HATEOAS (Hypermedia as the Engine of Application State)。
remove_link( $rel ) 移除指定关系的链接。

四、get_rest_response() 的源码剖析:关键逻辑

虽然 get_rest_response() 函数本身很简单,但它背后的逻辑却很关键。它实际上创建了一个 WP_REST_Response 对象,并将数据和状态码传递给它。

我们深入 WP_REST_Response 类,来看看它如何处理数据。

<?php

/**
 * Core class used to implement a REST response.
 *
 * @since 4.4.0
 */
class WP_REST_Response {

    /**
     * Response data.
     *
     * @var mixed
     */
    protected $data;

    /**
     * Response status code.
     *
     * @var int
     */
    protected $status = 200;

    /**
     * Response headers.
     *
     * @var array
     */
    protected $headers = array();

    /**
     * Response links.
     *
     * @var array
     */
    protected $links = array();

    /**
     * Constructor.
     *
     * @param mixed $data   Optional. Response data. Default null.
     * @param int   $status Optional. The HTTP status code. Default 200.
     * @param array $headers Optional. Response headers. Default array().
     */
    public function __construct( $data = null, $status = 200, $headers = array() ) {
        $this->set_data( $data );
        $this->set_status( $status );
        $this->set_headers( $headers );
    }

    /**
     * Sets the data for the response.
     *
     * @param mixed $data Data for the response.
     */
    public function set_data( $data ) {
        $this->data = $data;
    }

    /**
     * Retrieves the data for the response.
     *
     * @return mixed Data for the response.
     */
    public function get_data() {
        return $this->data;
    }

    /**
     * Sets the HTTP status code.
     *
     * @param int $status HTTP status code.
     */
    public function set_status( $status ) {
        $this->status = absint( $status );
    }

    /**
     * Retrieves the HTTP status code.
     *
     * @return int HTTP status code.
     */
    public function get_status() {
        return $this->status;
    }

    /**
     * Sets a HTTP header.
     *
     * @param string $key   Header name.
     * @param string $value Header value.
     */
    public function header( $key, $value ) {
        $this->headers[ $key ] = $value;
    }

    /**
     * Sets HTTP headers.
     *
     * @param array $headers Map of header name to header value.
     */
    public function set_headers( $headers ) {
        $this->headers = wp_parse_args( $headers, $this->headers );
    }

    /**
     * Retrieves all HTTP headers.
     *
     * @return array Map of header name to header value.
     */
    public function get_headers() {
        return $this->headers;
    }

    /**
     * Adds a link to the response.
     *
     * @param string $rel        Link relation.
     * @param string $href       Link URL.
     * @param array  $attributes Optional. Link attributes. Default array().
     */
    public function add_link( $rel, $href, $attributes = array() ) {
        if ( empty( $this->links[ $rel ] ) ) {
            $this->links[ $rel ] = array();
        }

        $this->links[ $rel ][] = array(
            'href'       => $href,
            'attributes' => $attributes,
        );
    }

    /**
     * Removes a link from the response.
     *
     * @param string $rel Link relation.
     */
    public function remove_link( $rel ) {
        unset( $this->links[ $rel ] );
    }

    /**
     * Retrieves all links.
     *
     * @return array Map of link relation to array of links.
     */
    public function get_links() {
        return $this->links;
    }
}

可以看到,WP_REST_Response 类主要负责存储和管理响应的数据、状态码、头部和链接。它并没有做太多的数据处理,而是将数据原封不动地保存下来。

五、数据序列化:将数据转换成 JSON

虽然 WP_REST_Response 类本身不负责数据序列化,但 WordPress REST API 会在发送响应之前,将数据转换成 JSON 格式。这个过程是由 rest_do_request() 函数触发的。

<?php

/**
 * Executes a REST request.
 *
 * Primarily used to execute a request from another endpoint, or internally.
 *
 * @since 4.4.0
 *
 * @param WP_REST_Request $request Request to execute.
 *
 * @return WP_REST_Response|WP_Error Response from the endpoint.
 */
function rest_do_request( $request ) {
    // ... (省略部分代码)

    /**
     * Filters the REST response before sending to the client.
     *
     * Allows modification of the response before returning.
     *
     * @since 4.4.0
     *
     * @param WP_REST_Response $response The REST response.
     * @param WP_REST_Request  $request  The REST request.
     */
    $response = apply_filters( 'rest_post_dispatch', $response, $request );

    // Convert the response to data for output.
    $data = rest_get_server()->transform_response_to_data( $response, $request );

    // ... (省略部分代码)
}

可以看到,rest_do_request() 函数调用了 rest_get_server()->transform_response_to_data() 方法,将 WP_REST_Response 对象转换成用于输出的数据。

WP_REST_Server 类的 transform_response_to_data() 方法会调用 wp_json_encode() 函数,将数据转换成 JSON 格式。

<?php

/**
 * Transforms a REST response to data suitable for outputting.
 *
 * @param WP_REST_Response $response The response to transform.
 * @param WP_REST_Request  $request  The request associated with the response.
 * @return array|string The data to output.
 */
public function transform_response_to_data( $response, $request ) {
    $data = $response->get_data();

    if ( is_object( $data ) && method_exists( $data, 'jsonSerialize' ) ) {
        $data = $data->jsonSerialize();
    }

    // Ensure we're not encoding objects, for compatibility with pre-WP 5.3.
    if ( is_object( $data ) ) {
        $data = (array) $data;
    }

    /**
     * Filters the data returned from a REST API response.
     *
     * @since 4.4.0
     *
     * @param mixed            $data     The response data.
     * @param WP_REST_Response $response The REST response.
     * @param WP_REST_Request  $request  The REST request.
     */
    $data = apply_filters( 'rest_prepare_response', $data, $response, $request );

    if ( isset( $_GET['_jsonp'] ) ) {
        return $data;
    }

    return wp_json_encode( $data );
}

六、自定义响应格式:rest_prepare_response 过滤器

如果你想自定义 WordPress REST API 的响应格式,可以使用 rest_prepare_response 过滤器。这个过滤器允许你在数据被转换成 JSON 之前,对数据进行修改。

例如,你可以添加一些额外的元数据到响应中:

<?php
add_filter( 'rest_prepare_response', 'my_custom_rest_response', 10, 3 );

function my_custom_rest_response( $data, $response, $request ) {
    $data['custom_field'] = 'Hello, world!';
    return $data;
}

这段代码会在每个 API 响应中添加一个 custom_field 字段,其值为 "Hello, world!"。

七、错误处理:WP_Error 对象

WordPress REST API 使用 WP_Error 对象来表示错误。WP_Error 对象包含错误代码、错误消息和错误数据。

<?php
$error = new WP_Error( 'invalid_parameter', 'The parameter is invalid.', array( 'status' => 400 ) );
return $error;

当 API 发生错误时,你可以返回一个 WP_Error 对象。WordPress REST API 会自动将 WP_Error 对象转换成 JSON 格式的错误响应。

错误响应的格式如下:

{
  "code": "invalid_parameter",
  "message": "The parameter is invalid.",
  "data": {
    "status": 400
  }
}

八、HATEOAS:让 API 具有可发现性

HATEOAS (Hypermedia as the Engine of Application State) 是一种 API 设计风格,它允许客户端通过 API 响应中的链接,发现 API 的功能。

WordPress REST API 支持 HATEOAS。你可以使用 WP_REST_Response 类的 add_link() 方法,在响应中添加链接。

例如,你可以添加一个链接到文章的评论列表:

<?php
$response = get_rest_response( $data );
$response->add_link( 'replies', rest_url( 'wp/v2/comments?post=' . $post_id ) );
return $response;

这段代码会在响应中添加一个 replies 链接,指向文章的评论列表 API 接口。

九、一个完整的例子

让我们来看一个完整的例子,展示如何使用 get_rest_response() 函数来构建一个简单的 API 接口。

<?php
/**
 * Registers a custom REST route.
 */
add_action( 'rest_api_init', function () {
    register_rest_route( 'my-plugin/v1', '/hello', array(
        'methods'  => 'GET',
        'callback' => 'my_plugin_hello_world',
    ) );
} );

/**
 * Handles the custom REST route.
 *
 * @param WP_REST_Request $request Full data about the request.
 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 */
function my_plugin_hello_world( $request ) {
    $name = $request->get_param( 'name' );

    if ( empty( $name ) ) {
        return new WP_Error( 'missing_name', 'You must specify a name.', array( 'status' => 400 ) );
    }

    $data = array(
        'message' => 'Hello, ' . $name . '!',
    );

    return get_rest_response( $data );
}

这段代码注册了一个名为 my-plugin/v1/hello 的 API 接口。当客户端发送 GET 请求到这个接口时,my_plugin_hello_world() 函数会被调用。

my_plugin_hello_world() 函数会获取 name 参数,并返回一个包含问候语的 JSON 响应。如果 name 参数为空,则返回一个 WP_Error 对象。

十、总结

get_rest_response() 函数是 WordPress REST API 中一个非常重要的函数。它负责将数据转换成 WP_REST_Response 对象,从而统一 API 的响应格式。

通过 rest_prepare_response 过滤器,你可以自定义 API 的响应格式,添加额外的元数据到响应中。

通过 WP_Error 对象,你可以处理 API 错误,并返回 JSON 格式的错误响应。

希望今天的讲解对大家有所帮助!祝大家编码愉快!

发表回复

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