Создание Схемы Маршрута
Схема в REST API — это полное описание маршрута, оно рассказывает нам о маршруте все. Подробнее о том, что такое Схема, читайте в другом разделе этого руководства, а здесь мы рассмотрим как создавать схему, когда создается произвольный маршрут.
JSON Схема
Формат JSON схемы это для REST — это отдельная разработка, у которой есть документация и сайт json-schema.org.
Если на запрос отдавать просто JSON ответ, то Клиенты (пользователей маршрутов) ничего не будут знать о том какие именно данные они получают (хорошо если они интуитивно понятны, но такое бывает не всегда). Используя схему мы упростим понимание данных для клиентов, а также улучшит нашу кодовую базу. Схема поможет лучше структурировать данные, чтобы приложения могли легче «рассуждать» о взаимодействиях с REST API. Также наличие схемы упрощает тестирование, дает возможность обнаружения (в некоторых моментах).
Можно создавать маршруты без описания Схемы, но в этом случае многое будет непонятно для Клиента, поэтому при создании маршрута, рекомендуется создавать (описывать) его схему!
Создание схемы может показаться глупым занятием и какой-то ненужной работой, но если вы создаете обнаруживаемые и легко расширяемые конечные точки, использовать схему просто необходимо!
Схема маршрута состоит из «Схемы ресурса» и «Схемы эндпоинтов» (см. подробнее). Ниже рассмотрим, как создавать каждую из этих схем.
Структура схемы
Базовая структура схемы содержит всего несколько элементов.
-
$schema — Ссылка на документацию по схеме. Например:
http://json-schema.org/draft-04/schema#. -
title — Название схемы. Обычно это заголовок для человека, но в WordPress это поле создано для прочтения программами. Примеры названия роутов для разных типов данных:
post,page,ярлык типа записи,tag,ярлык таксонмии,comment. -
type — Тип данных который будет получен Клиентом. Тут может быть указан любой из семи примитивных типов. В WordPress тут почти всегда указывается тип
object, даже для эндпоинтов коллекций которые возвращают массив объектов. - properties — Список свойств (параметров) которые содержит объект и аргументы каждого свойства. Каждое свойство само по себе также является схемой, только без верхнеуровнего аргумента
$schema. Для отличия можно сказать что это под-схема.
Пример создания такой схемы из ядра WP:
$schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => $this->post_type, 'type' => 'object', // Base properties for every Post. 'properties' => array( 'id' => array( 'description' => __( 'Unique identifier for the object.' ), 'type' => 'integer', 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, ), 'date' => array( 'description' => __( "The date the object was published, in the site's timezone." ), 'type' => array( 'string', 'null' ), 'format' => 'date-time', 'context' => array( 'view', 'edit', 'embed' ), ), 'guid' => array( 'description' => __( 'The globally unique identifier for the object.' ), 'type' => 'object', 'context' => array( 'view', 'edit' ), 'readonly' => true, 'properties' => array( 'raw' => array( 'description' => __( 'GUID for the object, as it exists in the database.' ), 'type' => 'string', 'context' => array( 'edit' ), 'readonly' => true, ), 'rendered' => array( 'description' => __( 'GUID for the object, transformed for display.' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), ), ), ... ), );
Схема ресурсов
Схема ресурса указывает, какие поля существуют у объекта (поста, рубрики и т.д.). Схему ресурса можно указать при регистрации маршрута.
Давайте посмотрим, как создать схему ресурса-комментариев:
// регистрируем маршрут (роут).
add_action( 'rest_api_init', 'kama_register_my_comment_route' );
function kama_register_my_comment_route() {
register_rest_route( 'my-namespace/v1', '/comments', array(
// регистрируем
array(
'methods' => 'GET',
'callback' => 'kama_get_comments',
),
// регистрируем схему ("схема" приравнивается к запросу OPTIONS)
'schema' => 'kama_get_comment_schema',
) );
}
# Получает 5 последних комментов и отдает их как REST ответ.
function kama_get_comments( $request ) {
$data = array();
$comments = get_comments( array(
'post_per_page' => 5,
) );
// нет комментов выходим
if ( empty( $comments ) )
return rest_ensure_response( $data );
foreach ( $comments as $comment ) {
// добавляем только те поля которые указаны в схеме
$schema = kama_get_comment_schema();
$comment_data = array();
// переименовываем поля в более понятные
if ( isset( $schema['properties']['id'] ) )
$comment_data['id'] = (int) $comment->comment_id;
if ( isset( $schema['properties']['author'] ) )
$comment_data['author'] = (int) $comment->user_id;
if ( isset( $schema['properties']['content'] ) )
$comment_data['content'] = apply_filters( 'comment_text', $comment->comment_content, $comment );
$response = rest_ensure_response( $comment_data );
$data[] = _kama_prepare_for_collection( $response );
}
// вернем все данные комментариев.
return rest_ensure_response( $data );
}
# Подготавливает ответ для вставки в коллекцию ответов.
# Код скопирован из класса WP_REST_Controller.
function _kama_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;
}
# Полчает нашу схему для комментариев.
function kama_get_comment_schema() {
$schema = array(
// показывает какую версию схемы мы используем - это draft 4
'$schema' => 'http://json-schema.org/draft-04/schema#',
// определяет ресурс который описывает схема
'title' => 'comment',
'type' => 'object',
// в JSON схеме нужно указывать свойства в атрибуете 'properties'.
'properties' => array(
'id' => array(
'description' => esc_html__( 'Unique identifier for the object.', 'my-textdomain' ),
'type' => 'integer',
'context' => array( 'view', 'edit', 'embed' ),
'readonly' => true,
),
'author' => array(
'description' => esc_html__( 'The id of the user object, if author was a user.', 'my-textdomain' ),
'type' => 'integer',
),
'content' => array(
'description' => esc_html__( 'The content for the object.', 'my-textdomain' ),
'type' => 'string',
),
),
);
return $schema;
}
В строках 31-44 можно видеть, что в ответ попадают только те данные комментария, которые указаны в созданной нами схеме.
После создания схемы таким образом, мы можем увидеть её сделав OPTIONS запрос на текущий маршрут.
Когда мы предоставили Схему ресурса, этот ресурс становится обнаруживаемым через запрос OPTIONS к текущему маршруту.
Создание Схемы ресурса - это только одна часть общей Схемы маршрута. Вторая - это создание схемы параметров конечных точек (см. ниже).
Примеры схем ресурса:
- WP_REST_Posts_Controller::get_item_schema()
- WP_REST_Terms_Controller::get_item_schema()
- WP_REST_Attachments_Controller::get_item_schema()
- WP_REST_Comments_Controller::get_item_schema()
- WP_REST_Post_Statuses_Controller::get_item_schema()
- WP_REST_Post_Types_Controller::get_item_schema()
- WP_REST_Revisions_Controller::get_item_schema()
- WP_REST_Settings_Controller::get_item_schema()
- WP_REST_Taxonomies_Controller::get_item_schema()
- WP_REST_Users_Controller::get_item_schema()
- WP_REST_Controller::get_item_schema()
Схема эндпоинтов и их параметров
Схема эндпоинтов описывает методы, по которым можно обратиться к эндпоинту и его параметры.
При регистрации маршрута всегда указываются его эндпоинты и параметры этих эндпоинтов (если параметры вообще нужны). Для каждого параметра можно указать его: описание (description), тип значения (type), является ли параметр обязательным (required). Все это попадет в Схему эндпоинта.
Рассмотрим пример, создания эндпоинта с параметром my_arg, для которого укажем поля показываемые в схеме и функции проверки/очистки значения:
// регистрация роута.
add_action( 'rest_api_init', 'kama_register_my_route' );
function kama_register_my_route() {
register_rest_route( 'my-namespace/v1', '/schema-arg', array(
// регистрация эндпоинта
array(
'methods' => 'GET',
'callback' => 'kama_get_item',
// схема аргументов (параметров) эндпоинта. Эти параметры появятся в схеме маршрута.
'args' => array(
'arg_str' => array(
'description' => esc_html__('This is the argument our endpoint returns.','dom'),
'type' => 'string',
'validate_callback' => 'kama_validate_params',
'sanitize_callback' => 'kama_sanitize_params',
'required' => true,
),
'arg_int' => array(
'description' => esc_html__('This is the argument our endpoint returns.','dom'),
'type' => 'integer',
'default' => 10,
'validate_callback' => 'kama_validate_params',
'sanitize_callback' => 'kama_sanitize_params',
),
// и т.д.
),
),
) );
}
## Вернет параметры как ответ на запрос.
function kama_get_item( $request ) {
// код вызовет ошибку "arg_str не установлен", если параметр не передан в запросе.
// это потому что мы использовали required в схеме.
return rest_ensure_response( $request['arg_str'] );
}
/**
* Функция проверки значения параметра.
*
* @param mixed $value Значение параметра.
* @param WP_REST_Request $request Объект текущего запроса.
* @param string $param Название параметра.
*/
function kama_validate_params( $value, $request, $param ) {
$attributes = $request->get_attributes();
$param_attr = & $attributes['args'][ $param ];
// передан параметр из схемы
if ( isset( $attributes['args'][ $param ] ) ) {
// убедимся что значение параметра является нужным типом (строкой, чилом)
if (
( 'string' === $param_attr['type'] && ! is_string( $value ) )
||
( 'integer' === $param_attr['type'] && ! is_numeric( $value ) )
) {
return new WP_Error( 'rest_invalid_param',
sprintf( esc_html__('%s is not of type %s','dom'), $param, $param_attr ),
array( 'status' => 400 )
);
}
}
// передан неизвестный параметр
else {
return new WP_Error( 'rest_invalid_param',
sprintf( esc_html__('%s was not registered as a request argument.','dom'), $param ),
array( 'status' => 400 )
);
}
// ели мы дошли до сюда, значит данные прошили проверку
return true;
}
/**
* Функция очистки значения параметра.
*
* @param mixed $value Значение параметра.
* @param WP_REST_Request $request Объект текущего запроса.
* @param string $param Название параметра.
*/
function kama_sanitize_params( $value, $request, $param ) {
$attributes = $request->get_attributes();
// передан параметр из схемы
if ( isset( $attributes['args'][ $param ] ) ) {
// если значение параметра является строкой, очищаем как строку.
if ( 'string' === $attributes['args'][ $param ]['type'] )
return sanitize_text_field( $value );
// если значение параметра является числом, очищаем как число.
if ( 'integer' === $attributes['args'][ $param ]['type'] )
return (int) $value;
}
// передан неизвестный параметр
else {
return new WP_Error( 'rest_invalid_param',
sprintf( esc_html__('%s was not registered as a request argument.','dom'), $param ),
array( 'status' => 400 )
);
}
// если мы дошил до сюда, то видимо в этом коде есть какая-то ошибка. До этого момента мы доходить никак не должны.
return new WP_Error( 'rest_api_sad',
esc_html__('Something went terribly wrong.','dom'),
array( 'status' => 500 )
);
}
В приведенном выше примере мы используем функции проверки и очистки только для одного параметра запроса my_arg. Однако, мы также можем использовать эти функции проверки и очистки для любого другого параметра, который должен быть строкой (для которой мы задали схему). По мере роста кода и конечных точек, схема поможет сохранить легкий и поддерживаемый код. Проверять и очищать значения параметров, можно и без схемы, однако в этом случае будет сложнее следить, какие функции очистки/проверки и где используются. Также, добавляя схему для параметров запроса, мы показываем Клиентам нашу схему параметров. Это поможет Клиентам не отправлять недопустимые параметры в запросах в API.