WordPress REST API

WordPress REST API е тема, която нашумя с излизането на WordPress 4.7 и версия 2 на въпросния API.

Възможностите, които дава са насочени както към native приложения за мобилни телефони и таблети, така и да предоставят възможност за комуникация с други web услуги.

Вдъхновение

Наскоро четох статията на Mark Zuckerberg Building Jarvis и реших и аз да опитам да направя FB Messenger bot, който като за начало да може да търси уроци в https://magadanski.com/. Доста по-ограничено от Jarvis.

За целта изходих от примерен код за Facebook Messenger роботи, базирани на Node.js. Качих примера на Heroku и стигнах до момента, в който трябва да намеря начин как точно да търся в сайта. Благодарение на официалния release на WP REST API V2 като част от ядрото на WordPress 4.7 преди по-малко от месец, реших да се възползвам.

Нуждата от WP REST API

Чрез него можете да вземете последните blogpost-ове от даден WordPress сайт. Достатъчно е след home адреса да добавите: /wp-json/wp/v2/posts/. Тъй като това е нещо, което всеки може да направи, този API Endpoint е напълно публичен. Досущ като RSS feed.

Други endpoint-ове за сега няма да разглеждаме, а ще наблегнем малко повече само на този.

Всички аргументи, които е възможно да подадете на WP_Query можете да добавите като filter параметър. Прави се като: /wp-json/wp/v2/posts/?filter[posts_per_page]=1. Този пример ще върне само 1 резултат (преди да тествате дочетете статията). Ако искате да комбинирате с друг параметър (примерно за търсене) можете да използвате /wp-json/wp/v2/posts/?filter[posts_per_page]=1&filter[s]=keyword. Не забравяйте да urlencode-нете интервали и специални знаци.

Проблемът е, че ако отворите https://magadanski.com/wp-json/wp/v2/posts/?filter[s]=markdown ще видите същото, което виждате просто на https://magadanski.com/wp-json/wp/v2/posts/. Защо става това? Защо търсенето не променя нищо?

Публични и частни API endpoint-ове

Оказва се, че за да прилагате който и да било от тези филтри е нужно да се оторизирате. В противен случай можете да видите само последните няколко статии (според настройката на сайта). Неприятното е, че ми отне 2 часа човъркане, за да разбера, че това е така. Няма съобщение за грешка, а само резултат, който не очаквате.

За промяната на броя на показваните статии разбирам. Ако някой въведе просто -1, а вашият сайт е на някаква много голяма новинарска агенция с десетки хиляди статии, RAM-та на сървъра ще свърши и ще забие. Друг е въпросът, че подобни вътрешни защити може да се направят с най-проста проверка min(get_option('posts_per_page'), $api_requested_parameter), където $api_requested_parameter е стойността, която сте поискали. Така ще пратите не повече от зададените по настройките на сайта, но и не повече от поисканите.

Facebook Open Graph API прави това. Никога не връща повече от 20 резултата на страница, но ако поискате 10 – ще ви върне само толкова.

Все пак разбирам, че WP REST API е нещо ново и е възможно да мине малко време за изглаждане. Така че с търпение, а и contribution ;), вероятно ще стигнем и до там.

Това, което все пак ме разочарова е, че търсенето не е публично. В крайна сметка всеки може да отвори home адреса на някой WordPress сайт и да добави ?s=keyword. Основното търсене е напълно публично. Вероятно причината е, че всички WP_Query аргументи са в един кюп – на забранените.

Проблеми с WP REST API

Предупреждавам, че примерите по-долу ще са на базата на cURL, защото не искам да са строго специфични за даден език и платформа.

Според документацията на WP REST API можем да се оторизираме или чрез OAuth, Application Passwords или Basic Authentication.

Накратко – OAuth изисква повече настройки, а тъй като аз бях само в dev environment за своя бот, реших набързо да тествам с Application Passwords. Това и Basic Authentication са почти еднакви. Просто при Application Passwords си прави user за сайта със собствена парола, който е активен само за REST API. Иначе и в двата случая трябва да се добавят username / password като „Authorization“ header на заявката.

Така, че или:

curl --user "username:password" 'https://magadanski.com/wp-json/wp/v2/posts?filter\[s\]=markdown'

или (по-доброто – base64 encode-нато):

curl 'https://magadanski.com/wp-json/wp/v2/posts/?filter\[s\]=markdown'
    -H 'Authorization: Basic {base64(username:password)}'

