概述

REST API 提供了一种将 URI 匹配到我们 WordPress 安装中各种资源的方法。默认情况下,如果您启用了美观的永久链接,WordPress REST API“居住”在 /wp-json/。在我们的 WordPress 站点 https://ourawesomesite.com,我们可以通过向 https://ourawesomesite.com/wp-json/ 发出 GET 请求来访问 REST API 的索引。该索引提供了有关特定 WordPress 安装中可用路由的信息,以及支持的 HTTP 方法和注册的端点。

如果我们想创建一个返回短语“Hello World, this is the WordPress REST API”的端点,我们首先需要注册该端点的路由。要注册路由,您应该使用 register_rest_route() 函数。它需要在 rest_api_init 操作钩子上调用。register_rest_route() 处理所有从路由到端点的映射。让我们尝试创建一个“Hello World, this is the WordPress REST API”路由。

/**
 * 这是我们的回调函数,它将短语嵌入到 WP_REST_Response 中
 */
function prefix_get_endpoint_phrase() {
    // rest_ensure_response() 将我们要返回的数据包装在 WP_REST_Response 中,并确保它将被正确返回。
    return rest_ensure_response( 'Hello World, this is the WordPress REST API' );
}

/**
 * 此函数是我们注册示例端点路由的地方。
 */
function prefix_register_example_routes() {
    // register_rest_route() 处理更多参数,但我们现在将坚持基础内容。
    register_rest_route( 'hello-world/v1', '/phrase', array(
        // 通过使用此常量,我们确保当 WP_REST_Server 更改时,我们的可读端点将按预期工作。
        'methods'  => WP_REST_Server::READABLE,
        // 在这里我们注册我们的回调。当此端点被 WP_REST_Server 类匹配时,会触发回调。
        'callback' => 'prefix_get_endpoint_phrase',
    ) );
}

add_action( 'rest_api_init', 'prefix_register_example_routes' );

传递给 register_rest_route() 的第一个参数是命名空间,它为我们提供了一种分组路由的方法。传递的第二个参数是资源路径或资源基础。对于我们的示例,我们检索的资源是“Hello World, this is the WordPress REST API”短语。第三个参数是选项数组。我们指定端点可以使用的方法以及当端点匹配时应发生的回调(可以做更多事情,但这些是基础)。

第三个参数还允许我们提供权限回调,这可以限制端点的访问权限仅针对某些用户。第三个参数还提供了一种为端点注册参数的方法,以便请求可以修改我们端点的响应。我们将在本指南的端点部分深入了解这些概念。

当我们前往 https://ourawesomesite.com/wp-json/hello-world/v1/phrase 时,我们可以看到 REST API 亲切地问候我们。让我们更深入地查看路由。

路由

REST API 中的路由由 URI 表示。路由本身是附加到 https://ourawesomesite.com/wp-json 末尾的内容。API 的索引路由是 '/',这就是为什么 https://ourawesomesite.com/wp-json/ 返回 API 的所有可用信息的原因。所有路由都应构建到此路由上,wp-json 部分可以更改,但通常建议保持相同。

我们要确保我们的路由是唯一的。例如,我们可以为书籍创建一个路由,如:/books。我们的书籍路由现在将位于 https://ourawesomesite.com/wp-json/books。然而,这不是一个好的做法,因为我们最终会污染 API 的潜在路由。如果我们想注册的另一个插件也想注册一个书籍路由怎么办?在这种情况下我们会大麻烦,因为两个路由会相互冲突,只能使用其中一个。register_rest_field() 的第四个参数是一个布尔值,用于确定路由是否应覆盖现有路由。

覆盖参数也不能真正解决我们的问题,因为两个路由都可以覆盖,或者我们可能希望为不同的用途使用两个路由。这就是为什么我们要为我们的路由使用命名空间。

命名空间

为您的路由添加命名空间非常重要。“核心”端点(等待合并到 WordPress 核心)使用 /wp/v2 命名空间。

除非您打算将端点合并到核心,否则不要将任何内容放入 /wp 命名空间中。

