Создание Схемы Маршрута
Схема в 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.