Rozdział 11. Programowanie sieciowe. Intensywny kurs

15 stycznia 2013
1 gwiadka2 gwiazdki3 gwiazdki4 gwiazdki5 gwiazdek

Możliwe, że czytasz ten tekst w oknie przeglądarki internetowej, co oznacza że przynajmniej trochę znasz się na internecie. Rozdział ten zawiera zwięzłe i powierzchowne informacje na temat sposobu działania internetu oraz jak to się ma do języka JavaScript. Głównym tematem jest zatem mówiąc krótko programowanie sieciowe w JavaScript. Trzy kolejne rozdziały zawierają bardziej praktyczną treść, ponieważ pokazuję w nich jak za pomocą JavaScriptu badać i modyfikować strony internetowe.


Internet to mówiąc najprościej sieć komputerów obejmująca większość świata. Dzięki tym sieciom możliwe jest przesyłanie wiadomości między komputerami. Techniczne aspekty działania sieci są bardzo ciekawe, ale niestety temat tej książki jest całkiem inny. Wystarczy pamiętać, że w uproszczeniu jeden komputer, który będziemy nazywać serwerem, czeka aż inne komputery zaczną się z nim komunikować. Gdy jakiś komputer, klient, nawiąże kontakt z serwerem, następuje wymiana jakichś danych przy użyciu specjalnego języka zwanego protokołem.

Internet służy do przesyłania wiadomości przy użyciu wielu różnych protokołów. Istnieją protokoły do czatowania, udostępniania plików, używane przez szkodliwe programy do kontrolowania komputerów głupków, którzy je zainstalowali itd. Nas interesuje protokół używany przez sieć ogólnoświatową (World Wide Web). Protokół ten nazywa się HTTP, co oznacza Hyper Text Transfer Protocol (protokół przesyłania hipertekstu), i służy do pobierania stron internetowych oraz powiązanych z nimi plików.

W komunikacji HTTP serwer jest komputerem używanym do przechowywania strony internetowej. Klientem jest komputer, np. Twój, który prosi serwer o przesłanie mu tej strony, aby mógł ją wyświetlić. Taka prośba o stronę nazywa się żądaniem HTTP.


Strony internetowe i inne pliki dostępne w internecie mają adresy URL, czyli uniwersalne lokalizatory zasobów (ang. Universal Resource Locators). Adres URL wygląda następująco:

http://acc6.its.brooklyn.cuny.edu/~phalsall/texts/taote-v3.html

Adres URL składa się z trzech części. Pierwsza część, http://, oznacza, że ten adres używa protokołu HTTP. Istnieją jeszcze inne protokoły, np. FTP (ang. File Transfer Protocol — protokół przesyłania plików), które również mogą być używane do tworzenia adresów URL. Druga część, acc6.its.brooklyn.cuny.edu, określa nazwę serwera, na którym znajduje się strona. Na końcu adresu URL, /~phalsal/texts/taote-v3.html, znajduje się nazwa konkretnego pliku na serwerze.

Najczęściej z sieci WWW korzysta się przy użyciu przeglądarki internetowej. Gdy użytkownik wpisze adres URL albo kliknie odnośnik, przeglądarka wysyła odpowiednie żądanie HTTP do serwera. Jeśli wszystko się uda, serwer odpowie wysyłając przeglądarce plik, a ta wyświetli go użytkownikowi.

Jeśli plikiem tym jest dokument HTML, zostanie on zaprezentowany jako strona internetowa. Krótki opis języka HTML znajduje się w rozdziale 6, w którym dowiedzieliśmy się, że w języku tym można odwoływać się do obrazów. W rozdziale 9 dowiedziałeś się też, że strony HTML mogą zawierać elementy <script> ładujące kod JavaScript z plików. Przed wyświetleniem dokumentu HTML przeglądarka pobiera wszystkie dodatkowe zasoby z serwerów, aby dodać je do dokumentu.


Mimo że adres URL powinien wskazywać plik, możliwości serwerów sieciowych są większe niż proste znajdowanie plików i wysyłanie ich do klientów. Serwer może też przetwarzać pliki a nawet możliwe jest, że wybrany plik w ogóle nie istnieje, tylko na serwerze działa specjalny program, który otrzymując żądanie określonego adresu URL generuje na bieżąco odpowiedni dokument.