在核心端点命名空间中有一些需要注意的关键事项。命名空间的第一个部分是 /wp,它代表供应商名称;WordPress。对于我们的插件,我们希望为命名空间的供应商部分想出独特的名称。在上例中,我们使用了 hello-world。

在供应商部分之后是命名空间的版本部分。“核心”端点使用 v2 来表示 WordPress REST API 的第二个版本。如果您正在编写插件,可以通过简单地创建新端点并提高您提供的版本号来维护 REST API 端点的向后兼容性。这样,原始 v1 和 v2 端点都可以访问。

命名空间之后的路由部分是资源路径。

资源路径

资源路径应表示端点关联的资源。在上例中,我们使用了单词 phrase 来表示我们要交互的资源是一个短语。为了避免任何冲突,我们在命名空间内注册的每个资源路径也应该是唯一的。资源路径应用于在给定命名空间内定义不同的资源路由。

假设我们有一个处理一些基本电子商务功能的插件。我们将有两个主要的资源类型:订单和产品。订单是对产品(s)的请求,但它们不是产品本身。同样的概念适用于产品。虽然这些资源是相关的,但它们不是同一件事,每个都应该生活在单独的资源路径中。我们的路由最终将类似于以下形式用于我们的电子商务插件:/my-shop/v1/orders 和 /my-shop/v1/products。

使用这样的路由,我们希望每个都返回订单或产品的集合。如果我们想通过 ID 获取特定产品,我们需要在路由中使用路径变量。

路径变量

路径变量使我们能够添加动态路由。为了扩展我们的电子商务路由,我们可以注册一个路由来获取单个产品。

/**
 * 这是我们的回调函数,用于返回我们的产品。
 *
 * @param WP_REST_Request $request 此函数接受一个 REST 请求以处理数据。
 */
function prefix_get_products( $request ) {
    // 在实际应用中,此函数会获取所需的数据。这里我们只是随意编造一些内容。
    $products = array(
        '1' => '我是产品 1',
        '2' => '我是产品 2',
        '3' => '我是产品 3',
    );

    return rest_ensure_response( $products );
}

/**
 * 这是我们的回调函数,用于返回单个产品。
 *
 * @param WP_REST_Request $request 此函数接受一个 REST 请求以处理数据。
 */
function prefix_get_product( $request ) {
    // 在实际应用中,此函数会获取所需的数据。这里我们只是随意编造一些内容。
    $products = array(
        '1' => '我是产品 1',
        '2' => '我是产品 2',
        '3' => '我是产品 3',
    );

    // 这里我们从 $request 对象中获取 'id' 路径变量。WP_REST_Request 实现了 ArrayAccess,允许我们像数组一样获取属性。
    $id = (string) $request['id'];

    if ( isset( $products[ $id ] ) ) {
        // 获取产品。
        $product = $products[ $id ];

        // 将产品作为响应返回。
        return rest_ensure_response( $product );
    } else {
        // 因为请求的产品未找到,所以返回一个 WP_Error。在这种情况下我们返回 404,因为主资源未找到。
        return new WP_Error( 'rest_product_invalid', esc_html__( '该产品不存在。', 'my-text-domain' ), array( 'status' => 404 ) );
    }

    // 如果代码以某种方式执行到这里说明发生了错误,返回一个 500。
    return new WP_Error( 'rest_api_sad', esc_html__( '出了一些严重的错误。', 'my-text-domain' ), array( 'status' => 500 ) );
}

/**
 * 此函数是我们注册示例端点路由的地方。
 */
