Статьи

Добавление пользовательских маршрутов в WordPress REST API

Эта статья о пользовательских маршрутах была первоначально опубликована журналом Torque Magazine и воспроизводится здесь с разрешения.

Большая часть обсуждения вокруг WordPress REST API была посвящена запросам маршрутов по умолчанию. В этом смысле мы рассматриваем его как монолитный API — например, API Twitter.

Правда в том, что WordPress REST API — это не один API, а миллионы настраиваемых API, которые также можно использовать в качестве инструмента для создания API. Да, он поставляется с маршрутами по умолчанию, но, по необходимости, эти маршруты являются компромиссом между десятками миллионов сайтов, включая многие, которые еще не были созданы.

Как и WordPress, это не просто глобальный объект WP_Query , а REST API — это не просто API по умолчанию. Придерживаться значений по умолчанию — это все равно, что иметь традиционный проект WordPress без создания собственного объекта WP_Query или перезаписывать запрос по умолчанию в pre_get_posts . Это возможно, но не каждая работа может быть выполнена только с помощью стандартных URL-адресов WordPress.

То же самое относится и к REST API.

В недавнем интервью с ведущим разработчиком REST API Райаном МакКью он рассказал о том, как вторая версия проекта разделена на две части — маршруты по умолчанию и инфраструктура для создания API-интерфейсов RESTful. Маршруты по умолчанию предоставляют отличные примеры того, как создавать свои собственные маршруты.

Система, используемая для добавления этих маршрутов и конечных точек, невероятно хорошо сделана. Я покажу вам основы его использования в этой статье; и, в качестве примера, я покажу, как создать собственный маршрут с двумя конечными точками, которые показывают информацию о продуктах на сайте электронной коммерции на основе Easy Digital Downloads (EDD). Этот пример основан на надстройке API, которую я создал для своего сайта. Если вы хотите увидеть полный исходный код на GitHub или API в действии , вы можете.

Хотя EDD предоставляет собственный RESTful API, я хотел показать конкретные пользовательские поля, которые я использую на своем собственном сайте. В мою собственную реализацию я также включил второй маршрут, называемый «документами», который обернут вокруг пользовательского типа записей, который я использую для документации.

Я мог бы справиться с API EDD или с пользовательским типом поста и мета-маршрутами основного API, чтобы делать то, что я хотел, но для простоты (и для того, чтобы иметь то, что было именно то, что мне нужно), я создал свои собственные маршруты и конечные точки. Это было быстро, весело и отлично сработало для тех двух мест, где я его реализовал.

Добавление маршрутов

Познакомьтесь с моей новой любимой функцией

Вторая версия REST API представляет новую функцию register_rest_route() . Это позволяет добавить маршрут к REST API и передать массив конечных точек. Для каждой конечной точки вы не просто предоставляете функцию обратного вызова для ответа на запрос, но вы также можете определить, какие поля вы хотите использовать в своем запросе, включая обратные вызовы по умолчанию, санитарию и проверку, а также отдельный обратный вызов разрешений.

Здесь есть дополнительные функции, которые все еще развиваются, я рекомендую прочитать класс для маршрутов сообщений по умолчанию. Это отличный ресурс о том, как использовать REST API для запроса сообщений.

Я собираюсь сосредоточиться на этих трех вещах: обратный вызов, аргументы поля и проверка разрешений. Эти три функции проиллюстрируют, как работает архитектура API. Они также очень полезны, потому что, как только вы вернетесь к обратному вызову, у вас будут все необходимые поля, и они будут очищены. Вы также будете знать, что запрос авторизован.

Эта архитектура обеспечивает разделение задач и помогает сохранить ваш код модульным. Я не могу преувеличить, как сильно я люблю это.

Настройка маршрута

При определении пользовательского маршрута используйте register_rest_route() в функции, подключенной к rest_api_init, которая является действием, которое запускается при инициализации REST API. Это важное действие, которое, вероятно, будет таким же ценным, как «plugin_loaded» и «init».

Эта функция принимает четыре аргумента:

Первое — это пространство имен для маршрута. Все маршруты должны быть пространством имен, которое затем используется как следующий сегмент URL после «wp-json». Маршруты по умолчанию имеют пространство имен с wp. Это означает, что основные маршруты имеют URL-адреса, такие как «wp-json / wp / posts», в то время как пользовательский маршрут «размеры» в пространстве имен «happy-hats-store» будет иметь URL-адрес «wp-json / happy-hats-store /». размеры «.

