Zajęcia 8
13. Interfejs programowania aplikacji
13.1. Czym jest API?
API to interfejs programowania aplikacji (z ang. Application Programming Interface). Umożliwia on wzajemną komunikację programom, na przykład skryptom.
Przeglądarki internetowe, skrypty, witryny internetowe oraz inne aplikacje bardzo często udostępniają swoją funkcjonalność, aby programiści mogli z niej korzystać. Na przykład:
- Przeglądarki internetowe - model DOM to API. Pozwala skryptom na uzyskanie dostępu i uaktualnienie zawartości strony internetowej po jej wczytaniu w przeglądarce.
- Skrypty - np. biblioteka jQuery to plik JavaScript wraz z API. Pozwala na wybór elementów, a następnie użycie metod jQuery do pracy z wybranymi elementami.
Nie musimy wiedzieć jak inny skrypt lub program wykonuje dane zadanie. Powinniśmy wiedzieć, co robi, jak zlecić mu wykonanie zadania, a także jak przetworzyć dane otrzymane w odpowiedzi.
API pozwala na tworzenie kodu wykonującego żądanie, w którym inny program lub skrypt jest proszony o wykonanie pewnego zadania. API określa także format, w jakim oczekuje odpowiedzi (aby mogła być ona zrozumiana).
13.2. SOAP
SOAP to akronim od Simple Object Access Protocol. Jest on najbardziej zbliżony do tego, co nazywamy serwisami w zwykłej aplikacji – zakłada, że mamy pewien zbiór operacji, które przyjmują określone argumenty. SOAP z definicji jest bardzo formalny – tzn. każdy serwis powinien udostępniać plik WSDL, który opisuje jak się nazywa każda operacja, jakie dane przyjmuje, jakiego typu są to dane itp. Dzięki temu mamy dostęp do narzędzi takich jak generowanie kodu na podstawie pliku WSDL (bardzo ułatwia korzystanie z zewnętrznego API). Z drugiej strony nawet proste zapytania wymagają dużo “kodu” dookoła w przesyłanej wiadomości (tzw. Envelope), przez co ciężko testować takie serwisy ręcznie, analizować przesyłane zapytania czy korzystać z nich za pomocą bardzo prostych klientów (np. mikrokontrolery, języki programowania bez bibliotek itp.).
13.3. REST
REST to akronim od REpresentational State Transfer i jest zbudowany wokół całkowicie innych założeń. W przypadku REST mamy adresy URL które są pewnego rodzaju identyfikatorami. Wysyłamy na te adresy zapytanie, które może być zarówno JSONem, XMLem, ale też zwykłym tekstem czy danymi binarnymi. To, co powinno się wydarzyć jest określone przez użytą metodę protokołu HTTP (mamy ich 7) – np. GET pobiera element, DELETE usuwa go itd. Pola klas możemy wysyłać w komplecie, a część możemy pominąć (czasami). Odpowiedź może być w formacie JSON, XML, lub też zależnie od tego, jakiego typu odpowiedzi zażyczy sobie klient.
Słowem: mamy dość dużą dowolność w zakresie implementacji, ale jest ona obarczona dość dużą niepewnością. Tego typu API sprawdza się wyśmienicie w przypadku systemów, które powinny być dostępne dla możliwie dużej ilości klientów (także np. bezpośrednio przeglądarek internetowych i stron WWW), lub w których format zapytań nie jest tak ważny.
Przede wszystkim w REST-owym API ważny jest adres URL – wszystkie działania na konkretnym obiekcie (np. kocie) wykonujemy na jego unikalnym adresie URL, np /api/koty/1, gdzie 1 to unikalny identyfikator konkretnego kota (np. z bazy danych).
Wyróżniamy dwa rodzaje adresów: konkretnego elementu (np. /koty/1) oraz całej kolekcji (wszystkich elementów, np. /koty).
Konkretne metody protokołu HTTP odpowiadają za odpowiednie operacje:
- GET – pobieranie;
- POST – tworzenie (wysyłanie);
- PUT – aktualizacja;
- PATCH – częściowa aktualizacja;
- DELETE – usuwanie.
Więcej o REST-owym API znajdziesz na stronie Wprowadzenie do architektury REST.
13.4. SOAP vs REST
SOAP jest ustandaryzowany. To oznacza, że będziemy musieli się dopasować do tych standardów, ale w zamian dostajemy masę narzędzi, które automatycznie zrobią część pracy za nas. Jest jednak bardziej problematyczny w manualnym testowaniu i analizie zapytań/usług.
REST na pewno jest bardzo logiczny jesli chodzi o operacje na danych – dodawanie, odczyt, aktualizacja itp. W przypadku operacji czysto proceduralnych (np. “oblicz jakąś wartość na podstawie danych wejściowych”) jest on dużo mniej jasny, a brak określonych standardów i norm powoduje, że implementacje często nie są zgodne z intencją RESTa i korzystanie z nich może przyprawić o bół głowy. Jest jednak dużo mniej wrażliwy na zmiany w API (np. dodanie lub usunięcie pól, nowe operacje itp.).
13.5. Biblioteka qwest
Przy pracy nad ostatnią fazą projektu będziemy korzystać z biblioteki qwest. Na początku dołączamy plik skryptu z biblioteką na koniec sekcji body (przed naszym skyptem JS):
Jak używa się biblioteki qwest?
qwest.method(url, data, options, before)
.then(function(xhr, response) {
// Run when the request is successful
})
.catch(function(e, xhr, response) {
// Process the error
})
.complete(function() {
// Always run
});
gdzie:
- method to wywoływana metoda REST lub inna metoda biblioteki;
- url to adres, z którym się łączymy;
- data to dane, jakie wysyłamy (np. w tablicy);
- options to opcje, z jakimi chcemy wywołać metodę (lista opcji znajduje się tutaj);
- before to własne opcje dla obiektu XHR (XMLHttpRequest).
Przykłady użycia biblioteki qwest dla API sealcode-owego:
var url='http://sealcode.org:8082/api/v1/resources/task';
Załóżmy, że chcemy przechowywać nasze dane lokalnie w tablicy o nazwie tasks.
Pobieranie danych
function getTasks() { // pobieramy listę zadań po wystąpieniu odpowiedniego zdarzenia
qwest.get(url, {}, {cache: true}).then(
function(xhr, response) {
response.forEach(function(element) { // wywołujemy dla każdego pobranego zasobu
tasks.push(element); // dodajemy pobrany element zasobu do tablicy "tasks"
/* Teraz treść danego zadania i jego inne własciwości będą ukrywać się w tasks[index].body.nazwaWlasciwosci, np. tasks[0].body.title - nazwa pierwszego zadania w tablicy! */
refresh(); // odświeżamy stan strony
})});
}
Wysłanie nowego zasobu
function addTaskServer(task) { // wysyłamy nowe zadanie po wciśnięciu klawisza ENTER lub kliknięciu przycisku
qwest.post(url, {title: task.title, is_done: task.is_done}, {cache: true}); // wysłanie nowego zadania w postaci obiektu o właściwościach "title" i "is_done"
}
Częściowa aktualizacja zasobu
function checkboxClick(event) { // stan kliknięcia checkboxa przy danym zadaniu (załóżmy, że funkcja wywołuje się po wystąpieniu pewnego zdarzenia
tasks[this.id].body.is_done = this.checked; // zmiana stanu kliknięcia danego zadania w tablicy (zakładamy, że każde zadanie ma swój identyfikator, dla uproszczenia przyjąłem, że identyfikatorem jest pozycja w tablicy
qwest.map('PATCH', url+'/'+tasks[this.id].id, tasks[this.id].body, {cache: true}).then(function(xhr, response) { // szukamy odpowiedniego zasobu na serwerze i modyfikujemy jego ciało
refresh(); // odświeżamy stan strony
});
}
Usuwanie zasobu
function deleteTask() { // usuwanie wybranego zadania pod wpływem wystąpienia pewnego zdarzenia
qwest.delete(url+'/'+tasks[this.id].id, null, {cache: true}).then(function(xhr, response) { // usuwamy zadanie o danym identyfikatorze (tym razem nie musimy przesyłać ciała takiego zadania)
refresh(); // odświeżamy stan strony
});
}
Zadania domowe
Zadanie 1.
Wykonaj piątą (ostatnią) fazę tworzenia projektu - łączenie się z API. Każda większa zmiana powinna być wysłana jako osobny commit na repozytorium utworzone w pierwszej fazie.
Źródła
- Duckett Jon, JavaScript and JQuery: Interactive Front-End Web Development, przeł. Robert Górczyński, Helion, 2015, ISBN 978-83-283-0126-9.
- kobietydokodu.pl
- github.com/pyrsmk/qwest
- mickl.net