概述
在许多方面,REST API非常简单。有输入,称为请求。输入由服务器解释,并创建输出。输出称为响应。在某些方面,您可以将WordPress REST API的请求视为应由API执行和解释的一组指示或说明。默认情况下,WordPress REST API旨在将HTTP请求用作其请求介质。HTTP是通过Internet进行数据通信的基础,这使WordPress REST API达到了很远的API。API中的请求利用了HTTP请求中存在的许多不同方面,例如URIS,HTTP方法,标题和参数。请求的数据结构可方便地处理WP_REST_Request
班级。
wp_rest_request
该类是WordPress 4.4中介绍的三个主要基础架构类之一。当向API的端点提出HTTP请求时,API将自动创建一个实例WP_REST_Request
类,匹配提供的数据。响应对象是自动生成的WP_REST_Server
’serve_request()
方法。一旦创建请求并检查了身份验证,请派遣请求,然后开始触发我们的端点回调。所有数据存储在WP_REST_Request
对象被传递到我们的注册端点的回调中。所以我们俩permissions_callback
和callback
被调用后被调用。这使我们能够访问回调中的各种请求属性,以便我们可以量身定制响应以匹配所需的输出。
请求属性
请求对象具有许多不同的属性,每个属性都可以通过各种方式使用。主要属性是请求方法,路由,标题,参数和属性。让我们将这些中的每一个都分解为他们在请求中的角色。如果您自己创建一个请求对象,则看起来像这样:
$request = new WP_REST_Request( 'GET', '/my-namespace/v1/examples' );
在上面的代码示例中,我们仅指定请求对象方法为GET
我们应该与路线匹配/my-namespace/v1/examples
在整个URL的背景下,它看起来像这样:https://ourawesomesite.com/wp-json/my-namepsace/v1/examples
。方法和路线参数WP_REST_Request
构造函数用于将请求映射到所需的端点。如果向未注册的端点提出请求,则在响应中返回有用的404错误消息。让我们更深入地看一下各种属性。
方法
默认情况下,请求对象的方法属性匹配HTTP请求方法。在大多数情况下,该方法将是一种GET
,POST
,PUT
,DELETE
,OPTIONS
,或者HEAD
。这些方法将用于匹配注册到路由的各种端点。当API找到该方法和路线的匹配时,它将为该端点发射回调。
以下约定是匹配HTTP方法的最佳实践:GET
对于仅阅读任务,POST
为了创造PUT
用于更新,以及DELETE
用于删除。该请求方法充当端点预期功能的指标。当你做一个GET
请求到路线,您应该期望返回仅阅读数据。
路线
默认情况下,请求的路由将匹配服务器环境变量以获取路径信息。$_SERVER['PATH_INFO']
。当您向WordPress REST API的路线提出HTTP请求时,生成的WP_REST_Request
将制作对象以匹配该路径,希望该路径将与有效的端点匹配。简而言之,请求的路线是您要在API中定位请求的位置。
如果我们注册了书籍端点,请使用GET
,它可能生活在https://ourawesomesite.com/wp-json/my-namespace/v1/books
。如果我们在浏览器中使用该URL,我们会看到JSON中代表的书籍收藏。WordPress将自动为我们生成请求对象,并处理所有匹配端点的路由。因此,由于我们真的不必担心自己理解如何在请求中传递我们想要的额外数据的路线是一件更重要的事情。
标题
HTTP请求标头只是有关我们的HTTP请求的额外数据。请求标题可以指定缓存策略,我们的请求内容是什么,请求来自何处以及许多其他内容。请求标题不一定直接与我们的端点交互,但是标题中的信息可以帮助WordPress知道该怎么做。要传递我们希望终点与之交互的数据,我们要使用参数。
参数
在向WordPress REST API提出请求时,传递的大多数附加数据将采用参数的形式。什么是参数?API上下文中有四种不同类型。有路由参数,查询参数,身体参数和文件参数。让我们更深入地看一下每个人。
URL参数
URL参数自动生成WP_REST_Request
从请求的路线中的路径变量。这意味着什么?让我们看一下这条路线,该路线可以抓住单个书籍:/my-namespace/v1/books/(?P\d+)
。看起来很奇怪(?P\d+)
是一个路径变量。路径变量的名称是’id
‘。
如果我们要提出一个请求GET https://ourawesomesite.com/wp-json/my-namespace/v1/books/5
,5
将成为我们的价值id
路径变量。这WP_REST_Request
对象将自动使用该路径变量并将其存储为URL参数。现在,在我们的端点回调中,我们可以很容易地与该URL参数进行交互。让我们看看一个例子。
// Register our individual books endpoint.
function prefix_register_book_route() {
register_rest_route( 'my-namespace/v1', '/books/(?P<id>\d+)', array(
// Supported methods for this endpoint. WP_REST_Server::READABLE translates to GET.
'methods' => WP_REST_Server::READABLE,
// Register the callback for the endpoint.
'callback' => 'prefix_get_book',
) );
}
add_action( 'rest_api_init', 'prefix_register_book_route' );
/**
* Our registered endpoint callback. Notice how we are passing in $request as an argument.
* By default, the WP_REST_Server will pass in the matched request object to our callback.
*
* @param WP_REST_Request $request The current matched request object.
*/
function prefix_get_book( $request ) {
// Here we are accessing the path variable 'id' from the $request.
$book = prefix_get_the_book( $request['id'] );
return rest_ensure_response( $book );
}
// A simple function that grabs a book title from our books by ID.
function prefix_get_the_book( $id ) {
$books = array(
'Design Patterns',
'Clean Code',
'Refactoring',
'Structure and Interpretation of Computer Programs',
);
$book = '';
if ( isset( $books[ $id ] ) ) {
// Grab the matching book.
$book = $books[ $id ];
} else {
// Error handling.
return new WP_Error( 'rest_not_found', esc_html__( 'The book does not exist', 'my-text-domain' ), array( 'status' => 404 ) );
}
return $book;
}
在上面的示例中,我们看到路径变量如何存储为请求对象中的URL参数。然后,我们可以在端点回调中访问这些参数。上面的示例是用于使用URL参数的非常常见的用例。在路由中添加太多路径变量可以减慢路由的匹配,并且它也可能使记录端点复杂化,建议谨慎使用URL参数。如果我们不应该直接在我们的URL路径中使用参数,那么我们需要另一种方法来传递额外信息以根据我们的要求。这是查询和身体参数进来的地方,它们通常会在您的API中进行大部分繁重。
查询参数
查询参数存在于URI的查询字符串部分中。URI的查询字符串部分https://ourawesomesite.com/wp-json/my-namespace/v1/books?per_page=2&genre=fiction
是?per_page=2&genre=fiction
。查询字符串由“?
‘字符,查询字符串中的不同值由’分开&
‘ 特点。我们在查询字符串中指定了两个参数;per_page
和fiction
。在我们的终点中,我们只想从小说类型中获取两本书。我们可以在这样的回调中访问这些值:$request['per_page']
,和$request['genre']
(假设$请求是我们正在使用的参数的名称)。如果您熟悉PHP,则可能在Web应用程序中使用了查询参数。
在PHP中,查询参数存储在超级全局中$_GET
。重要的是要注意,您绝对不应直接访问端点中的任何超级清单或服务器变量。最好与WP_REST_Request
班级。将变量传递到端点的另一种常见方法是使用身体参数。
身体参数
身体参数是存储在请求主体中的钥匙值对。如果您曾经发送过POST
通过a请求,通过卷曲或其他方法,您使用了身体参数。使用身体参数,您也可以将它们作为不同的内容类型传递。默认值
Content-Type
标题POST
请求是x-www-form-urlencoded
。使用时x-www-form-urlencoded
,这些参数像查询字符串一样发送;per_page=2&genre=fiction
。默认情况下,HTML表单将捆绑各种输入并发送POST
请求匹配x-www-form-urlencoded
图案。
重要的是要注意,尽管HTTP规范并未禁止使用发送身体参数GET
请求,鼓励您不使用身体参数GET
要求。身体参数可以并且应该用于POST
,PUT
,和DELETE
要求。
文件参数
文件参数WP_REST_Request
当请求使用特殊内容类型标头时,将存储对象;multipart/form-data
。然后可以使用请求对象访问文件数据$request->get_file_params()
。文件参数等效于PHP SuperGlobal:$_FILES
。请记住,不要直接访问超级全局WP_REST_Request
对象提供。
在端点回调中,我们可以使用wp_handle_upload()
然后将所需的文件添加到WordPress的媒体上传目录中。该文件参数仅对处理文件数据有用,您绝不应将它们用于任何其他目的。
属性
WP_REST_Request
还支持请求属性。请求的属性是注册到比赛路由的属性。如果我们做了GET
请求my-namespace/v1/books
,然后我们打电话$request->get_attributes()
在我们的端点回调中,我们将退还我们的所有注册选项my-namespace/v1/books
端点。如果我们做了POST
请求到同一路线,我们的端点回调也返回$request->get_attributes()
,我们将收到注册到POST
端点回调。
在属性中,我们将获得一个响应,其中包含支持的方法,选项,是否在索引中显示此端点,端点的注册参数列表以及我们的注册回调。看起来像这样:
{
"methods": {
"GET": true
},
"accept_json": false,
"accept_raw": false,
"show_in_index": true,
"args": {
"context": {
"description": "Scope under which the request is made; determines fields present in response.",
"type": "string",
"sanitize_callback": "sanitize_key",
"validate_callback": "rest_validate_request_arg",
"enum": [
"view",
"embed",
"edit"
],
"default": "view"
},
"page": {
"description": "Current page of the collection.",
"type": "integer",
"default": 1,
"sanitize_callback": "absint",
"validate_callback": "rest_validate_request_arg",
"minimum": 1
},
"per_page": {
"description": "Maximum number of items to be returned in result set.",
"type": "integer",
"default": 10,
"minimum": 1,
"maximum": 100,
"sanitize_callback": "absint",
"validate_callback": "rest_validate_request_arg"
},
"search": {
"description": "Limit results to those matching a string.",
"type": "string",
"sanitize_callback": "sanitize_text_field",
"validate_callback": "rest_validate_request_arg"
},
"after": {
"description": "Limit response to resources published after a given ISO8601 compliant date.",
"type": "string",
"format": "date-time",
"validate_callback": "rest_validate_request_arg"
},
"author": {
"description": "Limit result set to posts assigned to specific authors.",
"type": "array",
"default": [],
"sanitize_callback": "wp_parse_id_list",
"validate_callback": "rest_validate_request_arg"
},
"author_exclude": {
"description": "Ensure result set excludes posts assigned to specific authors.",
"type": "array",
"default": [],
"sanitize_callback": "wp_parse_id_list",
"validate_callback": "rest_validate_request_arg"
},
"before": {
"description": "Limit response to resources published before a given ISO8601 compliant date.",
"type": "string",
"format": "date-time",
"validate_callback": "rest_validate_request_arg"
},
"exclude": {
"description": "Ensure result set excludes specific ids.",
"type": "array",
"default": [],
"sanitize_callback": "wp_parse_id_list"
},
"include": {
"description": "Limit result set to specific ids.",
"type": "array",
"default": [],
"sanitize_callback": "wp_parse_id_list"
},
"offset": {
"description": "Offset the result set by a specific number of items.",
"type": "integer",
"sanitize_callback": "absint",
"validate_callback": "rest_validate_request_arg"
},
"order": {
"description": "Order sort attribute ascending or descending.",
"type": "string",
"default": "desc",
"enum": [
"asc",
"desc"
],
"validate_callback": "rest_validate_request_arg"
},
"orderby": {
"description": "Sort collection by object attribute.",
"type": "string",
"default": "date",
"enum": [
"date",
"relevance",
"id",
"include",
"title",
"slug"
],
"validate_callback": "rest_validate_request_arg"
},
"slug": {
"description": "Limit result set to posts with a specific slug.",
"type": "string",
"validate_callback": "rest_validate_request_arg"
},
"status": {
"default": "publish",
"description": "Limit result set to posts assigned a specific status; can be comma-delimited list of status types.",
"enum": [
"publish",
"future",
"draft",
"pending",
"private",
"trash",
"auto-draft",
"inherit",
"any"
],
"sanitize_callback": "sanitize_key",
"type": "string",
"validate_callback": [
{},
"validate_user_can_query_private_statuses"
]
},
"filter": {
"description": "Use WP Query arguments to modify the response; private query vars require appropriate authorization."
},
"categories": {
"description": "Limit result set to all items that have the specified term assigned in the categories taxonomy.",
"type": "array",
"sanitize_callback": "wp_parse_id_list",
"default": []
},
"tags": {
"description": "Limit result set to all items that have the specified term assigned in the tags taxonomy.",
"type": "array",
"sanitize_callback": "wp_parse_id_list",
"default": []
}
},
"callback": [
{},
"get_items"
],
"permission_callback": [
{},
"get_items_permissions_check"
]
}
如您所见,我们已经有所有已注册到端点的信息已经准备好了!该请求属性通常在较低级别使用,并由WP_REST_Server
但是,在端点回调中可以完成一些很酷的事情,例如限制接受的参数以匹配注册的参数。
WP REST API是为您设计的WP_REST_Request
通常不会被练习。使用WP REST API的核心链接到注册路线和端点。请求是我们用来告诉API的工具,我们要点击哪个端点。这最常见于HTTP,但是我们也可以使用WP_REST_Request
内部。
内部请求
提出内部请求的关键是使用rest_do_request()
。您需要做的就是通过请求对象传递,您将获得响应。因为该请求永远不会由WP_REST_Server
,响应数据永远不会编码为JSON,这意味着我们将响应对象作为PHP对象。这很棒,使我们能够做很多有趣的事情。首先,我们可以创建有效的批处理端点。从性能的角度来看,其中一个障碍是最大程度地减少HTTP请求。我们可以创建将使用的批处理端点rest_do_request()
在一个HTTP请求中,在内部提供所有要求。这是仅阅读数据的非常简单的批处理端点,因此您可以看到rest_do_request()
在行动中。
// Register our mock batch endpoint.
function prefix_register_batch_route() {
register_rest_route( 'my-namespace/v1', '/batch', array(
// Supported methods for this endpoint. WP_REST_Server::READABLE translates to GET.
'methods' => WP_REST_Server::READABLE,
// Register the callback for the endpoint.
'callback' => 'prefix_do_batch_request',
// Register args for the batch endpoint.
'args' => prefix_batch_request_parameters(),
) );
}
add_action( 'rest_api_init', 'prefix_register_batch_route' );
/**
* Our registered endpoint callback. Notice how we are passing in $request as an argument.
* By default, the WP_REST_Server will pass in the matched request object to our callback.
*
* @param WP_REST_Request $request The current matched request object.
*/
function prefix_do_batch_request( $request ) {
// Here we initialize the array that will hold our response data.
$data = array();
$data = prefix_handle_batch_requests( $request['requests'] );
return $data;
}
/**
* This handles the building of the response for the batch requests we make.
*
* @param array $requests An array of data to build WP_REST_Request objects from.
* @return WP_REST_Response A collection of response data for batch endpoints.
*/
function prefix_handle_batch_requests( $requests ) {
$data = array();
// Foreach request specified in the requests param run the endpoint.
foreach ( $requests as $request_params ) {
$response = prefix_handle_request( $request_params );
$key = $request_params['method'] . ' ' . $request_params['route'];
$data[ $key ] = prefix_prepare_for_collection( $response );
}
return rest_ensure_response( $data );
}
/**
* This handles the building of the response for the batch requests we make.
*
* @param array $request_params Data to build a WP_REST_Request object from.
* @return WP_REST_Response Response data for the request.
*/
function prefix_handle_request( $request_params ) {
$request = new WP_REST_Request( $request_params['method'], $request_params['route'] );
// Add specified request parameters into the request.
if ( isset( $request_params['params'] ) ) {
foreach ( $request_params['params'] as $param_name => $param_value ) {
$request->set_param( $param_name, $param_value );
}
}
$response = rest_do_request( $request );
return $response;
}
/**
* Prepare a response for inserting into a collection of responses.
*
* This is lifted from WP_REST_Controller class in the WP REST API v2 plugin.
*
* @param WP_REST_Response $response Response object.
* @return array Response data, ready for insertion into collection data.
*/
function prefix_prepare_for_collection( $response ) {
if ( ! ( $response instanceof WP_REST_Response ) ) {
return $response;
}
$data = (array) $response->get_data();
$server = rest_get_server();
if ( method_exists( $server, 'get_compact_response_links' ) ) {
$links = call_user_func( array( $server, 'get_compact_response_links' ), $response );
} else {
$links = call_user_func( array( $server, 'get_response_links' ), $response );
}
if ( ! empty( $links ) ) {
$data['_links'] = $links;
}
return $data;
}
/**
* Returns the JSON schema data for our registered parameters.
*
* @return array $params A PHP representation of JSON Schema data.
*/
function prefix_batch_request_parameters() {
$params = array();
$params['requests'] = array(
'description' => esc_html__( 'An array of request objects arguments that can be built into WP_REST_Request instances.', 'my-text-domain' ),
'type' => 'array',
'required' => true,
'validate_callback' => 'prefix_validate_requests',
'items' => array(
array(
'type' => 'object',
'properties' => array(
'method' => array(
'description' => esc_html__( 'HTTP Method of the desired request.', 'my-text-domain' ),
'type' => 'string',
'required' => true,
'enum' => array(
'GET',
'POST',
'PUT',
'DELETE',
'OPTIONS',
),
),
'route' => array(
'description' => esc_html__( 'Desired route for the request.', 'my-text-domain' ),
'required' => true,
'type' => 'string',
'format' => 'uri',
),
'params' => array(
'description' => esc_html__( 'Key value pairs of desired request parameters.', 'my-text-domain' ),
'type' => 'object',
),
),
),
),
);
return $params;
}
function prefix_validate_requests( $requests, $request, $param_key ) {
// If requests isn't an array of requests then we don't process the batch.
if ( ! is_array( $requests ) ) {
return new WP_Error( 'rest_invald_param', esc_html__( 'The requests parameter must be an array of requests.' ), array( 'status' => 400 ) );
}
foreach ( $requests as $request ) {
// If the method or route is not set then we do not run the requests.
if ( ! isset( $request['method'] ) || ! isset( $request['route'] ) ) {
return new WP_Error( 'rest_invald_param', esc_html__( 'You must specify the method and route for each request.' ), array( 'status' => 400 ) );
}
if ( isset( $request['params'] ) && ! is_array( $request['params'] ) ) {
return new WP_Error( 'rest_invald_param', esc_html__( 'You must specify the params for each request as an array of named key value pairs.' ), array( 'status' => 400 ) );
}
}
// This is a black listing approach to data validation.
return true;
}
这是相当不错的代码,涵盖了许多主题,但一切都围绕发生的事情prefix_handle_request()
。在这里,我们传递了一个数组,该数组告诉我们一个HTTP方法,路由和一组我们要转入请求的参数。然后,我们为方法和路由构建请求对象。如果指定了任何参数,我们使用WP_REST_Request::set_param()
添加所需参数的方法。曾经WP_REST_Request
准备去我们使用rest_do_request
要内部匹配该端点,并且响应将返回到我们的批处理端点响应集合。使用这样的批处理端点可以为您提供巨大的性能增长,因为您只会提出一个HTTP请求即可获得多个端点的响应。实施不一定是最好的,也是一个例子。不是唯一的方法。