Эти пространства имен действуют как пространства имен PHP или уникальные слагы для функций плагина. Они избегают столкновений между маршрутами. Таким образом, если я напишу плагин, который добавляет маршрут, называемый меню, он может использоваться рядом с написанным вами плагином, который добавляет маршрут, называемый меню — при условии, что мы используем разные пространства имен, которые соответствуют имени плагина. Пространства имен для маршрутов — это интеллектуальная система, поскольку весьма вероятно, что два или более разработчиков добавят маршруты с одинаковыми именами.

Второй аргумент — это URL после пространства имен для вашего маршрута. В этом примере мой первый маршрут — «/ products», а второй — «/ products». ‘/(?П [\ d] +). »Второй маршрут позволяет использовать номер, например идентификатор сообщения в последнем сегменте URL-адреса. Эти URL-адреса маршрута присоединяются к пространству имен. Итак, если ваше пространство имен — «chewbacca-api», а ваш маршрут — «/ products», то URL для него будет «/ wp-json / chewbacca-api / products».

 register_rest_route( 'chewbacca-api', '/products', array() ); 

Хорошей практикой является включение номера версии в ваши пространства имен. Я использовал calderawp_api / v2 для своего пространства имен.

Третий аргумент — где происходит настоящее волшебство. Здесь вы добавляете конечные точки к маршруту. Вот о чем остальная часть этой статьи, поэтому мы пропустим ее на секунду.

Четвертый и последний аргумент — необязательный логический аргумент, называемый «переопределение». Он используется для устранения конфликтов, которые могут происходить преднамеренно или непреднамеренно с уже определенными маршрутами. По умолчанию этот аргумент имеет значение false, а когда он равен false, будет предпринята попытка объединить маршруты. При желании вы можете установить значение true, чтобы заменить уже объявленные маршруты.

Настройка ваших конечных точек

До сих пор мы говорили о настройке маршрутов, но маршруты полезны, только если у них есть конечные точки. В оставшейся части этой статьи мы поговорим о добавлении конечных точек к маршруту, используя третий аргумент register_rest_route() .

Способ транспортировки

Все конечные точки должны определять один или несколько методов транспорта HTTP (GET / POST / PUT / DELETE). Определяя конечную точку как работающую только через запросы GET, вы указываете REST API, где получить правильные данные и как создавать ошибки для недопустимых запросов.

В массиве, который определяет вашу конечную точку, вы определяете свои транспортные методы в ключе, называемом «методы». Класс WP_REST_Server предоставляет константы для определения транспортных методов и типов тел JSON для запроса. Например, вот как мы можем определить конечную точку, которая допускает только запросы GET:

 register_rest_route( 'chewbacca-api', '/products', array( 'methods' => WP_REST_Server::READABLE, ) ); 

А вот как мы могли бы добавить маршрут, который принимает все транспортные методы:

 register_rest_route( 'chewbacca-api', '/products', array( 'methods' => WP_REST_Server::ALLMETHODS, ) ); 

Использование этих констант, которые вы можете увидеть здесь , гарантирует, что по мере развития сервера REST ваши маршруты будут правильно настроены для него.

Определение ваших полей

Одна из действительно важных частей определения конечных точек заключается в том, что вы указываете нужные поля: каковы их значения по умолчанию и как их очистить. Это позволяет функции обратного вызова для обработки запроса фактически доверять полученным данным.

REST API обрабатывает все это для вас.

Вот пример того, как я настраивал основную конечную точку полей, которая возвращает коллекцию продуктов:

 register_rest_route( "{$root}/{$version}", '/products', array( array( 'methods' => \WP_REST_Server::READABLE, 'callback' => array( $cb_class, 'get_items' ), 'args' => array( 'per_page' => array( 'default' => 10, 'sanitize_callback' => 'absint', ), 'page' => array( 'default' => 1, 'sanitize_callback' => 'absint', ), 'soon' => array( 'default' => 0, 'sanitize_callback' => 'absint', ), 'slug' => array( 'default' => false, 'sanitize_callback' => 'sanitize_title', ) ), 'permission_callback' => array( $this, 'permissions_check' ) ), ) ); 

Вы заметите, что большинство из них являются absint() или логическими полями, поэтому я настроил их для absint() с помощью absint() . Есть одно поле для запросов по почтовому слагу. Я использовал sanitize_title для него, так как они также очищаются перед записью в базу данных.

Мой другой маршрут для показа товара по идентификатору. В конечной точке этого маршрута я не указал никаких полей, потому что достаточно идентификатора, переданного в последнем сегменте URL.

 register_rest_route( "{$root}/{$version}", '/products' . '/(?P<id>[\d]+)', array( array( 'methods' => \WP_REST_Server::READABLE, 'callback' => array( $cb_class, 'get_item' ), 'args' => array( ), 'permission_callback' => array( $this, 'permissions_check' ) ), ) ); 