function prefix_register_product_routes() {
    // 这里我们注册产品集合的路由。
    register_rest_route( 'my-shop/v1', '/products', array(
        // 通过使用此常量,我们确保当 WP_REST_Server 发生变化时,我们的可读端点将按预期工作。
        'methods'  => WP_REST_Server::READABLE,
        // 这里我们注册我们的回调。当此端点被 WP_REST_Server 类匹配时,回调将被触发。
        'callback' => 'prefix_get_products',
    ) );
    // 这里我们注册单个产品的路由。(?P[\d]+) 是我们的 ID 路径变量,在本例中,它只能是某种形式的正数。
    register_rest_route( 'my-shop/v1', '/products/(?P[\d]+)', array(
        // 通过使用此常量,我们确保当 WP_REST_Server 发生变化时,我们的可读端点将按预期工作。
        'methods'  => WP_REST_Server::READABLE,
        // 这里我们注册我们的回调。当此端点被 WP_REST_Server 类匹配时,回调将被触发。
        'callback' => 'prefix_get_product',
    ) );
}

add_action( 'rest_api_init', 'prefix_register_product_routes' );

上面的示例涵盖了很多内容。需要注意的是,在我们要注册的第二个路由中,我们在资源路径 /products 上添加了一个路径变量 /(?P[\d]+)。路径变量是一个正则表达式。在这种情况下,它使用 [\d]+ 来表示应该至少有一个数字字符。如果您为资源使用数字 ID,那么这是一个如何使用路径变量的绝佳示例。在使用路径变量时,我们需要小心可以匹配的内容,因为这是用户输入。

幸运的是,正则表达式会过滤掉任何非数字内容。但是,如果请求的 ID 对应的产品不存在怎么办?我们需要进行错误处理。您可以在上面的代码示例中看到我们处理错误的基本方法。当您在端点回调中返回 WP_Error 时,API 服务器将自动处理向客户端提供错误。

虽然本节是关于路由的,但我们已经涵盖了相当多的端点内容。端点和路由是相互关联的,但它们确实有区别。

端点

端点是路由需要映射到的目的地。对于任何给定的路由,您可以为其注册多个不同的端点。我们将扩展我们虚构的电子商务插件,以更好地展示路由和端点之间的区别。我们将创建两个端点,它们存在于 /wp-json/my-shop/v1/products/ 路由上。一个端点使用 HTTP 动词 GET 获取产品,另一个端点使用 HTTP 动词 POST 创建新产品。

/**
 * 这是我们的回调函数,用于返回我们的产品。
 *
 * @param WP_REST_Request $request 此函数接受一个 REST 请求以处理数据。
 */
function prefix_get_products( $request ) {
    // 在实际应用中,此函数会获取所需的数据。这里我们只是随意编造一些内容。
    $products = array(
        '1' => '我是产品 1',
        '2' => '我是产品 2',
        '3' => '我是产品 3',
    );

    return rest_ensure_response( $products );
}

/**
 * 这是我们的回调函数,用于返回单个产品。
 *
 * @param WP_REST_Request $request 此函数接受一个 REST 请求以处理数据。
 */
function prefix_create_product( $request ) {
    // 在实际应用中,此函数会创建产品。这里我们只是随意编造一些内容。
   return rest_ensure_response( '产品已创建' );
}

/**
 * 此函数是我们注册示例端点路由的地方。
 */
function prefix_register_product_routes() {
    // 这里我们注册产品集合的路由以及产品的创建。
    register_rest_route( 'my-shop/v1', '/products', array(
        array(
            // 通过使用此常量,我们确保当 WP_REST_Server 发生变化时,我们的可读端点将按预期工作。
            'methods'  => WP_REST_Server::READABLE,
            // 这里我们注册我们的回调。当此端点被 WP_REST_Server 类匹配时,回调将被触发。
            'callback' => 'prefix_get_products',
        ),
        array(
            // 通过使用此常量,我们确保当 WP_REST_Server 发生变化时,我们的创建端点将按预期工作。
            'methods'  => WP_REST_Server::CREATABLE,
            // 这里我们注册我们的回调。当此端点被 WP_REST_Server 类匹配时,回调将被触发。
            'callback' => 'prefix_create_product',
        ),
    ) );
}

add_action( 'rest_api_init', 'prefix_register_product_routes' );

取决于我们为路由 /wp-json/my-shop/v1/products 使用的 HTTP 方法,我们将匹配到不同的端点并触发不同的回调。当我们使用 POST 时,我们触发 prefix_create_product() 回调,当我们使用 GET 时,我们触发 prefix_get_products() 回调。

