路由和端点

概述

REST API 为我们提供了一种将 URI 与 WordPress 安装中的各种资源相匹配的方法。默认情况下,如果您启用了永久链接,则 WordPress REST API“存在”于/wp-json/. 在我们的 WordPress 站点https://ourawesomesite.com, we can access the REST API's index by making a获取request tohttps://ourawesomesite.com/wp-json/\。该索引提供了有关哪些路由可用于特定 WordPress 安装的信息,以及支持哪些 HTTP 方法和注册了哪些端点。

路由与端点

端点是可通过 API 获得的功能。这可以是检索 API 索引、更新帖子或删除评论之类的事情。端点执行特定功能,获取一些参数并将数据返回给客户端。

路由是用于访问端点的“名称”,用在 URL 中。一个路由可以有多个与之关联的端点,使用哪个端点取决于 HTTP 动词。

例如,使用 URL `http://example.com/wp-json/wp/v2/posts/123`:

  • “路由”是wp/v2/posts/123——路由不包含,wp-json因为wp-json它是 API 本身的基本路径。
  • 这条路线有 3 个端点:
    • GET触发一个get_item方法,将发布数据返回给客户端。
    • PUT触发一个update_item方法,获取要更新的数据,并返回更新后的发布数据。
    • DELETE触发一个delete_item方法,将现在删除的发布数据返回给客户端。

警报:
在没有漂亮永久链接的网站上,路由会作为参数添加到 URL rest_route。对于上面的示例,完整的 URL 将是http://example.com/?rest_route=/wp/v2/posts/123

创建端点

如果我们想创建一个端点,当它收到 GET 请求时会返回短语“Hello World,这是 WordPress REST API”,我们首先需要为该端点注册路由。要注册路线,您应该使用该register_rest_route()功能。它需要在动作挂钩上调用rest_api_initregister_rest_route()处理到端点的路由的所有映射。让我们尝试创建一个“Hello World,这是 WordPress REST API”路由。

/**
 * This is our callback function that embeds our phrase in a WP_REST_Response
 */
function prefix_get_endpoint_phrase() {
    // 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( 'Hello World, this is the WordPress REST API' );
}

/**
 * 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( 'hello-world/v1', '/phrase', 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_endpoint_phrase',
    ) );
}

add_action( 'rest_api_init', 'prefix_register_example_routes' );

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

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

当我们访问“https://ourawesomesite.com/wp-json/hello-world/v1/phrase”时,我们现在可以看到我们的 REST API 亲切地问候我们。让我们更深入地了解一下路由。

航线

REST API 中的路由由 URI 表示。路由本身是附加在https://ourawesomesite.com/wp-json. The index route for the API is/ which is whyhttps://ourawesomesite.com/wp-json/ 末尾的内容,返回 API 的所有可用信息。所有路由都应该建立在这条路由之上,wp-json`部分可以改变,但一般情况下,建议保持不变。

我们要确保我们的路线是独一无二的。例如,我们可以有一条这样的书的路线:/books。我们的书籍路线现在将位于https://ourawesomesite.com/wp-json/books. However, this is not a good practice as we would end up polluting potential routes for the API. What if another plugin we wanted to register a books route as well? We would be in big trouble in that case, as the two routes would conflict with each other and only one could be used. The fourth parameter toregister\_rest\_route() \ 是一个布尔值,表示路线是否应覆盖现有路线。

override 参数也没有真正解决我们的问题,因为两个路由都可以覆盖,或者我们希望将两个路由用于不同的事情。这就是为我们的路由使用命名空间的地方。

命名空间

将名称空间添加到路由中非常重要。“核心”端点使用wp/v2命名空间。

警告: 不要将任何东西放入 wp命名空间,除非你正在创建端点以将它们合并到核心中。

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

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

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

资源路径

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

假设我们有一个插件可以处理一些基本的电子商务功能。我们将有两种主要的资源类型订单和产品。订单是对产品的请求,但它们不是产品本身。同样的概念也适用于产品。尽管这些资源是相关的,但它们不是一回事,每个资源都应该位于单独的资源路径中。对于我们的电子商务插件,我们的路线最终会看起来像这样:/my-shop/v1/orders/my-shop/v1/products

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

路径变量

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

/**
 * This is our callback function to return our products.
 *
 * @param WP_REST_Request $request This function accepts a rest request to process data.
 */