Programy przekształcające i generujące dokumenty na serwerze są wykorzystywane do tworzenia dynamicznych stron internetowych. Plik to po prostu plik i jego zawartość jest zawsze taka sama. Ale jeśli pliki są tworzone na bieżąco dla każdego żądania przez program, to u każdego użytkownika mogą wyglądać inaczej, np. ze względu na to czy użytkownik jest zalogowany lub zdefiniował jakieś ustawienia. Ponadto to bardzo ułatwia zarządzanie treścią stron internetowych — zamiast każdą nową treść wysyłać na serwer w postaci pliku, dodaje się nowy dokument do jakiegoś centralnego magazynu, a program już wie jak go znaleźć i zaprezentować.

Takie rodzaj programowania nazywa się programowaniem serwerowym. Polega na przetwarzaniu dokumentu przed jego wysłaniem do użytkownika. W większości przypadków dobrze jest też mieć program, który działa po wysłaniu strony, gdy użytkownik już ją ogląda. To nazywa się programowaniem po stronie klienta, ponieważ program działa na komputerze klienta. Programowanie po stronie klienta to główne zastosowanie JavaScriptu.


Jednak wykonywanie programów na kliencie ma pewną wadę, której nie da się wyeliminować. Nigdy nie wiadomo, jakie programy będą uruchomione na stronie, którą ma się zamiar odwiedzić. Gdyby program taki mógł wysyłać informacje z naszego komputera, niszczyć pliki albo infiltrować system, to surfowanie po internecie byłoby bardzo niebezpieczne.

Dlatego przeglądarki bardzo ograniczają możliwości programów JavaScript. Nie mogą one przeglądać plików użytkownika ani modyfikować niczego, co nie jest związane ze stroną internetową, z którą zostały pobrane. Takie izolowane środowisko programowe nazywa się piaskownicą (ang. sandbox). Nie jest łatwo tak określić uprawnienia programów aby mogły w miarę swobodnie działać, a jednocześnie nie miały możliwości narobienia szkód w komputerze. Co kilka miesięcy jakiś programista JavaScript znajduje kolejne luki w zabezpieczeniach pozwalające ominąć ograniczenia i wykonywać szkodliwe działania. Twórcy przeglądarek reagują na to modyfikacjami swoich programów tak, aby uniemożliwić wykonywanie odkrytej sztuczki i wszystko wraca do porządku. Dopóki ktoś znowu czegoś nie znajdzie…


Jedną z pierwszych powszechnie znanych sztuczek JavaScript jest użycie metody open obiektu window. Przyjmuje ona jako argument adres URL i otwiera nowe okno wyświetlając w nim stronę znajdującą się pod tym adresem.

var perry = window.open("http://www.pbfcomics.com");

Jeśli włączyłeś w rozdziale 6 blokowanie wyskakujących okienek, to najprawdopodobniej okno to zostanie zablokowane. Programy blokujące wyskakujące okienka istnieją nie bez powodu. Programiści sieciowi, z zwłaszcza ci, którzy chcą przyciągnąć uwagę użytkowników do płatnych reklam, nadużywają metody window.open tak bardzo, że większość internautów po prostu jej nienawidzi. Jest ona jednak czasami przydatna i w tej książce również kilka razy zostanie użyta. Ogólna zasada jest taka, że skrypt nie powinien otwierać nowych okien, jeśli użytkownik wyraźnie sobie tego nie zażyczy.

Warto zauważyć, że ponieważ metoda open (podobnie jak setTimeout itp.) należy do obiektu window, część window. w jej wywołaniu można opuścić. Gdy funkcja jest wywoływana „normalnie”, to zostaje wywołana jako metoda na obiekcie najwyższego poziomu, którym jest window. Ja jednak uważam, że nazwa open jest trochę za ogólna i wolę pisać window.open, dzięki czemu od razu wiadomo, że chodzi o otwarcie okna.

Wartością zwracaną przez metodę window.open jest nowe okno. Jest ono globalnym obiektem dla skryptu działającego w tym oknie i zawiera wszystkie standardowe rzeczy, takie jak konstruktor Object i obiekt Math. Gdy jednak spróbujesz je przejrzeć, większość przeglądarek prawdopodobnie na to nie zezwoli…

show(perry.Math);

Jest to związane z działaniem w piaskownicy, o której była mowa wcześniej. Strony otwierane przez naszą przeglądarkę mogą zawierać informacje przeznaczone tylko dla nas, np. w serwisach, w których trzeba się zalogować, i dlatego nie byłoby dobrze gdyby każdy skrypt mógł je odczytać. Wyjątkiem od tej reguły są strony należące do tej samej domeny: jeśli skrypt działający na stronie eloquentjavascript.net otworzy inną stronę w tej samej domenie, to może z nią robić wszystko, co chce.

Otwarte okno można zamknąć przy użyciu metody close. Jeśli jeszcze sami go nie zamknęliśmy…

perry.close();

Inne rodzaje poddokumentów, np. ramki (dokumenty w dokumentach), dla programów JavaScript również są oknami i mają własne środowiska JavaScript. W istocie środowisko, do którego masz dostęp w konsoli należy do małej niewidocznej ramki ukrytej gdzieś na tej stronie — dzięki temu trochę trudniej jest przez przypadek popsuć całą stronę podczas zabawy.


Każdy obiekt okna ma własność document zawierającą obiekt reprezentujący dokument wyświetlony w tym oknie. Obiekt ten zawiera np. własność location zawierającą informacje o adresie URL dokumentu.

show(document.location.href);

Ustawiając własność document.location.href na inny adres URL można spowodować wczytanie przez przeglądarkę innego dokumentu. Obiekt document zawiera także metodę write. Służy ona do wstawiania do dokumentu kodu HTML, który trzeba jej przekazać w postaci łańcucha. Gdy zostanie zastosowana do w pełni załadowanego dokumentu, to zastąpi go w całości otrzymanym kodem HTML, czego zazwyczaj nie chcemy. Najczęściej skrypt wywołuje ją w czasie ładowania dokumentu i wówczas kod HTML zostaje wstawiony do dokumentu w miejscu elementu script odpowiedzialnego za jej dodanie. Jest to prosty sposób na dodanie dynamicznych elementów do strony. Na przykład poniżej znajduje się banalnie prosty dokument pokazujący bieżącą godzinę.

print(timeWriter);
var time = viewHTML(timeWriter);
time.close();

W rozdziale 12 znajduje się opis bardziej klarownych i wszechstronnych sposobów modyfikowania dokumentów, ale czasami metoda document.write jest najprostszym i najlepszym rozwiązaniem.


Kolejną dziedziną dotyczącą stron internetowych, w której język JavaScript często znajduje zastosowanie są formularze. Kilka słów wyjaśnienia dl tych, którzy nie wiedzą do czego służą formularze.

Najprostsze żądanie HTTP to zwykła prośba do serwera o przesłanie pliku. Gdy żądany plik nie jest pasywnym plikiem lecz plikiem zwracanym przez program działający na serwerze, to czasami do żądania warto dołączyć także inne informacje niż tylko adres URL. Dlatego żądania HTTP mogą zawierać dodatkowe parametry. Oto przykład:

http://www.google.com/search?q=aztec%20empire

Po nazwie pliku (/search) znajduje się znak zapytania, po którym z kolei znajdują się parametry. To żądanie zawiera jeden parametr o nazwie q (prawdopodobnie od słowa ang. „query” czyli „zapytanie”), którego wartość to aztec empire. Ciąg %20 oznacza spację. W wartościach tych nie mogą występować niektóre znaki, np. spacje, ampersand (&) i znaki zapytania. Są one zastępowane specjalnymi kodami składającymi się ze znaku % i wartości liczbowej1. Znak procent w tym przypadku pełni taką samą rolę, jak ukośnik wsteczny w wyrażeniach regularnych, tylko trochę lepiej wygląda.

W języku JavaScript dostępne są funkcje encodeURIComponent i decodeURIComponent służące do dodawania tych kodów do łańcuchów i usuwania ich.

var encoded = encodeURIComponent("aztec empire");
show(encoded);
show(decodeURIComponent(encoded));

Gdy żądanie zawiera więcej parametrów, rozdziela się je znakami ampersand:

http://www.google.com/search?q=aztec%20empire&lang=pl