有几种不同的 HTTP 方法,REST API 可以使用其中任何一种。

HTTP 方法

HTTP 方法有时被称为 HTTP 动词。它们只是通过 HTTP 进行通信的不同方式。WordPress REST API 使用的主要方法有:

  • GET 应使用于从 API 检索数据。
  • POST 应使用于创建新资源(即用户、帖子、分类法)。
  • PUT 应使用于更新资源。
  • DELETE 应使用于删除资源。
  • OPTIONS 应使用于提供有关我们资源的上下文信息。

重要的是要注意,这些方法并非由每个客户端支持,因为它们是在 HTTP 1.1 中引入的。幸运的是,API 为这些不幸的情况提供了变通方案。如果您想删除资源但无法发送 DELETE 请求,则可以在请求中使用 _method 参数或 X-HTTP-Method-Override 标头。其工作原理是,您将向 https://ourawesomesite.com/wp-json/my-shop/v1/products/1?_method=DELETE 发送一个 POST 请求。现在您已删除了产品编号 1,即使您的客户端无法在请求中发送正确的 HTTP 方法,或者也许有防火墙阻止了 DELETE 请求。

HTTP 方法与路由和回调相结合,构成了端点的核心。

回调

REST API 目前仅支持两种类型的端点回调:callback 和 permissions_callback。主要回调应处理与资源的交互。权限回调应处理用户可访问端点的权限。您可以通过在注册端点时添加附加信息来添加额外的回调。然后您可以挂钩到 rest_pre_dispatch、rest_dispatch_request 或 rest_post_dispatch 钩子以触发您的新自定义回调。

端点回调

删除端点的主回调函数应仅删除资源并在响应中返回其副本。创建端点的主回调函数应仅创建资源并返回与新建数据匹配的响应。更新回调函数应仅修改实际存在的资源。读取回调函数应仅检索已存在的数据。重要的是要考虑幂等性的概念。

在 REST API 的上下文中,幂等性意味着如果对端点发出相同的请求,服务器将以相同的方式处理该请求。想象一下,如果我们的读取端点不是幂等的。每当我们要向它发出请求时,服务器的状态就会被请求修改,即使我们只是想获取数据。这可能是灾难性的。任何时候有人从您的服务器获取数据,内部都会有所变化。重要的是要确保读取、更新和删除端点没有恶意的副作用,并且只做它们打算做的事情。

在 REST API 中,幂等性的概念与 HTTP 方法而不是端点回调相关联。任何使用 GET、HEAD、TRACE、OPTIONS、PUT 或 DELETE 的回调函数都不应产生任何副作用。POST 请求不是幂等的,通常用于创建资源。如果您创建了幂等的创建方法,那么您永远只会创建一个资源,因为当您发出相同的请求时,服务器将不再有副作用。对于创建,如果您反复发出相同的请求,服务器应该每次都生成新资源。

为了限制端点的使用,我们需要注册权限回调函数。

权限回调

权限回调函数对于 WordPress REST API 的安全性至关重要。如果您有任何不应公开显示的内部数据,那么您需要为您的端点注册权限回调函数。下面是如何注册权限回调函数的示例。

/**
 * This is our callback function that embeds our resource in a WP_REST_Response
 */
function prefix_get_private_data() {
    // rest_ensure_response() wraps the data we want to return into a WP_REST_Response, and ensures it will be properly returned.
    return rest_ensure_response( 'This is private data.' );
}

/**
 * This is our callback function that embeds our resource in a WP_REST_Response
 */
function prefix_get_private_data_permissions_check() {
    // Restrict endpoint to only users who have the edit_posts capability.
    if ( ! current_user_can( 'edit_posts' ) ) {
        return new WP_Error( 'rest_forbidden', esc_html__( 'OMG you can not view private data.', 'my-text-domain' ), array( 'status' => 401 ) );
    }

    // This is a black-listing approach. You could alternatively do this via white-listing, by returning false here and changing the permissions check.
    return true;
}

/**
 * This function is where we register our routes for our example endpoint.
 */
function prefix_register_example_routes() {
    // register_rest_route() handles more arguments but we are going to stick to the basics for now.
    register_rest_route( 'my-plugin/v1', '/private-data', array(
        // By using this constant we ensure that when the WP_REST_Server changes our readable endpoints will work as intended.
        'methods'  => WP_REST_Server::READABLE,
        // Here we register our callback. The callback is fired when this endpoint is matched by the WP_REST_Server class.
        'callback' => 'prefix_get_private_data',
        // Here we register our permissions callback. The callback is fired before the main callback to check if the current user can access the endpoint.
        'permissions_callback' => 'prefix_get_private_data_permissions_check',
    ) );
}

add_action( 'rest_api_init', 'prefix_register_example_routes' );

如果您在没有启用身份验证的情况下尝试此端点,您也会收到错误响应,从而防止您查看数据。身份验证是一个巨大的主题,本章的一部分将创建以向您展示如何创建自己的身份验证过程。

参数

在向端点发出请求时,您可能需要指定额外的参数来更改响应。这些额外参数可以在注册端点时添加。让我们看看如何使用参数与端点的示例。

/**
 * This is our callback function that embeds our resource in a WP_REST_Response
 */
function prefix_get_colors( $request ) {
    // In practice this function would fetch the desired data. Here we are just making stuff up.
    $colors = array(
        'blue',
        'blue',
        'red',
        'red',
        'green',
        'green',
    );

    if ( isset( $request['filter'] ) ) {
       $filtered_colors = array();
       foreach ( $colors as $color ) {
           if ( $request['filter'] === $color ) {
               $filtered_colors[] = $color;
           }
       }
       return rest_ensure_response( $filtered_colors );
    }
    return rest_ensure_response( $colors );
}

/**
 * We can use this function to contain our arguments for the example product endpoint.
 */
function prefix_get_color_arguments() {
    $args = array();
    // Here we are registering the schema for the filter argument.
    $args['filter'] = array(
        // description should be a human readable description of the argument.
        'description' => esc_html__( 'The filter parameter is used to filter the collection of colors', 'my-text-domain' ),
        // type specifies the type of data that the argument should be.
        'type'        => 'string',
        // enum specified what values filter can take on.
        'enum'        => array( 'red', 'green', 'blue' ),
    );
    return $args;
}

/**
 * This function is where we register our routes for our example endpoint.
 */
function prefix_register_example_routes() {
    // register_rest_route() handles more arguments but we are going to stick to the basics for now.
    register_rest_route( 'my-colors/v1', '/colors', array(
        // By using this constant we ensure that when the WP_REST_Server changes our readable endpoints will work as intended.
        'methods'  => WP_REST_Server::READABLE,
        // Here we register our callback. The callback is fired when this endpoint is matched by the WP_REST_Server class.
        'callback' => 'prefix_get_colors',
        // Here we register our permissions callback. The callback is fired before the main callback to check if the current user can access the endpoint.
        'args' => prefix_get_color_arguments(),
    ) );
}

add_action( 'rest_api_init', 'prefix_register_example_routes' );

我们现在为此示例指定了一个 filter 参数。我们可以在请求端点时将参数指定为查询参数。如果我们向 https://ourawesomesitem.com/my-colors/v1/colors?filter=blue 发出 GET 请求,我们将只返回集合中的蓝色颜色。您也可以将这些参数作为请求体中的主体参数传递,而不是在查询字符串中。要了解查询参数和主体参数之间的区别,您应该阅读 HTTP 规范。查询参数位于附加到 URL 的查询字符串中,而主体参数直接嵌入 HTTP 请求的主体中。

我们为我们的端点创建了一个参数,但我们如何验证该参数是字符串并判断它是否匹配值 red、green 或 blue。为此,我们需要为参数指定验证回调函数。

验证

验证和清理对于 API 的安全性至关重要。验证回调(在 WP 4.6+ 中)在清理回调之前触发。您应该使用 validate_callback 来验证您接收到的输入是否有效。sanitize_callback 应在使用主回调处理参数之前用于转换参数输入或清除参数中的不需要的部分。

