В WordPress 5.6 появилась возможность выполнять серию REST API запросов в одном запросе к серверу. В самом простом виде это полезная оптимизация производительности, когда необходимо выполнить большое количество операций записи. Кроме того, опционально можно использовать базовые средства управления параллелизмом.
Оглавление
- Регистрация
- Выполнение запроса
- Формат запроса
- Обнаружение максимальных запросов
- Формат ответов
- Режимы проверки
- Callback для валидации
- Ограничения
Регистрация
Чтобы использовать пакетный запрос, маршруты должны поддерживать эту функцию. Она включается при регистрации. Например:
register_rest_route( 'my-ns/v1', 'my-route', [ 'methods' => WP_REST_Server::CREATABLE, 'callback' => 'my_callback', 'permission_callback' => 'my_permission_callback', 'allow_batch' => [ 'v1' => true ], ] );
Если маршрут REST API был реализован с использованием лучших практик, декларирование поддержки должно быть достаточным для того, чтобы маршрут можно было записывать через конечную точку пакетной обработки. В частности, вот на что следует обратить внимание:
Маршруты должны использовать объект WP_REST_Request для получения всех данных запроса. Другими словами, он не должен обращаться к переменным $_GET, $_POST или $_SERVER для получения параметров или заголовков.
Маршруты должны возвращать данные. Это может быть объект WP_REST_Response, объект WP_Error или любой вид сериализуемых данных JSON. Это означает, что маршрут не должен напрямую передавать вывод ответа или использовать
die()
. Например, с помощью wp_send_json() или wp_die().- Маршруты должны быть реентерабельными. Будьте готовы к тому, что один и тот же маршрут будет вызываться несколько раз в пакете.
Выполнение запроса
Чтобы отправить пакетный запрос, сделайте POST-запрос на /wp-json/batch/v1
с массивом нужных запросов. Например, простейший пакетный запрос выглядит следующим образом.
{ "requests": [ { "path": "/my-ns/v1/route" } ] }
Формат запроса
Каждый запрос - это объект, который может принимать следующие свойства.
{ "method": "PUT", "path": "/my-ns/v1/route/1?query=param", "headers": { "My-Header": "my-value", "Multi": [ "v1", "v2" ] }, "body": { "project": "Gutenberg" } }
method
- метод HTTP, который будет использоваться для запроса. Если метод опущен, используется метод POST.path
- маршрут REST API для вызова. Можно включить параметры запроса. Это свойство является обязательным.headers
- объект, содержащий имена заголовков и их значения. Если заголовок имеет несколько значений, его можно передать в виде массива.body
- это параметры, которые нужно передать маршруту. Заполняется в типе параметров POST.
Обнаружение максимальных запросов
По умолчанию REST API принимает до 25 запросов в одном пакете. Однако это значение является фильтруемым, поэтому его можно увеличивать или уменьшать в зависимости от ресурсов сервера.
function my_prefix_rest_get_max_batch_size() { return 50; } add_filter( 'rest_get_max_batch_size', 'my_prefix_rest_get_max_batch_size' );
Поэтому клиентам настоятельно рекомендуется сделать предварительный запрос, чтобы узнать лимит. Например, запрос OPTIONS
к batch/v1
вернет следующий ответ.
{ "namespace": "", "methods": ["POST"], "endpoints": [ { "methods": ["POST" ], "args": { "validation": { "type": "string", "enum": ["require-all-validate", "normal" ], "default": "normal", "required": false }, "requests": { "type": "array", "maxItems": 25, "items": { "type": "object", "properties": { "метод": { "type": "string", "enum": [ "POST", "PUT", "PATCH", "DELETE" ], "default": "POST" }, "path": { "type": "string", "required": true }, "body": { "type": "object", "properties": [], "additionalProperties": true }, "headers": { "type": "object", "properties": [], "additionalProperties": { "type": ["string", "array" ], "items": { "type": "string" } } } } }, "required": true } } } ], "_links": { "self": [ { "href": "http://trunk.test/wp-json/batch/v1" } ] } }
Ограничение задается в свойстве endpoints[0].args.requests.maxItems
.
Формат ответов
Конечная точка пакетной обработки возвращает код состояния 207 и ответы на каждый запрос в том же порядке, в котором они были запрошены. Например:
{ "responses": [ { "body": { "id": 1, "_links": { "self": [ { "href": "http://trunk.test/wp-json/my-ns/v1/route/1" } ] } }, "status": 201, "headers": { "Location": "http://trunk.test/wp-json/my-n1/v1/route/1", "Разрешить": "GET, POST" } } ] }
Внутри REST API оборачивает каждый ответ, прежде чем включить его в массив ответов.
Режимы проверки
По умолчанию каждый запрос обрабатывается изолированно. Это означает, что пакетный ответ может содержать несколько успешных и несколько неудачных запросов. Иногда хочется обработать пакет только в том случае, если все запросы валидны. Например, в Gutenberg мы не хотим сохранять некоторые пункты меню, в идеале должны быть сохранены все или ни одного.
Для этого REST API позволяет передавать режим проверки require-all-validate
. Когда этот параметр установлен, REST API сначала проверит, что каждый запрос является валидным в соответствии с WP_REST_Request::has_valid_params() и WP_REST_Request::sanitize_params(). Если какой-либо запрос не прошел проверку, то вся партия будет отклонена.
В данном примере выполняется пакет из двух запросов, и второй запрос не прошел валидацию. Поскольку порядок ответов не отличается от порядка запросов, для указания, что запрос не прошел проверку, используется null
.
{ "failed": "validation", "responses": [ null, { "body": { "code": "error_code", "message": "Неверные данные запроса", "data": { "status": 400 } }, "status": 400, "headers": {} } ] }
Примечание: Использование require-all-validate
не гарантирует, что все запросы будут успешными. Обратный вызов маршрута все равно может вернуть ошибку.
Callback для валидации
Методы WP_REST_Request используют обратный вызов validate_callback
и sanitize_callback
, указанные для каждого параметра при регистрации маршрута. В большинстве случаев это означает проверку на основе схемы.
Любая проверка, выполненная внутри маршрута, например, в методе prepare_item_for_database
, не приведет к отклонению пакета. Если это вызывает беспокойство, рекомендуется перенести как можно больше валидации в validate_callback
для каждого отдельного параметра. Это может быть сделано, например, поверх существующей проверки на основе схемы.
'post' => array( 'type' => 'integer', 'minimum' => 1, 'required' => true, 'arg_options' => array( 'validate_callback' => function ( $value, $request, $param ) { $valid = rest_validate_request_arg( $value, $request, $param ); if ( is_wp_error( $valid ) ) { return $valid; } if ( ! get_post( $value ) || ! current_user_can( 'read_post', $value ) ) { return new WP_Error( 'invalid_post', __( 'Этого поста не существует.' ) ); } return true; } ) )
Иногда при выполнении проверки требуется полный контекст запроса. Обычно такая проверка выполнялась в prepare_item_for_database
, но в WordPress 5.6 появилась альтернатива. При регистрации маршрута теперь можно указать верхний уровень validate_callback
. Он будет получать полный объект WP_REST_Request и может возвращать экземпляр WP_Error или false
. Обратный вызов не будет выполнен, если проверка на уровне параметров не прошла успешно.
register_rest_route( 'my-ns/v1', 'route', array( 'callback' => '__return_empty_array', 'permission_callback' => '__return_true', 'validate_callback' => function( $request ) { if ( $request['pass1'] !== $request['pass2'] ) { return new WP_Error( 'passwords_must_match', __( 'Пароли должны совпадать.' ), array( 'status' => 400 ) ); } return true; } ) );
Ограничения
В настоящее время ни один из встроенных маршрутов не поддерживает пакетную обработку. Это будет добавлено в будущем релизе, скорее всего, начиная сразу с WordPress 5.7.
GET-запросы не поддерживаются. Разработчикам рекомендуется использовать ссылки и встраивание или параллельные запросы.
--
See: https://make.wordpress.org/core/2020/11/20/rest-api-batch-framework-in-wordpress-5-6/