Formularz mówiąc najprościej to narzędzie ułatwiające użytkownikom przeglądarek tworzenie takich parametryzowanych adresów URL. W formularzu znajdują się pola, np. tekstowe i wyboru, które można zaznaczać oraz obiekty pozwalające wybrać jedna z kilku wartości itp. Najczęściej dostępny jest też przycisk zatwierdzania formularza oraz niewidoczny dla użytkownika adres URL wskazujący program, który ma być użyty do przetworzenia danych z tego formularza. Kliknięcie przycisku zatwierdzania lub naciśnięcie klawisza Enter powoduje dodanie informacji wprowadzonych w formularzu do adresu URL jako parametrów i wysłanie żądania pod taki adres.

Poniżej znajduje się kod HTML prostego formularza:

<form name="userinfo" method="get" action="info.html">
  <p>Podaj nam swoje dane, abyśmy mogli Ci wysyłać
  spam.</p>
  <p>Imię i nazwisko: <input type="text" name="name"/></p>
  <p>E-Mail: <input type="text" name="email"/></p>
  <p>Płeć: <select name="sex">
            <option>Mężczyzna</option>
            <option>Kobieta</option>
            <option>Inna</option>
          </select></p>
  <p><input name="send" type="submit" value="Wyślij!"/></p>
</form>

Posługując się nazwą formularza można uzyskać dostęp do niego z poziomu JavaScriptu, co za chwilę zostanie pokazane. Nazwy pól determinują nazwy parametrów HTTP używanych do przechowywania ich wartości. Zatwierdzenie tego formularza może spowodować utworzenie następującego adresu URL:

http://planetspam.com/info.html?name=Tadek&email=tadek@zork.com&sex=Mężczyzna

Formularze mają jeszcze wiele innych elementów i własności, ale my skoncentrujemy się tylko na tych kilku podstawowych, ponieważ przede wszystkim interesuje nas JavaScript.


Atrybut method="get" w przedstawionym formularzu oznacza, że dane wpisane w polach tego formularza mają zostać zakodowane w postaci parametrów adresu URL. Istnieje też inna metoda wysyłania parametrów o nazwie post. Żądanie HTTP wysłane metodą post oprócz adresu URL zawiera dodatkowo blok danych. Formularz wysyłany metodą post wstawia wartości swoich parametrów do tego bloku zamiast do adresu URL.

Metoda post lepiej nadaje się do przesyłania w formularzach dużych ilości danych, ponieważ wysyłanie ich metodą get spowodowałoby powstanie kilometrowych adresów URL. Jednak różnica między tymi dwiema metodami to nie tylko kwestia urody adresów. Metody get zwyczajowo używa się do wysyłania do serwera żądań dokumentów, natomiast post do wysyłania żądań wykonania czegoś na serwerze, co spowoduje tam jakąś zmianę. Na przykład żądanie listy ostatnio opublikowanych na forum internetowym wpisów wysłano by metodą get, natomiast żądanie dodania nowego wpisu — metodą post. Trzymanie się tej zasady ma uzasadnienie — automaty przeglądające sieć, np. wyszukiwarki internetowe, zazwyczaj wykonują tylko żądania get. Gdyby za pomocą żądań get dokonywano zmian na stronach, te nieszkodliwe z założenia narzędzia mogłyby narobić wiele szkód.


Gdy przeglądarka wyświetla stronę zawierającą formularz, programy JavaScript mogą przeglądać i modyfikować wartości wprowadzane w polach tego formularza. To umożliwia wykonywanie różnych sztuczek, takich jak sprawdzanie wartości przed wysłaniem ich na serwer czy automatyczne wypełnianie niektórych pól.

Powyższy formularz znajduje się w pliku example_getinfo.html. Otwórz go.

var form = window.open("/wp-content/ejs/example_getinfo.html");

Gdy adres URL nie zawiera nazwy serwera, to nazywa się adresem względnym. Adresy względne są przez przeglądarki interpretowane jako odnoszące się do plików znajdujących się na tym samym serwerze, co bieżący dokument. Jeśli nie zaczynają się od ukośnika, brana jest ścieżka (lub katalog) bieżącego dokumentu i do tego dodaje się podaną ścieżkę.