在上述示例中,我们需要验证 filter 参数是字符串,并且它匹配值 red、green 或 blue。让我们看看在添加 validate_callback 后代码看起来是什么样的。

/**
 * 这是我们的回调函数,它将资源嵌入到 WP_REST_Response 中
 */
function prefix_get_colors( $request ) {
    // 在实际应用中,此函数会获取更实用的数据。这里我们只是随意编造一些内容。
    $colors = array(
        'blue',
        'blue',
        'red',
        'red',
        'green',
        'green',
    );

    if ( isset( $request['filter'] ) ) {
       $filtered_colors = array();
       foreach ( $colors as $color ) {
           if ( $request['filter'] === $color ) {
               $filtered_colors[] = $color;
           }
       }
       return rest_ensure_response( $filtered_colors );
    }
    return rest_ensure_response( $colors );
}
/**
 * 根据注册到路由的详细信息验证请求参数。
 *
 * @param  mixed            $value   'filter' 参数的值。
 * @param  WP_REST_Request  $request 当前请求对象。
 * @param  string           $param   参数的键。在本例中为 'filter'。
 * @return WP_Error|boolean
 */
function prefix_filter_arg_validate_callback( $value, $request, $param ) {
    // 如果 'filter' 参数不是字符串,则返回错误。
    if ( ! is_string( $value ) ) {
        return new WP_Error( 'rest_invalid_param', esc_html__( 'The filter argument must be a string.', 'my-text-domain' ), array( 'status' => 400 ) );
    }

    // 获取此端点请求的注册属性。
    $attributes = $request->get_attributes();

    // 获取 filter 参数的架构。
    $args = $attributes['args'][ $param ];

    // 如果 filter 参数不是我们枚举中的值,我们也应该返回错误。
    if ( ! in_array( $value, $args['enum'], true ) ) {
        return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not one of %s' ), $param, implode( ', ', $args['enum'] ) ), array( 'status' => 400 ) );
    }
}

/**
 * 我们可以使用此函数来包含示例产品端点的参数。
 */
function prefix_get_color_arguments() {
    $args = array();
    // 这里我们注册 filter 参数的架构。
    $args['filter'] = array(
        // description 应该是该参数的易读描述。
        'description' => esc_html__( 'The filter parameter is used to filter the collection of colors', 'my-text-domain' ),
        // type 指定参数应包含的数据类型。
        'type'        => 'string',
        // enum 指定 filter 可以采用的值。
        'enum'        => array( 'red', 'green', 'blue' ),
        // 这里我们注册 filter 参数的验证回调函数。
        'validate_callback' => 'prefix_filter_arg_validate_callback',
    );
    return $args;
}

/**
 * 此函数是我们注册示例端点路由的地方。
 */
function prefix_register_example_routes() {
    // register_rest_route() 处理更多参数,但我们现在只关注基础部分。
    register_rest_route( 'my-colors/v1', '/colors', array(
        // 通过使用此常量,我们确保当 WP_REST_Server 发生变化时,我们的可读端点仍能按预期工作。
        'methods'  => WP_REST_Server::READABLE,
        // 这里我们注册我们的回调函数。当此端点被 WP_REST_Server 类匹配时,该回调函数会被触发。
        'callback' => 'prefix_get_colors',
        // 这里我们注册权限回调函数。该回调函数在主回调函数之前触发,以检查当前用户是否可以访问此端点。
        'args' => prefix_get_color_arguments(),
    ) );
}

add_action( 'rest_api_init', 'prefix_register_example_routes' );

过滤

在上述示例中,我们不需要使用 sanitize_callback,因为我们限制输入仅包含枚举中的值。如果我们没有严格的验证并接受任何字符串作为参数,我们肯定需要注册一个 sanitize_callback。如果我们想更新内容字段,而用户输入了类似 alert('ZOMG Hacking you'); 的内容呢?字段值可能潜在地成为可执行脚本。为了剥离不需要的数据或将数据转换为所需的格式,我们需要为参数注册一个 sanitize_callback。以下是如何使用 WordPress 的 sanitize_text_field() 作为清理回调函数的示例:

/**
 * 这是我们的回调函数,它将资源嵌入到 WP_REST_Response 中。
 *
 * 此时参数已经过清理,因此我们可以放心使用它。
 */
function prefix_get_item( $request ) {
    if ( isset( $request['data'] ) ) {
        return rest_ensure_response( $request['data'] );
    }

    return new WP_Error( 'rest_invalid', esc_html__( 'The data parameter is required.', 'my-text-domain' ), array( 'status' => 400 ) );
}

/**
 * 根据注册到路由的详细信息验证请求参数。
 *
 * @param  mixed            $value   'filter' 参数的值。
 * @param  WP_REST_Request  $request 当前请求对象。
 * @param  string           $param   参数的键。在本例中为 'filter'。
 * @return WP_Error|boolean
 */
function prefix_data_arg_validate_callback( $value, $request, $param ) {
    // 如果 'data' 参数不是字符串,则返回错误。
    if ( ! is_string( $value ) ) {
        return new WP_Error( 'rest_invalid_param', esc_html__( 'The filter argument must be a string.', 'my-text-domain' ), array( 'status' => 400 ) );
    }
}

/**
 * 根据注册到路由的详细信息清理请求参数。
 *
 * @param  mixed            $value   'filter' 参数的值。
 * @param  WP_REST_Request  $request 当前请求对象。
 * @param  string           $param   参数的键。在本例中为 'filter'。
 * @return WP_Error|boolean
 */
function prefix_data_arg_sanitize_callback( $value, $request, $param ) {
    // 这很简单,只需返回清理后的值即可。
    return sanitize_text_field( $value );
}

/**
 * 我们可以使用此函数来包含示例产品端点的参数。
 */
function prefix_get_data_arguments() {
    $args = array();
    // 这里我们注册 filter 参数的架构。
    $args['data'] = array(
        // description 应该是该参数的易读描述。
        'description' => esc_html__( 'The data parameter is used to be sanitized and returned in the response.', 'my-text-domain' ),
        // type 指定参数应包含的数据类型。
        'type'        => 'string',
        // 将参数设置为端点所需的参数。
        'required'    => true,
        // 我们为 data 参数注册基本的验证回调函数。
        'validate_callback' => 'prefix_data_arg_validate_callback',
        // 这里我们注册 filter 参数的清理回调函数。
        'sanitize_callback' => 'prefix_data_arg_sanitize_callback',
    );
    return $args;
}

/**
 * 此函数是我们注册示例端点路由的地方。
 */
function prefix_register_example_routes() {
    // register_rest_route() 处理更多参数,但我们现在只关注基础部分。
    register_rest_route( 'my-plugin/v1', '/sanitized-data', array(
        // 通过使用此常量,我们确保当 WP_REST_Server 发生变化时,我们的可读端点仍能按预期工作。
        'methods'  => WP_REST_Server::READABLE,
        // 这里我们注册我们的回调函数。当此端点被 WP_REST_Server 类匹配时,该回调函数会被触发。
        'callback' => 'prefix_get_item',
        // 这里我们注册权限回调函数。该回调函数在主回调函数之前触发,以检查当前用户是否可以访问此端点。
        'args' => prefix_get_data_arguments(),
    ) );
}

add_action( 'rest_api_init', 'prefix_register_example_routes' );

总结

我们已经涵盖了为 WordPress REST API 注册端点的基础知识。路由是我们端点所在的 URI。端点是回调函数、方法、参数和其他选项的集合。在使用 register_rest_route() 时,每个端点都映射到一个路由。默认情况下,一个端点可以支持各种 HTTP 方法、主回调函数、权限回调函数和注册的参数。我们可以注册端点以涵盖我们与 WordPress 交互的任何用例。端点是与 REST API 进行核心交互的接口,但还有许多其他主题需要探索和了解,以便充分利用这个强大的 API。