Вы можете использовать эти примеры для создания своих собственных маршрутов. Просто помните, что мои примеры написаны в контексте объекта — то есть они будут использоваться внутри метода класса. Кроме того, этот метод должен быть подключен к rest_api_init.

Функция обратного вызова

Функция обратного вызова, которую вы указываете для каждого маршрута в ключе «обратный вызов», является методом, на который будет отправлен запрос, если обратный вызов разрешений пройдет.

В моем примере я передаю свой основной маршрут методу класса обратного вызова с именем «get_items» и отдельному маршруту продукта к методу, называемому «get_item». Это соответствует соглашениям, изложенным в базовом классе пост-запроса. Это важно, потому что мой класс обратного вызова фактически расширяет этот класс в базовом API «WP_REST_Post_Controller». Это позволяет мне использовать большую часть его функциональных возможностей при определении моих собственных маршрутов. Я буду обсуждать обработку запросов и ответы на них в этой функции позже. На данном этапе мы просто определяем, что это такое.

В последнем разделе я показал вам две регистрации маршрута. Оба имеют массив для «обратного вызова», который передает объект класса, используемого для обратного вызова, и имя функции.

Разрешение Обратный звонок

Как и основной обратный вызов, этот метод передал объект WP_Request_Class , который позволяет вам использовать части запроса для вашей схемы аутентификации. Обратный вызов разрешений просто должен возвращать true или false; как вы туда добираетесь, зависит от вас.

Одна стратегия состоит в том, чтобы использовать традиционную логику проверки текущих возможностей пользователя, которую вы используете при разработке WordPress. Это возможно, потому что проверка разрешений будет выполняться после установки текущего пользователя, поэтому, если вы используете какой-либо из методов аутентификации, они уже будут запущены.

Вам вовсе не обязательно полагаться на текущего пользователя или аутентификацию WordPress. Одна вещь, которую вы можете сделать, это добавить определенную авторизацию для ваших пользовательских маршрутов и проверить определенные части запроса на правильные ключи. Другой вариант: если на вашем сайте реализована социальная регистрация, вы можете проверить наличие ключей oAuth, аутентифицировать их в этой сети и, если они пройдут, войти в систему пользователя, связанного с этой учетной записью.

Я буду обсуждать эти стратегии больше в будущем.

В примере, приведенном в этой статье, я показываю, как создать общедоступный API только для чтения, чтобы мы могли либо создать одну функцию, которая всегда возвращает значение true, чтобы использовать ее в качестве обратного вызова прав доступа, либо использовать __return_true WordPress. Я выбрал первый вариант, поэтому в будущем у меня будет его, когда я начну добавлять аутентифицированные POST-запросы.

Обработка и ответ на запросы

Функция обратного вызова для каждой конечной точки будет передана объекту класса WP_REST_Request . Существует метод для получения всех данных из запроса, обработанных и проверенных, с заполненными значениями по умолчанию.

В большинстве случаев мы можем просто использовать метод get_params() . Это дает нам параметры из запроса, сопоставленного с любым предоставленным нами методом транспорта. Использование этого метода вместо доступа к глобальным переменным POST или GET важно по многим причинам.

Во-первых, возвращаемый массив проверяется и очищается для нас. Кроме того, он обрабатывает переключение между транспортными методами. Это означает, что если вы переключите определение конечных точек с использования GET на PUT (это изменение в одну строку), весь код в обратном вызове будет работать просто отлично.

Это также приводит к лучшей абстракции. В этой статье я показываю базовую версию моего надстройки для API, но если вы посмотрите на источник плагина, на котором он основан, вы увидите, что все запросы к конечным точкам продуктов и документов обрабатываются абстрактным классом, который обрабатывает создание аргументов WP_Query , просматривая результаты и возвращая их.

Независимо от того, как вы справляетесь с обработкой вашей конечной точки, вы захотите закончить экземпляром класса WP_REST_Response . Лучший способ сделать это — использовать функцию ensure_rest_response() , которая возвращает экземпляр этого класса, а также может хорошо обрабатывать ошибки.

Этот класс гарантирует, что ваш ответ правильно сформирован JSON и имеет минимально необходимые заголовки. Он также предоставляет методы для добавления дополнительных заголовков.