Dodamy do naszego formularza mechanizm sprawdzający poprawność jego wypełnienia, który pozwoli na jego zatwierdzenie tylko, gdy pole nazwiska nie będzie puste, a pole adresu e-mail będzie wyglądać na poprawnie wypełnione. Jako że nie chcemy już aby formularz był wysyłany natychmiast po naciśnięciu przycisku Wyślij!, zmienimy jego atrybut type z submit na button, dzięki czemu stanie się zwykłym przyciskiem. ― W rozdziale 13 opisana jest o wiele lepsza metoda zrobienia tego, ale tutaj wystarczy nam takie uproszczone rozwiązanie.


Aby móc pracować z nowo otwartym oknem (jeśli je zamknąłeś, to otwórz je znowu), musimy powiązać je z konsolą:

attach(form);

Gdy to zrobisz, kod w konsoli będzie uruchamiany w określonym oknie. Aby sprawdzić czy rzeczywiście działamy w tym oknie, co trzeba, możemy zajrzeć do własności location i title dokumentu.

print(document.location.href);
print(document.title);

Ponieważ otworzyliśmy nowe środowisko, wcześniej zdefiniowane zmienne, jak np. form, nie są już dostępne.

show(form);

Aby wrócić do początkowego środowiska, możemy posłużyć się funkcją detach (bez argumentów). Ale wcześniej musimy dodać system sprawdzający do formularza.


Z każdym elementem HTML znajdującym się w dokumencie jest powiązany obiekt JavaScript. Obiektów tych można używać do przeglądania i manipulowania prawie wszystkimi aspektami dokumentu. W tym rozdziale będziemy używać obiektów formularzy i pól formularzy w podstawowy sposób, a w rozdziale 12 zajmiemy się nimi bardziej szczegółowo.

Obiekt document ma własność o nazwie forms zawierający łącza do wszystkich formularzy znajdujących się w dokumencie, wg nazw. Nasz formularz ma atrybut name="userinfo", a więc można go znaleźć we własności o nazwie userinfo.

var userForm = document.forms.userinfo;
print(userForm.method);
print(userForm.action);

W tym przypadku atrybuty method i action, które zostały przekazane do elementu HTML form również są dostępne jako własności obiektu JavaScript. Najczęściej tak jest, ale nie zawsze: niektóre atrybuty HTML w JavaScript mają inne nazwy, a niektórych nie ma w ogóle. Sposób dostania się do wszystkich własności przedstawiłem w rozdziale 12.

Obiekt elementu form ma własność elements odnoszącą się do obiektu zawierającego wszystkie pola formularza wg nazw.

var nameField = userForm.elements.name;
nameField.value = "Kowalski";

Obiekty pól tekstowych mają własność value, przy użyciu której można odczytywać i zmieniać ich zawartość. Jeśli wykonasz powyższy kod, zauważysz, że w polu nazwiska formularza pojawi się nazwisko.


Ćwiczenie 11.1

Dzięki możliwości odczytywania wartości pól formularza możemy napisać funkcję validInfo, która pobiera jako argument obiekt formularza i zwraca wartość logiczną: true jeśli pole name nie jest puste, a pole email zawiera coś, co wygląda jak adres e-mail oraz false w pozostałych przypadkach. Poniżej znajduje się kod implementacji tej funkcji.

function validInfo(form) {
  return form.elements.name.value != "" &&
    /^.+@.+.w{2,3}$/.test(form.elements.email.value);
}

show(validInfo(document.forms.userinfo));

Gdy zastanawiałeś się nad sprawdzaniem adresu e-mail, przyszło ci do głowy, aby użyć wyrażeń regularnych, prawda?


Pozostało nam już tylko określenie czynności, jaka ma zostać wykonana, gdy ktoś kliknie przycisk Wyślij!. Na razie jego kliknięcie nic nie zmienia. Ale można to zmienić korzystając z własności onclick.

userForm.elements.send.onclick = function() {
  alert("Klik.");
};

Podobnie jak w przypadku akcji przekazywanych do funkcji setInterval i setTimeout (rozdział 8), wartość zapisywana w onclick (lub podobnej) własności może być funkcją lub łańcuchem kodu JavaScript. Tym razem przekazaliśmy funkcję otwierającą okienko alertu. Kliknij przycisk.


Ćwiczenie 11.2