Не, че има функция {base64()} – просто така съм означил, че трябва низа username:password да е кодиран така.

Може би ще забележите и „\“ преди квадратните скоби – това е за да сработи cURL-а.

Трябват ви plugin-и, за да се оторизирате

И за двата типа оторизация с header-и, обаче се изисква инсталиране на допълнителен plugin в WordPress. Не ми е проблем – направих го, обаче plugin-а за Application Passwords не сработи. Тъй като не бях сигурен каква е причината реших да опитам да инсталирам и двата, тъй като допуснах, че може да работят само в комбина. Това обаче не реши проблема и cURL заявките си останаха на публично ниво.

Тъй като не бях сигурен как точно да debug-на WP REST API оторизация, реших да опитам просто с OAuth – макар и да изискваше повече set-up изглеждаше като по-лесен вариант, от debug-ване.

Инсталирах трети plugin, който беше нужен за OAuth, но се оказа че поради някаква причина той не работи при мене. При създаване на нова двойка ключове просто получавах грешка с текст: „Сигурни ли сте, че искате да продължите?“ без линк или каквото и да било. Просто текстът на wp_die() беше въпрос, сякаш имах избор. Допускам, че това се дължи на грешка в превода, която ще проверя до няколко дена. Така че и третият и последен метод отпадна.

След още 2 часа търсене на информация стигнах до Issues списъка в GitHub за WP-API.

Защо GitHub? Както и да е – това няма особено голямо значение.

Много хора се оплакват от проблеми с оторизацията

След като прочетох проблемите изписани там обаче се оказа, че реално 8 от 17 отворени ticket-а се дължат на един и същи проблем. Същият проблем, който имах и аз. Описан е в issue #1 от април 2014 (преди 2 години и половина!). Освен това забелязах и че последната активност по проекта в GitHub е доста стара. Даже issue #38 просто пита гледа ли се изобщо още този GitHub. Може би най-добре ще е вече нещата да се report-нат просто на https://core.trac.wordpress.org/tickets/active. И това ще направя до няколко дена.

Custom Endpoint

Първо обаче искам да си подкарам бота и реших, че най-лесно може би ще е да си направя собствен API Endpoint, който да е публичен и да позволява търсене.

Документацията на WP REST API ми помогна повече по този въпрос с един добър старт. Ако не ви се чете всичко, ето няколко бързи насоки как можете да постигнете някакъв резултат (имайте предвид, че е ограничен):

init

Започваме с връзване към rest_api_init action.

add_action('rest_api_init', 'my_custom_function');

Собствен route

След това на този action добавяме нов rest route посредством:

function my_custom_function() {
    register_rest_route('namespace/version', 'endpoint/(?P<params>.*)', array('methods' => 'GET', 'callback' => 'my_custom_callback'));
}

namespace/version е задължително да присъства, за да не стават конфликти с други plugin-и или теми. Макар и да не е задължително форматът да е от две думи разделени с /, това е добра конвенция, която е наложена и от WP Core: /wp-json/wp/v2/. wp-json просто оказва, че става въпрос, за REST API; wp/v2 са namespace/version.

Вторият параметър е регулярен израз, който да определи какъв метод да се извика и с какви параметри. В горния пример методът се нарича endpoint и след „/“ оказваме, че всичко друго е просто параметър с име „params“.

След това имаме аргументи, че става въпрос за „GET“ заявка и че параметрите ще бъдат подадени на функцията my_custom_callback.

function my_custom_callback($data) {
    $result = my_custom_handle_params($data['params']);

    return $result;
}

Обработка на callback

Накрая имаме имплементация на callback функцията, която приема аргумент match-натото от регулярния израз. В нашия пример това е просто всичко след endpoint/, съхранено в $data['params']. Можем да обработим тези данни по какъвто си искаме начин и резултатът връщаме като PHP обект. WordPress вече се грижи резултатът да бъде json_encode-нат.

И за да тестваме всичко можем просто да отворим home адреса на сайта в браузъра и да добавим: /wp-json/namespace/version/endpoint/params. При мене това е просто https://magadanski.com/wp-json/mf/v1/search/markdown, което съвсем скоро ще ми позволи да завърша първа версия на бота си.

Вашият коментар

Вашият имейл адрес няма да бъде публикуван. Задължителните полета са отбелязани с *

Този сайт използва Akismet за намаляване на спама. Научете как се обработват данните ви за коментари.