Rozdział 18. Delphi a Internet

Adam Boduch

Tak, tak, o Internecie była mowa już w rozdziale 11., lecz wówczas podjąłem jedynie temat programowania sieciowego z użyciem różnych protokołów internetowych. Tym razem sprawa wygląda nieco inaczej, bowiem mowa będzie o internetowych zastosowaniach Delphi.

W dziedzinie informatyki postęp jest nieunikniony. Powstają nowe usługi, a języki programowania wciąż są rozwijane. Firma Borland postanowiła udoskonalić swój produkt w zakresie internetowych zastosowań, umożliwiając użytkownikom tworzenie programów działających w sieci Internet, tzw. weplikacji (zlepek słów Web Application). Tworzenie aplikacji internetowych nie jest nowością wprowadzoną dopiero w Delphi 7, istniało już w poprzednich wersjach tego produktu. Owe technologie umożliwiają tworzenie dynamicznych stron WWW, nie wymagając od użytkownika znajomości języka HTML, nie mówiąc już o językach programowania takich jak PHP, CGI, Java czy JavaScript.

1 Z czego będziemy korzystali?
     1.1 Serwer Personal Web Server
2 CGI, ISAPI, NSAPI
3 Tworzenie rozszerzeń serwera
4 Akcje serwera
5 Uruchamianie biblioteki
6 Kod źródłowy biblioteki ISAPI
7 TWebRequest i TWebResponse
8 Wykorzystanie szablonów
     8.2 Tworzenie nowego szablonu
     8.3 Szablony dynamiczne
          8.3.1 Zdarzenie OnHTMLTag
     8.4 Przykładowy program
9 Witaj!
     9.5 Dodatkowe parametry
10 Wysyłanie i odbieranie cookies
     10.6 Ustawianie pliku cookies
     10.7 Odczyt cookies
11 Wysyłanie strumieni
12 Korzystanie z baz danych
13 WebSnap
14 Podsumowanie

W tym rozdziale:
*poznasz znaczenie terminów ISAPI, NSAPI i CGI;
*dowiesz się, w jaki sposób można tworzyć dynamiczne serwisy WWW;
*nasze przykłady będą opierać się o tworzenie aplikacji z wykorzystaniem technologii ISAPI.

Z czego będziemy korzystali?

Do zrealizowania niektórzy zadań będziemy potrzebować serwera WWW obsługującego standard ISAPI . Co prawda niektóre technologie w Delphi udostępniają własny serwer na potrzeby działania aplikacji, lecz chcąc zaprezentować w pełni działanie programów, będę potrzebował prawdziwego serwera. Ze względu na łatwość obsługi i dostępność na potrzeby tego rozdziału wybrałem serwer firmy Microsoft Personal Web Server, ale Ty możesz skorzystać z dowolnego serwera obsługującego standard ISAPI np. IIS lub Apache.

Serwer Personal Web Server

Program Personal Web Serwer (PWS) jest dostarczany wraz z systemem operacyjnym Windows. W moim przypadku (Windows 98) jest dostępny na płycie CD-ROM w katalogu add-one. Jego instalacja jest prosta - wystarczy postępować zgodnie ze wskazówkami wyświetlanymi na ekranie. Po instalacji na pulpicie zostanie utworzony skrót do owego serwera, a na dysku C: katalog Inetpub.

Program w trakcie działania przedstawiony jest na rysunku 18.1.

18.1.jpg
Rysunek 18.1. Program Personal Web Server

Właściwe uruchomienie serwera nastąpi po naciśnięciu przycisku Uruchom. Od tego momentu po wpisaniu w przeglądarce internetowej adresu http://127.0.0.1 wczytana zostanie strona z naszego serwera.

Program Personal Web Server można także pobrać z Internetu ? wystarczy skorzystać z jakieś popularnej wyszukiwarki, np. www.google.pl.

CGI, ISAPI, NSAPI

Świat Internetu jest pełen niezrozumiałych pojęć, jak chociażby CGI czy ISAPI. Za chwilę postaram się objaśnić, o co właściwie w tym wszystkim chodzi.

Kiedy powstał Internet, a wraz z nim strony WWW, ich wyświetlanie ograniczało się jedynie do statycznej prezentacji obrazu lub tekstu (czyli stron zapisanych w języku HTML ? ang. HyperText Markup Language). Z czasem, wobec wciąż powiększającej się ?pajęczyny? stron WWW, statyczne strony przestały wystarczać. Serwisy stawały się coraz bardziej rozbudowane, a każda np. grafika wymagała dokonania modyfikacji w każdej podstronie. Zaczęto szukać rozwiązania polegającego na tworzeniu dynamicznych stron WWW, czyli takich, których wygląd zależy od określonej czynności. Pierwszą specyfikacją tego typu było CGI (ang. Common Gataway Interface), które umożliwiało zapisywanie specjalnych skryptów (programów CGI), które generowały dynamiczne strony WWW. Obecnie skrypty CGI są pisanie przeważnie w języku Perl, który jest zwykłym językiem programowania ? dzięki niemu można w łatwy sposób stworzyć księgę gości, system nowości oraz inne elementy interaktywnych stron WWW.

W tamtym okresie na rynku serwerów WWW dominowały dwie firmy ? Microsoft oraz Netscape. Obie doceniły znaczenie tworzenia dynamicznych stron WWW i utworzyły podobne do siebie standardy ? ISAPI (Microsoft) oraz NSAPI (Netscape). Zarówno ISAPI, jak i NSAPI to w rzeczywistości biblioteki DLL, umożliwiające dynamiczne generowanie stron internetowych; nazywane są często rozszerzeniami serwerów WWW. Większą popularność zyskała technologia ISAPI i to przede wszystkim nią zajmiemy się w tym rozdziale. Jednak ISAPI potrzebuje do działania serwera WWW ? stąd potrzebny nam był chociażby najprostszy serwer, jakim jest Personal Web Server.

Obecnie dominującą technologią tworzenia stron WWW jest PHP, lecz niektóre firmy (rzadziej prywatne osoby) wciąż stosują ISAPI w celu zaprojektowania dynamicznych stron i dlatego zajmiemy się teraz ich tworzeniem.

W tym miejscu należy się jeszcze jedna, mała uwaga. W momencie wpisania w przeglądarce np. adresu http://127.0.0.1/SCRIPTS/ISAPI.dll załadowana zostanie biblioteka DLL, która (niestety) będzie przebywać w przestrzeni adresowej serwera aż do jego zamknięcia. Inaczej mówiąc, Windows nie pozwoli na usunięcie takiej biblioteki ani jej zmodyfikowanie przed zakończeniem pracy serwera. To rozwiązanie ma jednak zalety w postaci większej wydajności, w przeciwieństwie do programów CGI, które muszą być uruchamiane za każdym wywołaniem.
Nie wszystkie serwery stosują takie praktyki ? w niektórych istnieje możliwość podmiany pliku w trakcie działania serwera.

Tworzenie rozszerzeń serwera

Tworzenie biblioteki ISAPI zaczynamy jak zwykle w Repozytorium (rysunek 18.2). Po zaznaczeniu ikony Web Server Application i kliknięciu OK Delphi otworzy okno, w którym będziemy musieli wybrać rodzaj rozszerzenia serwera WWW ? patrz rysunek 18.3.

18.2.jpg
Rysunek 18.2. Repozytorium z zaznaczoną ikoną Web Server Application

18.3.jpg
Rysunek 18.3. Nowa aplikacja serwera

Pierwsze domyślnie zaznaczone pole to projekt biblioteki ISAPI lub NSAPI (tym się zajmiemy); kolejna opcja służy do tworzenia rozszerzenia typu CGI, które przebiega bardzo podobnie. Kolejne dwie pozycje związane są z tworzeniem modułów najpopularniejszego serwera WWW ? Apache. Mamy do wyboru tworzenie modułu do wersji 1.x tego serwera lub do wersji 2.x.

Ostatnia pozycja związana jest z tworzeniem rozszerzenia wykorzystującego specjalny debuger, który stanowi także serwer dla tworzonego projektu.

W poprzednich wersjach Delphi możliwe było także tworzenie aplikacji rozszerzeń Win-CGI (Common Gataway Interface for Windows), lecz obecnie Borland wycofał się z tej strategii, uznając ją za przestarzałą.

Korzystanie z ISAPI (Internet Server API) jest możliwe jedynie na platformie Windows.

Po wybraniu typu rozszerzenia Delphi utworzy formularz oparty o klasę TWebModule, która stanowi jedynie ?pojemnik? na umieszczane komponenty. W rzeczywistości nie pozwala na umieszczanie komponentów wizualnych, gdyż nie ma to w tym wypadku najmniejszego sensu. Jedyne komponenty mogące znaleźć się na formularzu to komponenty niewidoczne ? w szczególności komponenty z zakładki Internet oraz komponenty do obsługi baz danych.

Akcje serwera

Klasa TWebModule implementuje system obsługi protokołu HTTP, jednak wykorzystanie ISAPI opiera się na tzw. akcjach, w ramach których program wykonuje pewne zadania. Sama ?akcja? dołączana jest do adresu w przeglądarce ? np.:

http://127.0.0.1/isapi.dll/test

W tym wypadku akcję stanowi fragment /test.

Tworzenie akcji odbywa się poprzez właściwość Action klasy TWebModule (rysunek 18.4).

18.4.jpg
Rysunek 18.4. Edytowanie akcji modułu aplikacji

Całość odbywa się za pośrednictwem okna WebModule.Actions (rysunek 18.4). Okno to podzielone jest na poszczególne kolumny: PathInfo jest właśnie fragmentem wstawianym na koniec adresu i identyfikującym daną akcję. Pierwsza kolumna ? Name ? określa nazwę.

Utworzenie nowej akcji odbywa się poprzez naciśnięcie pierwszego przycisku z lewej lub naciśnięciu klawisza Insert. Kod źródłowy, który ma zostać wykonany w wyniku wywołania takiego adresu, generowany jest za pośrednictwem zdarzenia OnAction:

procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin

end;

Informacja zwrotna musi zostać zawarta w parametrze Response typu TWebResponse. W parametrze k zawarte są informacje na temat żądania HTTP, co niesie ze sobą ciekawe informacje, jak nazwa protokołu i inne nagłówki HTTP. W naszym przykładzie kod całego modułu prezentuje się tak, jak w listingu 18.1.

Listing 18.1. Kod źródłowy modułu

unit MainFrm;

interface

uses
  SysUtils, Classes, HTTPApp;

type
  TWebModule1 = class(TWebModule)
    procedure WebModule1WebActionItem1Action(Sender: TObject;
      Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  WebModule1: TWebModule1;

implementation

{$R *.dfm}

procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
  HTML : String;
begin
  HTML := '<html>' +
          '<head>' +
          '<title>Przykład wykorzystania ISAPI</title>' +
          '</head>'+
          '<body>'+
          '<h1>Witaj, użytkowniku!</h1>'+
          '<hr>'+
          '<i>Copyright (c) 2003 by Adam Boduch</i>'+
          '</body>'+
          '</html>';
  Response.Content := HTML;
end;

end.

Na samym początku następuje formułowanie zawartości strony WWW zwracanej w wyniku wywołania biblioteki. Kod HTML przypisywany jest do zmiennej HTML; zwracanie zawartości tej zmiennej następuje w momencie przypisania jej do właściwości Content parametru Response.

Uruchamianie biblioteki

Jako że nasza biblioteka nie może działać samodzielnie, należy umieścić ją w odpowiednim katalogu serwera. Zbuduj więc bibliotekę (Project/Build), w wyniku czego w katalogu z projektem utworzony zostanie plik Isapi1.dll (w moim przypadku). Ów plik należy umieścić gdzieś w katalogu serwera ? niech będzie to C:\Inetpub\scripts. Następnie uruchom przeglądarkę http://127.0.0.1/scripts/isapi1.dll/default.0.1/scripts/isapi1.dll/default.

Rezultat działania takiej biblioteki przedstawiony jest na rysunku 18.5.

18.5.jpg
Rysunek 18.5. Rezultat działania biblioteki ISAPI

Nie zapominaj o ?doklejeniu? na końcu adresu WWW fragmentu /default.

Kod źródłowy biblioteki ISAPI

Spójrz na kod źródłowy głównego projektu *.dpr biblioteki (Project/View Source):

library isapi1;

uses
  ActiveX,
  ComObj,
  WebBroker,
  ISAPIThreadPool,
  ISAPIApp,
  MainFrm in 'MainFrm.pas' {WebModule1: TWebModule};

{$R *.res}

exports
  GetExtensionVersion,
  HttpExtensionProc,
  TerminateExtension;

begin
  CoInitFlags := COINIT_MULTITHREADED;
  Application.Initialize;
  Application.CreateForm(TWebModule1, WebModule1);
  Application.Run;
end.

Na pierwszy rzut oka główny plik *.dpr jest bardzo podobny do głównego pliku zwykłej aplikacji VCL. Zwróć uwagę na trzy procedury eksportowane przez naszą bibliotekę (GetExtensionVersion, HttpExtensionProc, TerminateExtension). Pierwsza z nich zwraca serwerowi numer wersji rozszerzenia; kolejne polecenie eksportuje rozszerzenia związane z obsługą protokołu HTTP. Ostatnia procedura jest związana z prawidłową obsługą procesu zakończenia działania programu i zwolnienia biblioteki.

TWebRequest i TWebResponse

Obie klasy organizują komunikację pomiędzy serwerem WWW a rozszerzeniem, czyli biblioteką DLL. W parametrze Request znajdują się żądania klienta, czyli także nagłówki HTTP, natomiast Response określa zwracane przez bibliotekę wartości.

Klasa TWebRequest, a konkretnie jej właściwości, dostarczają wielu ciekawych informacji na temat naszego rozszerzenia oraz na temat użytkownika ? np. dane o przeglądarce (nazwa i wersja), systemie operacyjnym, metodzie wywołania strony, adresie itp. Najciekawsze informacje zgromadziłem w tabeli 18.1.

Tabela 18.1. Właściwości klasy TWebRequest

NazwaOpis
`Method`Metoda wywołania strony (GET, POST)
`ProtocolVersion`Wersja protokołu HTTP
`UserAgent`Używana przeglądarka
`URL`Adres wywoływanego programu w katalogu serwera
`ServerPort`Numer portu serwera
`ScriptName`Nazwa wywoływanego skryptu (np. /skrpty/isapi.dll)
`RemoteAddr`Adres IP użytkownika korzystającego z programu
`RemoteHost`Nazwa hosta użytkownika (np. ppp.tpnet.pl).
`Referer`Strona, z której użytkownik trafił na nasz program
`Query`Dodatkowe parametry przekazane wraz z programem (np. imie=adam&nazwisko=boduch)
`QueryFields`Rozdzielone już parametry, dołączane do programu (w postaci typu `TStringList`)
`PathInfo`Człon określający akcje do wykonania, np. w przypadku adresu http://127.0.0.1/default.dll/get właściwość zwróci /get
`Host`Host, czyli adres strony ? np. http://127.0.0.1
`ContentLength`Długość nagłówka HTTP
`ContentEncoding`Mechanizm kodowania nagłówka
`ContentFields`Zawiera parametry przekazane metodą POST do skryptu

Teraz przykład prezentujący korzystanie z tej wiedzy w praktyce ? oto, jak może wyglądać zdarzenie OnAction:

procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
  HTML : TStringList;
begin
{ konstruowanie treści zwracanej przez program }
  HTML := TStringList.Create;
  try
    HTML.Add('<html>');
    HTML.Add('<head>');
    HTML.Add('<title>Informacje dotyczące HTTP</title>');
    HTML.Add('</head>');
    HTML.Add('<body>');

    HTML.Add('<h1>HTTP</h1>');
    HTML.Add('Metoda: <b>' + Request.Method + '</b><br>');
    HTML.Add('URL: <b>' + Request.URL + '</b><br>');
    HTML.Add('Przeglądarka: <b>' + Request.UserAgent + '</b><br>');   


    HTML.Add('</body>');
    HTML.Add('</html>');

    Response.Content := HTML.Text;
  finally
    HTML.Free;
  end;
  Handled := True;
end;

Do skonstruowania strony HTML użyłem typu TStringList, gdyż jest to chyba najprostszy sposób, lepszy niż łączenie wszystkiego w jeden długi łańcuch String. Ze względu na długość kodu w przykładzie (rysunek 18.6) zaprezentowałem wykorzystanie jedynie trzech właściwości, lecz Ty ? jeżeli chcesz ? możesz napisać kod wyświetlający większą ilość danych.

18.6.jpg
Rysunek 18.6. Przykład wykorzystania danych z nagłówka HTTP

Wykorzystanie szablonów

Takie wpisywanie treści strony bezpośrednio w kodzie źródłowym programu może być trochę niepraktyczne. Można jednak ten problem ominąć, wykorzystując element szablonów. Polega to, ogólnie mówiąc, na oddzieleniu rzeczywistego kodu programu od treści HTML, która ma być wynikiem działania skryptu.

Funkcje taką umożliwia komponent TPageProducer, mieszczący się w zakładce Internet. Aby z niego skorzystać, należy podczas tworzenia akcji rozwinąć w Inspektorze Obiektów właściwość Producer i wybrać komponent typu TPageProducer (rysunek 18.7).

18.7.jpg
Rysunek 18.7. Właściwość Producer

Od tej pory przy każdorazowym wywołaniu tej akcji wyświetlony zostanie kod z właściwości HTMLDoc komponentu TPageProducer.

Tworzenie nowego szablonu

Aby nasz szablon w ogóle zadziałał, konieczne jest wpisanie jakiejś treści HTML we właściwości HTMLDoc ? np. takiej:

<html>
<head>
<title>Strona generowana z użyciem komponentu TPageProducer</title>
</head>

<body>
Witam! Oto przykładowa strona generowana z użyciem szablonów!
</body>
</html>

Właściwość HTMLDoc jest typu TStrings, więc jej edycja może nastąpić zarówno z poziomu programu, jak i podczas jego projektowania (rysunek 18.8).

18.8.jpg
Rysunek 18.8. Edycja właściwości HTMLDoc

Właściwie nie jest konieczne wpisywanie żadnego kodu ? nasz program jest już gotowy do kompilacji i uruchomienia.
Zamiast właściwości HTMLDoc można zastosować także właściwość HTMLFile. Wówczas treść strony HTML będzie odczytywana z pliku.

Szablony dynamiczne

Rozszerzenia serwerów są w końcu używane po to, aby umożliwić użytkownikowi generowanie dynamicznych stron w zależności od określonego zdarzenia. Szablony w pewien sposób uniemożliwiają zachowanie dynamiczności, gdyż zawartość dokumentu ? odczytywana z właściwości HTMLDoc ? jest stała.

Problem ten można częściowo rozwiązać, stosując specjalnie znaczniki w treści kodu HTML. Wówczas możemy w trakcie programu zmieniać zawartość owych znaczników wedle własnego uznania.

Przykładowo jeżeli w treści strony HTML znajduje się znacznik <#your_name>, to zastąpienie tego znacznika określoną treścią ogranicza się jedynie do wywołania metody OnHTMLTag komponentu TPageProducer:

procedure TWebModule1.HTMLPageHTMLTag(Sender: TObject; Tag: TTag;
  const TagString: String; TagParams: TStrings; var ReplaceText: String);
begin
  if Tag = tgCustom then
  begin
    if TagString = 'your_name' then ReplaceText := strName
    else if TagString = 'country' then ReplaceText := strCountry;
  end;
end;

Ważna jest jednak specjalna konstrukcja znaczników HTML ? muszą być zawarte w nawiasach < i >, a pierwszym znakiem nazwy znacznika musi być #. Wówczas komponent analizuje treść HTML z właściwości HTMLDoc i przy każdorazowym napotkaniu znacznika wywołuje zdarzenie OnHTMLTag.

Zdarzenie OnHTMLTag

Znaczenie poszczególnych parametrów zdarzenia OnHTMLTag jest następujące:
*Tag ? wskazanie na typ TTag, który identyfikuje rodzaj znacznika (patrz tabela 18.2).
*TagString ? łańcuch (String) identyfikujący określony znacznik. Nazwa znacznika jest pozbawiona początkowych znaków <# oraz >.
*TagParams ? dodatkowe parametry znacznika. Do określonego znacznika mogą być dołączone określone parametry, ale o tym powiem za chwilę.
*ReplaceText ? tekst, który ma zastąpić identyfikowany znacznik.

Tabela 18.2. Znaczenie poszczególnych pozycji typu TTag

W naszym przykładzie najpierw sprawdzamy, czy znacznik nie jest określony (tgCustom), a następnie podmieniamy wartości:

...
if TagString = 'your_name' then ReplaceText := strName;
...

strName to w tym wypadku zmienna globalna, która zawiera wartość przechwyconą jako parametr wywoływanego programu.

Przykładowy program

Pierwszym krokiem jest stworzenie przykładowego dokumentu HTML, który będzie formularzem, w którym użytkownik wpisze dane przesyłane następnie do programu. Kod takiego formularza prezentuje listing 18.2.

Listing 18.2. Kod źródłowy przykładowej strony WWW
<html>

<head> <title>Podaj swoje imię</title> </head> <body>

Witaj!

Proszę wpisać w poniższym formularzu swoje imię i miejsce zamieszkania

<form action="templates2.dll/get" method="GET"> Imię: <input type="text" name="your_name">
Kraj: <input type="text" name="your_country">
<input type="Submit" value="Wyślij"> </form> </body> </html> ```

Tak stworzony dokument należy zapisać np. pod nazwą formularz.html i umieścić w katalogu serwera ? np. scripts. Tematem tej książki nie jest język HTML, więc nie zamierzam tutaj szczegółowo objaśniać poszczególnych znaczników. Powiem jedynie, że powyższy kod powoduje wyświetlenie formularza z dwoma polami, w których użytkownik musi podać swoje imię i kraj, z którego pochodzi (rysunek 18.9).

18.9.jpg
Rysunek 18.9. Formularz strony WWW

Gdy użytkownik naciśnie przycisk, w przeglądarce zostanie załadowana strona:
http://127.0.0.1/scripts/templates2.dll/get?your_name=Adam&your_country=Polska
Jeżeli przyjrzysz się odnośnikowi, to stwierdzisz, że oprócz odwołania do biblioteki ISAPI zawiera on parametry wpisane w formularzu. Oznacza to, że do naszej biblioteki DLL należy odczytanie tych parametrów i przedstawienie ich w odpowiedniej formie (szablonie). Przypominam, że za odczytaniem konkretnego elementu stoi konstrukcja:

Request.QueryFields.Values['your_name'];

W powyższej instrukcji następuje odczytanie wartości parametru your_name. Pełny kod programu znajduje się w listingu 18.3.

Listing 18.3. Kod źródłowy programu

unit MainFrm;

interface

uses
  SysUtils, Classes, HTTPApp, HTTPProd;

type
  TWebModule1 = class(TWebModule)
    HTMLPage: TPageProducer;
    procedure WebModuleBeforeDispatch(Sender: TObject;
      Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
    procedure HTMLPageHTMLTag(Sender: TObject; Tag: TTag;
      const TagString: String; TagParams: TStrings;
      var ReplaceText: String);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  WebModule1: TWebModule1;

implementation

{$R *.dfm}

var
  strName, strCountry : String;

procedure TWebModule1.WebModuleBeforeDispatch(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
  strName := Request.QueryFields.Values['your_name'];
  strCountry := Request.QueryFields.Values['your_country'];
end;

procedure TWebModule1.HTMLPageHTMLTag(Sender: TObject; Tag: TTag;
  const TagString: String; TagParams: TStrings; var ReplaceText: String);
begin
  if Tag = tgCustom then
  begin
    if TagString = 'your_name' then ReplaceText := strName
    else if TagString = 'country' then ReplaceText := strCountry;
  end;
end;

end.

Powyższy listing powoduje odczytanie parametrów przekazanych do biblioteki i przedstawienie ich w szablonie. Rezultat działania programu znajduje się na rysunku 18.10.

18.10.jpg
Rysunek 18.10. Działanie biblioteki

Dodatkowe parametry

Tworząc szablon strony WWW, czyli ? inaczej mówiąc ? wpisując zawartość we właściwości HTMLDoc, można w trakcie pisania znaczników stosować różne parametry, określające dalsze zachowanie programu. Przykładowo niech zawartość szablonu wygląda tak:

<html> <head> <title>Strona generowana z użyciem komponentu TPageProducer</title> </head> <body> <#IMAGE ID=helion> </body> </html> ```

Jak widać, oprócz znacznika <#IMAGE> umieszczony jest w kodzie parametr ID o zawartości helion. Jako że w kodzie może występować kilka znaczników <#IMAGE>, trzeba je jakoś rozróżnić ? np. właśnie za pomocą parametrów.

Oto przykład, jak może wyglądać zdarzenie OnHTMLTag komponentu TPageProducer:

procedure TWebModule1.HTMLPageHTMLTag(Sender: TObject; Tag: TTag;
  const TagString: String; TagParams: TStrings; var ReplaceText: String);
begin
  if Tag = tgImage then
  begin
    if TagParams.Values['ID'] = 'helion' then
      ReplaceText := '<img src=http://127.0.0.1/helion.bmp>';
  end;
end;

Parametr TagParams określa właśnie parametry znacznika. Pierwszy warunek sprawdza, czy parametr id ma wartość helion ? jeżeli tak, przydziela do parametru ReplaceText kod HTML mający wyświetlić obrazek.

Wysyłanie i odbieranie cookies

Cookie w języku ang. oznacza ciasteczko. Jest to mechanizm przechowywania pewnych danych na komputerze klienta (odwiedzającego stronę WWW). Możliwe jest wówczas stwierdzenie, czy dany użytkownik był już na danej stronie WWW, czy jest tam po raz pierwszy. Takie ciasteczka mogą zawierać również inne informacje, jak np. datę załadowania strony itp.
Ustawienie oraz odczytanie pliku cookie odbywa się w zdarzeniu OnAction; za odczytywanie odpowiada metoda klasy TWebRequest, a za zapisanie ? TWebResponse.

Ustawianie pliku cookies

Plik cookie ustawiany jest za pomocą metody SetCookieField z klasy TWebResponse:

procedure SetCookieField(Values: TStrings; const ADomain, APath: string; AExpires: TDateTime; ASecure: Boolean);

Pierwszym parametrem w formie klasy TStrings muszą być dane, które mają zostać umieszczone w ciastku. Drugi parametr określa domenę, w ramach której ustawiony zostanie plik (cookies są odczytywane i ?widziane? na podstawie tego właśnie parametru); trzeci podobnie określa ścieżkę (adres), pod którą widoczne będzie ciastko. Parametr AExpires określa datę i czas wygaśnięcia cookie, a ostatni parametr zawiera informację, czy ciastko korzysta z protokołu zabezpieczeń (połączenie bezpieczne SSL).

Przede wszystkim utwórz dwie akcje ? /set oraz /get. Jak nietrudno się domyśleć, pierwsza będzie powodowała ustawienie ciastka, a druga jego odebranie.

procedure TWebModule1.WebModule1setAction(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
  S : TStringList;
begin
  S := TStringList.Create;
  try
    S.Add('Browser=' + Request.UserAgent); // dodanie informacji o przeglądarce
    S.Add('Visit=' + FormatDateTime('dd:mm:yyyy', Now)); // informacja o dacie wizyty
    Response.SetCookieField(S, '', '', 17-02-2004, False);
    Response.Content := 'Ciasteczko zostało ustawione...';
  finally
    S.Free;
  end;
end;

Poszczególne parametry ciastka znajdują się w kolejnych wierszach zmiennej typu TStringList. Uważaj! Nazwy ustawianych parametrów muszą być oddzielone od wartości znakiem =. Po ustawieniu parametrów, które mają widnieć w cookie, należy określić czas wywołania konkretnej funkcji ? SetCookieField. W naszym wypadku ciasteczko straci wartość 17 lutego 2004 roku.

Odczyt cookies

Wraz z pozostałymi danymi HTTP (nagłówkiem) do naszego programu dostarczane są informacje na temat cookies. Owe informacje zapisane są w zmiennej typu TStringList ? CookieFields. Oto przykład wykorzystania:

procedure TWebModule1.WebModule1getAction(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
  Response.Content := 'Byłeś tutaj ' + Request.CookieFields.Values['Visit'] + ' i korzystałeś z ' + Request.CookieFields.Values['Browser'];
end;

Rysunek 18.11. przedstawia program w trakcie działania (odczytywanie cookies).

18.11.jpg
Rysunek 18.11. Odczytanie pliku cookies

Tak naprawdę pomimo tego, że cookies mogą wydawać się doskonałą technologią, nie są przeznaczone dla bardziej zaawansowanych rozwiązań, np. wymagających przechowywania danych binarnych. Rozmiar pojedynczego ciastka wynosi 4 KB, a jedna domena może ustawić najwyżej 20 plików cookies.

W systemach Windows pliki cookies przechowywane są w katalogu C:\Windows\Cookies (Windows 9.x.).

Wysyłanie strumieni

Oprócz zwykłego tekstu możliwe jest wysyłanie do przeglądarki strumieni w postaci np. obrazków. Oczywiście takie strumienie muszą być identyfikowane przez przeglądarkę ? nie mogą to być dowolne dane binarne.

Ogólnie polega to na ustawieniu odpowiedniego typu danych wyjściowych ? w tym wypadku: image/pjpeg, identyfikującego obrazki typu JPEG. Przydzielenie danych wyjściowych odbywa się za pomocą właściwości ContentType:

Response.ContentType := 'image/pjpeg';

Poniższy kod źródłowy prezentuje ładowanie obrazku z zasobów, a następnie wysłanie całości danych do przeglądarki:

procedure TWebModule1.WebModule1sendAction(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
  JPG : TResourceStream;
begin
{ załaduj obrazek z rejestru }
  JPG := TResourceStream.Create(hInstance, 'PIC', 'JPEGFILE');
  try
  // typ danych wyjściowych ? obrazek JPEG
    Response.ContentType := 'image/pjpeg';
    Response.ContentStream := JPG; // wysyłane dane
    Response.SendResponse;  // wyślij
    Handled := True;
  finally
    JPG.Free;
  end;
end;

Ostateczne wysłanie obrazka dokonywane jest za pomocą metody SendResponse. Nie zapomnij o dołączeniu odpowiedniej dyrektywy, powodującej dołączenie do pliku ISAPI pliku z zasobami:

{$R FILES.res}

Korzystanie z baz danych

Moduł TWebModule zezwala na umieszczanie na naszym formularzu niewidocznych komponentów służących do obsługi baz danych. Obsługa takiej bazy danych jest identyczna ze zwyczajnym obsługiwaniem komponentów BDE. Różnica polega na tym, że konieczne jest samodzielne napisanie funkcji odczytujących rekordy z bazy danych. Następnie takie rekordy muszą być zapisane w formie tabeli HTML.

Na formularzu musisz więc umieścić jedynie komponent TTable ? to wystarczy. Przed kompilacją projektu z właściwości DatabaseName wybierz naszą bazę danych ? MojaBaza. Z listy właściwości TableName wybierz MainTablek. Kod źródłowy modułu ISAPI przedstawiony został w listingu 18.4.

Listing 18.4. Kod źródłowy programu korzystającego z baz danych

unit MainFrm;

interface

uses
  SysUtils, Classes, HTTPApp, HTTPProd, DB, DBTables;

type
  TWebModule1 = class(TWebModule)
    Table: TTable;
    procedure WebModule1WebActionItem1Action(Sender: TObject;
      Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  WebModule1: TWebModule1;

implementation

{$R *.dfm}

procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
  HTML : TStringList;
begin
  HTML := TStringList.Create;
  try
  { tworzenie początku szablonu }
    HTML.Add('<html>');
    HTML.Add('<head>');
    HTML.Add('<title>Odczyt z baz danych</title>');
    HTML.Add('</head>');
    HTML.Add('<body>');
    HTML.Add('<table width="60%" size="1" border="1">'); // tworzenie tabeli
    HTML.Add('<tr><td width="5%">ID</td><td width="60%">Towar</td><td width="15%">Cena</td><td width="20%">Data</td></tr>');

    Table.Active := True;

    { kolejne odczytywanie następnych rekordów i dodawanie ich do rezultatu HTML }
    while not Table.Eof do
    begin
      HTML.Add('<tr>');
      HTML.Add('<td width="5%">' + IntToStr(Table.FieldValues['id']) + '</td>');
      HTML.Add('<td widrh="60%">' + Table.FieldValues['Towar'] + '</td>');
      HTML.Add('<td width="15%">' + FloatToStr(Table.FieldValues['Cena']) + '</td>');
      HTML.Add('<td width="20%">' + DateTimeToStr(Table.FieldValues['Data']) + '</td>');  
      HTML.Add('</tr>');
      Table.Next;
    end;    

    HTML.Add('</table>');

    Table.Active := False;
    HTML.Add('</body>');
    HTML.Add('</html>');

    Response.Content := HTML.Text;
    Handled := True;
  finally
    HTML.Free;
  end;
end;

end.

Po kompilacji i umieszczeniu pliku DLL na serwerze oraz wpisaniu odpowiedniego adresu przeglądarka wyświetli rezultat widoczny na rysunku 18.12.

Przed odczytaniem rekordów z bazy danych musisz zmienić wartość właściwości Active komponentu TTable na True. Równoważne z tą instrukcją jest wywołanie metody Open i Close komponentu.

18.12.jpg
Rysunek 18.12. Odczyt rekordów z bazy danych

Nie zapomnij o konwersji! Odczytując wartości z poszczególnych kolumn za pomocą FieldValues, otrzymujemy rezultat w postaci zmiennej typu . Jeżeli jednak nie zastosujemy w tym wypadku konwersji, a wartość w kolumnie będzie typu Integer ? program (biblioteka ISAPI) nie zostanie wykonany, a serwer zwróci stronę z komunikatem o błędzie numer 500 (oznaczającym błąd serwera).

WebSnap

Dotąd mówiliśmy o tworzeniu programów (bibliotek) ISAPI. W Delphi 6 wprowadzono nową technologię, zwaną WebSnap. Pozwala ona na tworzenie wydajnych aplikacji internetowych z wykorzystaniem najnowszych technologii (projektowanie odbywa się w środowisku RAD). Postanowiłem jednak opuścić ten temat ze względu na obecność rozdziału 19., w którym opisuje nowość, jaka pojawiła się w Delphi 7 ? IntraWeb, która moim zdaniem ma ogromną przewagę nad WebSnap nie tylko pod względem prostoty tworzenia aplikacji. Pozwalam sobie nawet stwierdzić, że IntraWeb po prostu w przyszłości wyprze WebSnap, gdyż zapewnia naprawdę wielką prostotę tworzenia aplikacji ? ale o tym w kolejnym rozdziale?

Podsumowanie

W tym rozdziale miałeś okazję zapoznać się z procesem tworzenia bibliotek ISAPI i ? ogólnie ? tworzeniem dynamicznych stron WWW. Kto wie, być może technologia przyda Ci się w czasie projektowania własnych stron internetowych? Tak czy inaczej w kolejnym rozdziale zaprezentuję lepszy (nowszy) sposób na tworzenie dynamicznych witryn WWW.

Załączniki:

</li> </ul> </td> Więcej informacji



Format: B5, stron: 1048
oprawa twarda
Zawiera CD-ROM
</td> </tr> </table>

0 komentarzy