Na zakończenie prac nad systemem weryfikacji własności onclick przycisku nadamy nową wartość ― funkcję sprawdzającą formularz i zatwierdzającą go, gdy jest poprawnie wypełniony oraz wyświetlającą ostrzeżenie, gdy zawiera błędy. Przyda się wiedza, że obiekty formularzy mają metodę submit, która nie przyjmuje żadnych argumentów i tylko zatwierdza formularz.

userForm.elements.send.onclick = function() {
  if (validInfo(userForm))
    userForm.submit();
  else
    alert("Podaj nazwisko i poprawny adres e-mail!");
};

Inna sztuczka związana z polami wejściowymi formularzy i innymi elementami, które można wybierać, np. przyciskami i łączami, jest związana z metodą focus. Jeśli ma się pewność, że po wejściu na stronę użytkownik zacznie wprowadzanie danych od konkretnego pola, można automatycznie umieścić w nim kursor, aby użytkownik nie musiał w nim klikać ani przechodzić do niego w żaden inny sposób.

userForm.elements.name.focus();

Ponieważ formularz znajduje się w innym oknie, w niektórych przeglądarkach może nie być oczywiste, że coś zostało wybrane. Niektóre strony automatycznie przenoszą kursor do następnego pola, gdy poprzednie wydaje się wypełnione, np. gdy użytkownik wpisze kod pocztowy. Nie należy z tym przesadzać, ponieważ taka funkcja sprawia, że formularz zachowuje się w nieoczekiwany sposób. Jeśli użytkownik jest przyzwyczajony do zmieniania pól formularza za pomocą klawisza Tab albo źle coś wpisze i zechce to poprawić, to takie magiczne przeskakiwanie kursora może go bardzo denerwować.


detach();

Przetestuj weryfikatora. Gdy wpiszesz prawidłowe dane i klikniesz przycisk, formularz powinien zostać zatwierdzony. Jeśli w tym czasie konsola będzie cały czas do niego podłączona, to się odłączy, ponieważ nastąpi ponowne wczytanie strony i utworzenie nowego środowiska JavaScript.

Jeśli jeszcze nie zamknąłeś okna formularza, to spowoduje jego zamknięcie.

form.close();

Opisane powyżej czynności mogą się wydawać proste, ale uwierz mi, programowania po stronie klienta to nie jest bułka z masłem. Czasami bywa niesamowicie trudne. Dlaczego? Ponieważ programy przeznaczone do działania w komputerze klienta muszą prawidłowo działać we wszystkich popularnych przeglądarkach. A każda z ich działa trochę inaczej. Co gorsza każda taka aplikacja ma własny zbiór usterek. Niech Ci się nie wydaje, że skoro program jest własnością firmy dysponującej milionowym budżetem, to na pewno nie zawiera błędów. Dlatego my, programiści, musimy skrupulatnie testować nasze programy, znajdować błędy i opracowywać poprawne rozwiązania.

Możesz sobie pomyśleć: „Po prostu zgłoszę wszelkie usterki, jakie znajdę do producenta przeglądarki, aby szybko je usunął i wszystko będzie w porządku”. Niestety czeka Cię wielkie rozczarowanie. Producenci często nie spieszą się z poprawianiem błędów, a do perfekcji umiejętność tę opanował Microsoft ze swoją przeglądarką Internet Explorer, chociaż w jej najnowszych wersjach sytuacja uległa już znacznej poprawie. Błędy są i będą.

Ale nie zrażaj się. Jeśli masz obsesję na punkcie znajdowania nowych wyzwań, to tutaj na pewno znajdziesz ich wiele. A jeśli nie lubisz marnować czasu, to wystarczy, że będziesz unikać słabiej poznanych zakamarków funkcjonowania przeglądarek, aby nie wpaść w poważne kłopoty.

  1. Wartości kodów znakowych są określone w standardzie ASCII, w którym każdej literze alfabetu łacińskiego i symbolowi przypisana jest liczba z przedziału 0-127. Standard ten jest prekursorem standardou Unicode, o którym była mowa w rozdziale 2.

Autor: Marijn Haverbeke

Źródło: http://eloquentjavascript.net/1st_edition/chapter11.html

Tłumaczenie: Łukasz Piwko

Treść tej strony dostępna jest na zasadach licencji CC BY 3.0

Dyskusja

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