Здесь вы можете увидеть, как я использовал его для добавления заголовков, основываясь на том, как основные почтовые маршруты добавляют заголовки для общих результатов, страниц и предыдущих / следующих ссылок:

 /** * Create the response. * * @since 0.0.1 * * @access protected * * @param \WP_REST_Request $request Full details about the request * @param array $args WP_Query Args * @param array $data Raw response data * * @return \WP_Error|\WP_HTTP_ResponseInterface|\WP_REST_Response */ protected function create_response( $request, $args, $data ) { $response = rest_ensure_response( $data ); $count_query = new \WP_Query(); unset( $args['paged'] ); $query_result = $count_query->query( $args ); $total_posts = $count_query->found_posts; $response->header( 'X-WP-Total', (int) $total_posts ); $max_pages = ceil( $total_posts / $request['per_page'] ); $response->header( 'X-WP-TotalPages', (int) $max_pages ); if ( $request['page'] > 1 ) { $prev_page = $request['page'] - 1; if ( $prev_page > $max_pages ) { $prev_page = $max_pages; } $prev_link = add_query_arg( 'page', $prev_page, rest_url( $this->base ) ); $response->link_header( 'prev', $prev_link ); } if ( $max_pages > $request['page'] ) { $next_page = $request['page'] + 1; $next_link = add_query_arg( 'page', $next_page, rest_url( $this->base ) ); $response->link_header( 'next', $next_link ); } return $response; } 

Вы заметите, что я не обсуждал, как собрать ваши данные для ответа. Это зависит от вас, как вы это делаете.

Вы можете использовать WP_Query , wpdb , get_post_meta или использовать встроенные функции плагина. Это зависит от вас, это ваш API. Это уже навыки, которыми вы обладаете как разработчик WordPress.

Во многих случаях, если вы добавляете RESTful API к существующему плагину или сайту, у вас уже должны быть классы для получения нужных вам данных. Вы можете использовать REST API, чтобы получить параметры для этих классов из HTTP-запроса, а затем передать результаты в класс ответа REST API.

В своем API я использовал WP_Query для получения сообщений. Вот метод, который я использовал для циклического WP_Query объекта WP_Query и получения необходимых мне данных:

 /** * Query for products and create response * * @since 0.0.1 * * @access protected * * @param \WP_REST_Request $request Full details about the request * @param array $args WP_Query args. * @param bool $respond. Optional. Whether to create a response, the default, or just return the data. * * @return \WP_HTTP_Response */ protected function do_query( $request, $args, $respond = true) { $posts_query = new \WP_Query(); $query_result = $posts_query->query( $args ); $data = array(); if ( ! empty( $query_result ) ) { foreach ( $query_result as $post ) { $image = get_post_thumbnail_id( $post->ID ); if ( $image ) { $_image = wp_get_attachment_image_src( $image, 'large' ); if ( is_array( $_image ) ) { $image = $_image[0]; } } $data[ $post->ID ] = array( 'name' => $post->post_title, 'link' => get_the_permalink( $post->ID ), 'image_markup' => get_the_post_thumbnail( $post->ID, 'large' ), 'image_src' => $image, 'excerpt' => $post->post_excerpt, 'tagline' => get_post_meta( $post->ID, 'product_tagline', true ), 'prices' => edd_get_variable_prices( $post->ID ), 'slug' => $post->post_name, ); for ( $i = 1; $i <= 3; $i++ ) { foreach( array( 'title', 'text', 'image' ) as $field ) { if ( 'image' != $field ) { $field = "benefit_{$i}_{$field}"; $data[ $post->ID ][ $field ] = get_post_meta( $post->ID, $field, true ); }else{ $field = "benefit_{$i}_{$field}"; $_field = get_post_meta( $post->ID, $field, true ); $url = false; if ( is_array( $_field ) && isset( $_field[ 'ID' ] )) { $img = $_field[ 'ID' ]; $img = wp_get_attachment_image_src( $img, 'large' ); if ( is_array( $img ) ) { $url = $img[0]; } } $_field[ 'image_src' ] = $url; $data[ $post->ID ][ $field ] = $_field; } } } return $data; } } if ( $respond ) { return $this->create_response( $request, $args, $data ); } else { return $data; } } 

Как вы можете видеть, это смесь почтовых полей, некоторых мета-полей и функций, определенных EDD. Опять же, если вы хотите увидеть полный исходный код, включая то, как я добавил второй маршрут и больше конечных точек для каждого из маршрутов, перейдите на GitHub и посмотрите .

Наступают пользовательские API, как зима

Тот факт, что WordPress REST API добавляет полезный набор маршрутов по умолчанию на ваш сайт, удивителен. Это известно.

Еще более захватывающим и удивительным является то, что при создании маршрутов мы получаем действительно потрясающий сервер API RESTful, который предоставляет нам мощные инструменты для создания наших собственных пользовательских API.

Так что в следующий раз вы не можете сделать маршрут по умолчанию, который будет делать то, что вы хотите, просто сделайте свой собственный.