REST i JavaScript – metody i nagłówki HTTP

Opisując metody tworzenia RESTful API nie możemy zapomnieć o klientach. Wszystkie ciekawe nagłówki i metody HTTP powinniśmy móc wywołać z poziomu przeglądarki. Dlatego dziś zajmiemy się korzystaniem z REST API w JavaScripcie.

Do testowania napiszmy sobie w PHP mały skrypt logujący nasze wywołania:

<?php
file_put_contents("log.txt",$_SERVER['REQUEST_METHOD']."\n",FILE_APPEND);

Od tego momentu możemy monitorować w konsoli zawartość pliku log.txt choćby za pomocą „tail -f„. Możemy przejść do Javascriptu.

Obiekt XMLHttpRequest

Do wywołań AJAX korzystamy z obiektu XMLHttpRequest. Przeważnie podajemy w nim adres i otrzymujey z powrotem dane. Ale to nie wszystko. W metodzie open() możemy skorzystać z możliwości podania metody HTTP. Możemy więc napisać przykładowy kod:

<script type="text/javascript">
  var xhr = new XMLHttpRequest();
  xhr.open("POST", "http://localhost/index.php");
  xhr.send();
</script>

Jeśli wywołamy go w przeglądarce nasz serwer powiadomi nas o wywołaniu wypisując nazwę metody:

POST

Serwer mówi, że nie obsługuje metody POST ale jednak ją dostał. W podobny sposób możemy korzystać z pozostałych metod HTTP. Wyjątkiem są metody CONNECT, TRACE i TRACK, które raportują w przeglądarce błąd (a więc żądania nie docierają do serwera):

SecurityError: The operation is insecure.
xhr.open("TRACE", "http://localhost:8000/");

Niektóre metody, te mniej znane mogą nie być rozumiane przez przeglądarkę. W takim przypadku przeglądarka, nie wiedząc czy może wywołać tą metodę wysyła wpierw zapytanie OPTIONS. Musimy zatem uzbroić nasz serwer w obsługę tej metody.

A czy możemy wysłać inne, własne nazwy metod? Możemy. Nic nie stoi na przeszkodzie aby wpisać w nazwie dowolny tekst:

xhr.open("FIRE", "http://localhost/index.php");

Serwer przyjmuje metodę FIRE raportując ją w pliku.

Wysyłamy nagłówki HTTP

Na początek rozszerzmy nasz kod PHP o raportowanie przesłanych nagłówków. Dodamy kod:

$headers = apache_request_headers();
foreach ($headers as $header => $value) {
    file_put_contents("log.txt","$header: $value\n",FILE_APPEND);
}

W obiekcie XMLHttpRequest możemy dodawać dowolne nagłówki do wysłania. W poprzednim kodzie JavaScript przed wysłaniem żądania wstawmy kod:

xhr.setRequestHeader("Naglowek","Dowolny tekst");

Jeśli teraz uruchomimy nasz JavaScript otrzymamy w logu informację podobną do tej:

FIRE
Host: localhost
User-Agent: Mozilla/5.0 ...
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: pl,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Naglowek: Dowolny tekst
Referer: http://localhost/index.html
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache

Widzimy, że przeglądarka wysłała zestaw swoich nagłówków a dodatkowo dołączyła nasz. Wywołując metodę setRequestHeader() kilkukrotnie możemy dodać dowolne nagłówki.

Zmiana nagłówków przeglądarki też jest możliwa. Wystarczy po prostu ustawić dowolny nagłówek, który normalnie wysyła przeglądarka. Przykładowo ustawiamy typ danych:

xhr.setRequestHeader("Accept","text/plain");

Ten nagłówek zostanie wysłany zamiast domyślnego.

Czytanie nagłówków HTTP

Skoro tak łatwo możemy wysyłać nagłówki, równie łatwe powinno być ich odczytywanie. Tak jest w rzeczywistości. Do przeczytania nagłówków mamy dostępne dwie metody. Pierwsza: getResponseHeader(name) przyjmuje jako parametr etykietę nagłówka, który chcemy pobrać. Druga metoda getAllResponseHeaders() zwraca nam wszystkie nagłówki jako jeden tekst. Spróbujmy użyć ich w JavaScript:

var xhr = new XMLHttpRequest();
xhr.open("GET", "http://localhost:8000/");
xhr.onload = function(e) {
  var headers = document.querySelector("#headers");
  headers.innerHTML = this.getAllResponseHeaders();
};
xhr.send();

Jeśli będziemy mieć w kodzie HTML element z identyfikatorem „headers”, np. element <pre>, zobaczymy w nim wszystkie nagłówki, które zwróci nam serwer. Przykładowo:

Date: ...
Server: Apache
X-Powered-By: PHP/5.5.10
Keep-Alive: timeout=5, max=98
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: image/jpeg

Praktyczne zastosowanie

W poprzednim artykule przy okazji metody LINK wspomniałem o możliwości stworzenia galerii zdjęć. Obrazy same mogą informować o adresie poprzedniego i następnego zdjęcia za pomocą nagłówków. Czas wdrożyć to rozwiązanie w życie.

Na początek stwórzmy kod PHP prezentujący fotografię, np w pliku image.php:

<?php
// linki do poprzedniego i następnego zdjęcia wysyłamy w nagłówku "link"
$prev = "http://localhost/image0.jpg";
$next = "http://localhost/image2.jpg";
header("Link: $prev;rel=prev,$next;rel=next");
 
// nagłówek i dane bieżącego zdjęcia
header("Content-type: image/jpeg");
echo file_get_contents("image.jpg");

Jeśli teraz otworzymy w przeglądarce adres naszego pliku, zobaczymy po prostu fotografię. Sama przeglądarka ignoruje nagłówek „Link” bo nie wie co z nim zrobić. Możemy jednak obsłużyć go w AJAXie.

Javascript nie jest dobrze znany ze swej obsługi danych binarnych. Możemy jednak skorzystać z ustawienia responseType, którym wymusimy otrzymanie obiektu BLOB zamiast XML. Oto kod Javascript:

var xhr = new XMLHttpRequest();
xhr.open("GET", "http://localhost/image.php");
xhr.responseType = "blob";
xhr.onload = response;
xhr.send();

Cała obsługa odbywa się w funkcji response(), która tworzy obiekt z obrazkiem oraz pobiera linki:

function response(e) {
   // tworzymy obrazek i dodajemy go do strony
   var urlCreator = window.URL || window.webkitURL;
   var imageUrl = urlCreator.createObjectURL(this.response);
   document.querySelector("#photo").src = imageUrl;

  // pobieramy linki i parsujemy zwrócony tekst
  var links = this.getResponseHeader("Link").split(",");
  for (var link in links) {
  var link = links[i].split(";");
  switch (link[1]) {
    case 'rel=prev': 
      console.log("Poprzedni obrazek: "+link[0]);
      break;
    case 'rel=next':
      console.log("Następny obrazek: "+link[0]);
      break;
   }
}

Aby kod był kompletny musimy jeszcze przygotować w HTML miejsce na obrazek, np tworząc pusty element:

<img id="photo"/>

Na koniec

Warto poczytać specyfikację obiektu XMLHttpRequest, choć nie należy ona do najlżejszych lektur. Zwłaszcza różne rodzaje responseType są ciekawe. Oprócz XML możemy odbierać dane binarne w różnych formach jak również dane JSON.

Widzimy, że JavaScript pozwala nam swobodnie operować protokołem HTTP. Tworząc serwisy RESTowe możemy bez problemu korzystać z nich bezpośrednio w przeglądarce.

2 myśli na temat “REST i JavaScript – metody i nagłówki HTTP”

  1. Fajny przykład.
    W function response(e) brakuje nawiasu zamykającego „}”.
    i jest undefined, można użyć
    for (var i = 0, l = links.length; i<l; i++) {
    var link = links[i].split(";");

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *