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, което съвсем скоро ще ми позволи да завърша първа версия на бота си.