function prefix_get_products( $request ) {
    // In practice this function would fetch the desired data. Here we are just making stuff up.
    $products = array(
        '1' => 'I am product 1',
        '2' => 'I am product 2',
        '3' => 'I am product 3',
    );

    return rest_ensure_response( $products );
}

/**
 * This is our callback function to return a single product.
 *
 * @param WP_REST_Request $request This function accepts a rest request to process data.
 */
function prefix_get_product( $request ) {
    // In practice this function would fetch the desired data. Here we are just making stuff up.
    $products = array(
        '1' => 'I am product 1',
        '2' => 'I am product 2',
        '3' => 'I am product 3',
    );

    // Here we are grabbing the 'id' path variable from the $request object. WP_REST_Request implements ArrayAccess, which allows us to grab properties as though it is an array.
    $id = (string) $request['id'];

    if ( isset( $products[ $id ] ) ) {
        // Grab the product.
        $product = $products[ $id ];

        // Return the product as a response.
        return rest_ensure_response( $product );
    } else {
        // Return a WP_Error because the request product was not found. In this case we return a 404 because the main resource was not found.
        return new WP_Error( 'rest_product_invalid', esc_html__( 'The product does not exist.', 'my-text-domain' ), array( 'status' => 404 ) );
    }

    // If the code somehow executes to here something bad happened return a 500.
    return new WP_Error( 'rest_api_sad', esc_html__( 'Something went horribly wrong.', 'my-text-domain' ), array( 'status' => 500 ) );
}

/**
 * This function is where we register our routes for our example endpoint.
 */
function prefix_register_product_routes() {
    // Here we are registering our route for a collection of products.
    register_rest_route( 'my-shop/v1', '/products', 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_products',
    ) );
    // Here we are registering our route for single products. The (?P<id>[\d]+) is our path variable for the ID, which, in this example, can only be some form of positive number.
    register_rest_route( 'my-shop/v1', '/products/(?P<id>[\d]+)', 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_product',
    ) );
}

add_action( 'rest_api_init', 'prefix_register_product_routes' );

上面的例子涵盖了很多。需要注意的重要部分是,在我们注册的第二条路线中,我们将路径变量添加/(?P<id>[\d]+)到我们的资源路径中/products。路径变量是一个正则表达式。在这种情况下,它用来[\d]+表示至少应该是任何数字字符一次。如果您为资源使用数字 ID,那么这是如何使用路径变量的一个很好的示例。使用路径变量时,我们现在必须注意可以匹配的内容,因为它是用户输入。

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

虽然本节是关于路由的,但我们已经介绍了很多关于端点的内容。端点和路由是相互关联的,但它们肯定有区别。

端点

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

/**
 * This is our callback function to return our products.
 *
 * @param WP_REST_Request $request This function accepts a rest request to process data.
 */
function prefix_get_products( $request ) {
    // In practice this function would fetch the desired data. Here we are just making stuff up.
    $products = array(
        '1' => 'I am product 1',
        '2' => 'I am product 2',
        '3' => 'I am product 3',
    );

    return rest_ensure_response( $products );
}

/**
 * This is our callback function to return a single product.
 *
 * @param WP_REST_Request $request This function accepts a rest request to process data.
 */
function prefix_create_product( $request ) {
    // In practice this function would create a product. Here we are just making stuff up.
   return rest_ensure_response( 'Product has been created' );
}

/**
 * This function is where we register our routes for our example endpoint.
 */
function prefix_register_product_routes() {
    // Here we are registering our route for a collection of products and creation of products.
    register_rest_route( 'my-shop/v1', '/products', array(
        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_products',
        ),
        array(
            // By using this constant we ensure that when the WP_REST_Server changes, our create endpoints will work as intended.
            'methods'  => WP_REST_Server::CREATABLE,
            // Here we register our callback. The callback is fired when this endpoint is matched by the WP_REST_Server class.
            'callback' => 'prefix_create_product',
        ),
    ) );
}

add_action( 'rest_api_init', 'prefix_register_product_routes' );

根据我们用于路由的 HTTP 方法/wp-json/my-shop/v1/products,我们匹配到不同的端点并触发不同的回调。当我们使用时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这是如何工作的,您将向POSThttps://ourawesomesite.com/wp-json/my-shop/v1/products/1?_method=DELETE 发送请求。现在您将删除产品编号 1,即使您的客户端无法在请求中发送正确的 HTTP 方法,或者可能存在阻止 DELETE 请求的防火墙。

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

回调

目前 REST API 支持的端点回调只有两种类型;callbackpermission_callback。主回调应该处理与资源的交互。权限回调应该处理用户有权访问端点的内容。您可以在注册端点时通过添加附加信息来添加附加回调。然后,您可以挂接到rest_pre_dispatchrest_dispatch_requestrest_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.
        'permission_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为这个例子指定了一个参数。我们可以在请求端点时将参数指定为查询参数。如果我们GET向 https://ourawesomesitem.com/my-colors/v1/colors?filter=blue 发出请求,我们将只返回我们集合中的蓝色。您还可以将它们作为请求正文中的正文参数传递,而不是在查询字符串中传递。要了解查询参数和正文参数之间的区别,您应该阅读 HTTP 规范。查询参数存在于附加到 URL 的查询字符串中,正文参数直接嵌入到 HTTP 请求的正文中。

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

验证

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

在上面的示例中,我们需要验证参数filter是一个字符串,并且它与值 red、green 或 blue 匹配。让我们看看添加一个validate_callback.

/**
 * 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 more practical 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 );
}
/**
 * Validate a request argument based on details registered to the route.
 *
 * @param  mixed            $value   Value of the 'filter' argument.
 * @param  WP_REST_Request  $request The current request object.
 * @param  string           $param   Key of the parameter. In this case it is 'filter'.
 * @return WP_Error|boolean
 */
function prefix_filter_arg_validate_callback( $value, $request, $param ) {
    // If the 'filter' argument is not a string return an error.
    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 ) );
    }

    // Get the registered attributes for this endpoint request.
    $attributes = $request->get_attributes();

    // Grab the filter param schema.
    $args = $attributes['args'][ $param ];

    // If the filter param is not a value in our enum then we should return an error as well.
    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 ) );
    }
}

/**
 * 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' ),
        // Here we register the validation callback for the filter argument.
        'validate_callback' => 'prefix_filter_arg_validate_callback',
    );
    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' );

消毒

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

/**
 * This is our callback function that embeds our resource in a WP_REST_Response.
 *
 * The parameter is already sanitized by this point so we can use it without any worries.
 */
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 ) );
}

/**
 * Validate a request argument based on details registered to the route.
 *
 * @param  mixed            $value   Value of the 'filter' argument.
 * @param  WP_REST_Request  $request The current request object.
 * @param  string           $param   Key of the parameter. In this case it is 'filter'.
 * @return WP_Error|boolean
 */
function prefix_data_arg_validate_callback( $value, $request, $param ) {
    // If the 'data' argument is not a string return an error.
    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 ) );
    }
}

/**
 * Sanitize a request argument based on details registered to the route.
 *
 * @param  mixed            $value   Value of the 'filter' argument.
 * @param  WP_REST_Request  $request The current request object.
 * @param  string           $param   Key of the parameter. In this case it is 'filter'.
 * @return WP_Error|boolean
 */
function prefix_data_arg_sanitize_callback( $value, $request, $param ) {
    // It is as simple as returning the sanitized value.
    return sanitize_text_field( $value );
}

/**
 * We can use this function to contain our arguments for the example product endpoint.
 */
function prefix_get_data_arguments() {
    $args = array();
    // Here we are registering the schema for the filter argument.
    $args['data'] = array(
        // description should be a human readable description of the argument.
        'description' => esc_html__( 'The data parameter is used to be sanitized and returned in the response.', 'my-text-domain' ),
        // type specifies the type of data that the argument should be.
        'type'        => 'string',
        // Set the argument to be required for the endpoint.
        'required'    => true,
        // We are registering a basic validation callback for the data argument.
        'validate_callback' => 'prefix_data_arg_validate_callback',
        // Here we register the validation callback for the filter argument.
        'sanitize_callback' => 'prefix_data_arg_sanitize_callback',
    );
    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-plugin/v1', '/sanitized-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_item',
        // 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_data_arguments(),
    ) );
}

add_action( 'rest_api_init', 'prefix_register_example_routes' );

概括

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