Programowanie serwerów
WWW
Niniejszy rozdział poświęcony jest programowaniu aplikacji serwerów WWW opartych na
technologiach CGI/WinCGI oraz ISAPI/NSAPI z wykorzystaniem technologii WebBroker i
InternetExpress.
Reprezentantami technologii WebBroker na gruncie C++Buildera są: specjalizowany moduł
danych, zwany modułem danych WWW lub krótko modułem WWW (ang. WebModule),
bogaty zestaw komponentów i dwa kreatory, z których najczęściej używanym jest Web Server
Application Wizard. Drugi z kreatorów Database Web Application Wizard
umożliwia integrację tabeli danych z modułem WWW.
Wspomniane komponenty podzielone są na dwie grupy. Pierwsza z nich to tzw. komponenty
producenci, m.in. TPageProducer, TDataSetPageProducer,
TDataSetTableProducer, TQueryTableProducer; C++Builder 5 w wersji Enterprise
wprowadza dodatkowo dwa komponenty: TMidasPageProducer i
TReconcilePageProducer. Komponenty drugiej grupy to podstawowe zręby aplikacji
WWW: TWebModule, TWebDispatcher, TWebRequest i TWebResponse.
Przed uruchomieniem przykładowego projektu ilustrującego zagadnienia poruszane w
niniejszym rozdziale konieczne jest dokonanie pewnych ustawień w środowisku C++Buildera, w
szczególności instalacja dodatkowych pakietów i komponentów; szczegółowe instrukcje na ten
temat znajdują się w pliku README.TXT umieszczonym na załączonym CD ROMie wraz ze
wspomnianym projektem w katalogu WebServer.
Moduły WWW
Określenia WebBroker i Moduł WWW często używane są zamiennie. W rzeczywistości
WebBroker stanowi element modułu WWW (tzw. dyspozytor akcji action dispatcher)
wzbogacający zwykły moduł danych (datamodule) o elementy charakterystyczne dla modułu
WWW. Innymi słowy, stanowi on centralną część mechanizmów umożliwiających budowę
aplikacji opartych na protokołach ISAPI/NSAPI, CGI lub WinCGI bez troszczenia się o różnice
pomiędzy tymi protokołami. Dodatkowo Web Bridge umożliwia wykorzystanie tej samej
aplikacji zarówno dla protokołu ISAPI (wszystkie wersje) jak i NSAPI (Netscape do wersji 3.6
włącznie).
Aplikacje serwerów WWW są aplikacjami niewizualnymi interfejs użytkownika zapewniają
im aplikacje klienckie. Moduły WWW nie mogą więc zawierać komponentów wizualnych, ich
konstruowanie odbywa się poza tym w sposób identyczny z konstruowaniem formularzy.
Web Serwer Application Wizard
Standardowy kreator wspomagający tworzenie aplikacji serwerów WWW Web Server
Application Wizard dostępny jest na karcie New okna New Items (otwieranego za
pomocą opcji File|New menu głównego IDE). Bezpośrednio po jego uruchomieniu
wyświetlane jest okno umożliwiające wybór protokołu tworzonej aplikacji (rys. 13.1):
Tu proszę wkleić rysunek z pliku ORIG-13-1.BMP
Rysunek 13.1 Wybór protokołu aplikacji serwera WWW
CGI
Aplikacja typu CGI (Common Gateway Interface) jest aplikacją konsolową, ładowaną przez
serwer na żądanie użytkownika i rozładowywaną po zrealizowaniu tego żądania. Żądanie klienta
przekazywane jest aplikacji poprzez standardowe wejście (StdIn), natomiast odpowiedz
zazwyczaj w formacie HTML wypisywana jest do standardowego wyjścia (StdOut).
WinCGI
WinCGI stanowi specyficzną dla Windows implementację protokołu CGI. W przeciwieństwie do
standardowego CGI aplikacja WinCGI jest aplikacją typu GUI, chociaż nie manifestuje swej
obecności w postaci wizualnej. Rolę standardowych strumieni StdIn i StOut przejął
pojedynczy plik INI, za pośrednictwem którego odbywa się wymiana danych pomiędzy serwerem
i użytkownikiem.
ISAPI/NSAPI
Rozszerzenia serwera typu ISAPI (Microsoft IIS) i NSAPI (Netscape) tym różnią się od
aplikacji CGI/WinCGI, iż w przeciwieństwie do tych ostatnich są bibliotekami DLL. Realizacja
żądań odbywa się tu szybciej, ponieważ biblioteki te, ładowane jednorazowo w efekcie
pierwszego żądania, pozostają w pamięci serwera.
Jako że przyszłe wersje Netscape z pewnością obsługiwać będą protokół ISAPI, a w chwili
obecnej możliwe jest mapowanie odwołań do ISAPI na odwołania do NSAPI, w dalszym ciągu
rozdziału używać będziemy dla uproszczenia pojedynczej nazwy ISAPI na określenie obydwu
protokołów.
CGI czy ISAPI?
Biblioteki DLL stanowiące rozszerzenia typu ISAPI mają tę niezaprzeczalną przewagę nad
aplikacjami CGI, iż są szybsze i efektywniejsze w działaniu wszystkie żądania dotyczące danej
biblioteki obsługiwane są przez jej pojedynczy egzemplarz, oszczędza się więc zarówno czas, jak i
pamięć. Owa integracja biblioteki z serwerem ma jednak swe ujemne strony: po pierwsze, jej
podmiana na nową wersję wymaga zatrzymania serwera i jego ponownego uruchomienia; po
drugie i ważniejsze błędna biblioteka może spowodować załamanie całego serwera, tak więc
przed poważnym zastosowaniem musi ona zostać przetestowana znacznie gruntowniej niż
zwykła aplikacja. Należy zdawać sobie sprawę z faktu, iż biblioteka taka pracuje w warunkach
powtarzalnego wykorzystywania (ang. serial reusability), co stwarza okazję do popełnienia wielu
typowych błędów, jak np. brak inicjowania zmiennych roboczych przy każdym żądaniu
niezależne aplikacje CGI wolne są od tego problemu.
Niezależnie jednak od tego, czy finalnym produktem danego projektu ma być aplikacja CGI, czy
biblioteka ISAPI/NSAPI, jego moduł WWW jest dokładnie taki sam. Stwarza to możliwość
przetestowania przyszłej aplikacji CGI w tymczasowej postaci biblioteki DLL (śledzenie bibliotek
DLL w C++Builderze jest znacznie łatwiejsze niż śledzenie zewnętrznych procesów). Należy w
tym celu utworzyć (w ramach jednej grupy) dwa projekty: zasadniczy, tworzący aplikację CGI i
pomocniczy, tworzący bibliotekę ISAPI. Następnie, używając Menedżera Projektu, należy usunąć
moduł WWW z projektu zasadniczego (CGI) a następnie dodać do tegoż projektu moduł WWW z
projektu pomocniczego (ISAPI) w ten sposób obydwa projekty dzielić będą ten sam moduł
WWW.
Podstawowe komponenty WebBrokera
Po wybraniu typu aplikacji z okna dialogowego pokazanego na rysunku 13.1 nastąpi utworzenie
pustego modułu danych i związanego z nim modułu zródłowego. Używając opcji File|Save
All z menu głównego IDE należy zapisać ów projekt w oddzielnym katalogu (np. WebServer)
nadając modułowi nazwę WebMod, zaś projektowi nazwę WebShow (tych właśnie nazw używać
będziemy w dalszej części rozdziału).
Przed przystąpieniem do budowy aplikacji należy wpierw zapewnić, by nie była on zależna od
żadnych zewnętrznych pakietów. W tym celu należy przejść na kartę Linker opcji projektu
(Shift+Ctrl+F11) i wyłączyć opcję Use dynamic RTL, a następnie na kartę Packages i
wyłączyć opcję Build with runtime packages.
Moduł WWW jest już gotowy do tego, by umieszczać na nim stosowne komponenty. Komponenty
te znajdują się w palecie na stronie Internet (patrz rysunek 13.2) oraz na stronie
InternetExpress, którą zajmiemy się nieco pózniej.
Tu proszę wkleić rysunek z pliku ORIG-13-2.BMP
Rysunek 13.2 Strona Internet palety Komponentów
Spoglądając na rysunek 13.2 widzimy (kolejno od lewej) ikony komponentów TClientSocket
i TServerSocket (nie mają one związku z WebBrokerem) a następnie komponenty
TWebDispatcher, TPageProducer, TQueryTableProducer,
TDataSetTableProducer, TDataSetPageProducer i TCppWebBrowser na bazie
tego ostatniego zbudowano aplikację umożliwiającą śledzenie aplikacji WWW z poziomu IDE,
którą to aplikację wykorzystywać będziemy w dalszej części rozdziału.
Niezależnie od komponentów znajdujących się w Palecie opiszemy komponenty kluczowe dla
modułów WWW: TWebModule, TWebRequest i TWebResponse.
TWebDispatcher
Komponent TWebDispatcher jest tym elementem modułu WWW, który odróżnia ów moduł
od zwykłego modułu danych (tak więc TDataModule + TWebDispatcher =
TWebModule). Jego zadaniem jest przekazywanie żądań użytkowników do odpowiednich
komponentów TWebActionItem zgrupowanych w kolekcję reprezentowaną przez właściwość
Actions modułu WWW.
W danej aplikacji może wystąpić tylko jeden komponent TWebDispatcher co oznacza, iż
aplikacja ta może posiadać tylko jeden moduł WWW. C++Builder nie zezwoli na umieszczenie
dodatkowego komponentu TWebDispatcher w module WWW utworzonym przez kreator
zagadkowy więc wydaje się fakt, iż nie zabrania on tego w stosunku do zwykłego modułu
TDataModule.
TWebModule
Moduł WWW, reprezentowany przez komponent TWebModule, może być uważany za
wrażliwą na Internet odmianę modułu danych (TDataModule). Najważniejszą jego
właściwością jest właściwość Actions wskazując kolekcję grupującą komponenty
TWebActionItem. Każdy z tych komponentów reprezentuje pewną akcję stanowiącą
odpowiednik żądania użytkownika ich właściwość PathInfo jest mianowicie tożsama z
łańcuchem występującym z treści żądania bezpośrednio po URL; za odnalezienie komponentu
TWebActionItem odpowiadającego danemu żądaniu odpowiedzialny jest TWebDispatcher.
Dodawanie i usuwanie komponentów TWebActionItem realizowane jest przez tzw. edytor
akcji, stanowiący specjalizowany edytor właściwości Actions. Można go uruchomić na kilka
sposobów na przykład klikając w wielokropek w linii właściwości Actions lub wybierając
opcję Action Editor z menu kontekstowego modułu WWW. W wersji 5 C++Buildera można
także posłużyć się projektantem modułu danych (Visual Data Module Designer) w którego
drzewie (widocznym w lewej części modułu) przedmiotowe komponenty ukazane są jako potomne
w stosunku do właściwości Actions modułu, a ich zestaw zmieniać można za pomocą menu
kontekstowego.
Rysunek 13.3 przedstawia uruchomiony edytor akcji, w oknie którego widoczne są pozycje
reprezentujące osiem akcji odpowiadających ośmiu różnych żądaniom użytkownika /hello,
/page, /dataset, /table, /query, /login, /browse i /final. Zwróć uwagę, iż pierwszy z
komponentów akcji WebActionItem1 jest komponentem domyślnym jego
właściwość Default ma wartość true. Jeżeli żądanie użytkownika nie odpowiada właściwości
PathInfo w żadnym z komponentów akcji, wybierany jest właśnie komponent domyślny.
Tu proszę wkleić rysunek z pliku ORIG-13-3.BMP
Rysunek 13.3 Edytor akcji modułu WWW
W kontekście wybranego przez TWebDispatcher komponentu akcji generowane jest następnie
zdarzenie OnAction, w ramach obsługi którego dokonać ma się realizacja żądania użytkownika.
Szkielet funkcji obsługującej to zdarzenie wygląda następująco:
void __fastcall TWebModule1::WebModule1WebActionItem1Action(
TObject *Sender, TWebRequest *Request, TWebResponse *Response,
bool &Handled)
{
}
Zanim jednak przystąpimy do oprogramowania poszczególnych akcji, konieczne jest przyjrzenie
się pewnym szczegółom związanym z parametrami Request i Response.
TWebResponse
Odpowiedz na żądanie użytkownika, generowana w ramach zdarzenia OnAction,
reprezentowana jest przez parametr Response funkcji zdarzeniowej. Parametr ten posiada kilka
właściwości, z których najważniejszą jest Content zawierająca treść odpowiedzi w postaci
łańcucha HTML wykonanie poniższej funkcji spowoduje wygenerowanie odpowiedzi Hello,
world! :
void __fastcall TWebModule1::WebModule1WebActionItem1Action(
TObject *Sender, TWebRequest *Request, TWebResponse *Response,
bool &Handled)
{
Response >Content = "
Hello, world!
"
}
Format informacji przypisywanej właściwości Content określony jest przez właściwość
ContentType. Musi mieć ona postać zgodną z jednym z formatów MIME (w postaci
typ/podtyp ) wartością domyślną jest text/html. Odpowiedzi w formie binarnej (np.
obrazki) nie mogą być jednak przekazywane wprost przez właściwość Content do ich
przekazania służy właściwość ContentStream.
Pozostałe właściwości klasy TWebResponse odpowiedzialne są za kodowanie odpowiedzi
(ContendEncoding), zapisywanie cookies w komputerze klienta (Cookies), bezpieczeństwo
(StatusCode, ReasonString, WWWAuthenticate, Realm), czas życia dynamicznie
generowanego dokumentu (Date, Expires) i informacje diagnostyczne (LogMessage).
TWebRequest
Parametr Request funkcji zdarzeniowej określa różnorodne aspekty żądania użytkownika.
Zależnie od metody wysłania żądania (GET lub POST patrz niżej) określonej przez właściwość
Method jego treść dostępna jest albo pod właściwościami Query i QueryFields, albo pod
właściwościami Content i ContentFields. Oto prosty przykład wykorzystania tej informacji
w generowanej odpowiedzi:
void __fastcall TWebModule1::WebModule1WebActionItem1Action(
TObject *Sender, TWebRequest *Request, TWebResponse *Response,
bool &Handled)
{
Response->Content = "
Hello, world!
";
if (Request->Method == "GET")
Response->Content = Response->Content + "
GET" +
"
Query: " + Request->Query;
else
if (Request->Method == "POST")
Response->Content = Response->Content + "
POST" +
"
Content: " + Request->Content;
}
Do innych użytecznych właściwości klasy TWebRequest należą m.in. CookieField
(reprezentująca treść cookie odczytanego z komputera klienta), Authorization
(odpowiedzialna za bezpieczeństwo) oraz Referrer i UserAgent (związane bezpośrednio z
metodą wysyłania żądania).
Wysłanie żądania wg metody GET polega na umieszczeniu jego treści wprost w URL. Jest to
metoda względnie szybka, lecz rozmiar przesyłanych danych jest ograniczony do co najwyżej
kilku kilobajtów.
W przypadku metody POST żądanie przesyłane jest za pomocą standardowych mechanizmów
wejścia wyjścia (lub przez plik INI w przypadku WinCGI). Przesyłanie jest wolniejsze, lecz
rozmiar żądania ograniczony jest tylko wielkością dostępnej przestrzeni dyskowej. Ponadto nie
jest widoczna treść przesyłanych danych, nie są więc one narażone na (przypadkowe) uszkodzenie
przez użytkownika.
Serwery WWW
Pora przystąpić do przetestowania pierwszej aplikacji serwera WWW. Do swego działania
wymaga ona funkcjonującego serwera WWW, którym może być Personal Web Server (dla
Windows 95/98) albo Microsoft Internet Information Server (IIS) dla Windows NT/2000.
Firmowy podręcznik użytkownika (Borland C++Builder 5 Developer s Guide) , dostarczany wraz
z kopią C++Buildera 5 zawiera w rozdziale 30. szczegółowe wskazówki co do skonfigurowania
Personal Web Servera tak, by można było na nim testować i śledzić aplikacje CGI i biblioteki
ISAPI.
Należy przypomnieć, iż biblioteki ISAPI raz załadowane przez serwer nie są rozładowywane aż do
zakończenia jego pracy. Przed utworzeniem nowej wersji biblioteki należy więc zatrzymać serwer
(oraz usługę administracyjną IIS), a po zakończeniu kompilacji uruchomić go ponownie (co
automatycznie uruchomi również usługę administracyjną IIS). Błędnie skonstruowana biblioteka
DLL może ponadto spowodować kompletne załamanie serwera, wymagające jego zatrzymania i
ponownego uruchomienia. Problem ten złagodzony został w najnowszej wersji IIS, oferującej
opcję Run in Separate Memory Space, powodującą odizolowanie wątpliwego procesu
od pozostałych procesów serwera. Możliwe jest także rozładowywanie biblioteki ISAPI po
zrealizowaniu każdego żądania i ponowne jej ładowanie po nadejściu kolejnego, co umożliwia jej
aktualizację bez zatrzymywania serwera (należy w tym celu wyłączyć opcję Cache ISAPI
Application dla danej biblioteki w oknie dialogowym Menedżera IIS); uczynienie tego w
rzeczywistym serwerze spowoduje jednak utratę korzyści wynikających z szybkości protokołu
ISAPI.
W charakterze narzędzia wspomagającego śledzenie aplikacji serwera WWW można użyć
freeware owego programu IntraBob stworzonego przez jednego z autorów amerykańskiego
wydania niniejszej książki. Program ten znajduje się na załączonym CD ROMie w katalogu
Software\IntraBob, można go także pobrać ze strony http://www.drbob42.com.
Zastępuje on serwer WWW i umożliwia śledzenie bibliotek ISAPI z poziomu IDE C++Buildera
lub Delphi. Należy przekopiować jego pliki do katalogu zawierającego przedmiotową bibliotekę
ISAPI i użyć pliku IntraBob.exe w charakterze aplikacji nadrzędnej (Host
Application), jak pokazuje to rysunek 13.4:
Tu proszę wkleić rysunek z pliku ORIG-13-4.BMP.
Rysunek 13.4 IntraBob jako aplikacja nadrzędna dla śledzonej biblioteki ISAPI
Śledzenie biblioteki DLL będzie jednakże możliwe tylko wtedy, gdy w jej kodzie zródłowym
ustanowiony zostanie punkt przerwania (breakpoint). Ustawmy go więc w jednej z początkowych
linii funkcji zdarzeniowej komponentu WebActionItem1 (rys. 13.5):
Tu proszę wkleić rysunek z pliku AG-13-A.BMP
Rysunek 13.5 Ustawienie punktu przerwania w kodzie zródłowym biblioteki DLL
Aby uruchomić śledzoną bibliotekę ISAPI należy wpierw przygotować formularz HTML
zawierający odpowiednie żądanie skierowane do tej biblioteki, na przykład:
WebBroker HTML Form
Po wyświetleniu tej strony w przeglądarce WWW, wpisaniu czegokolwiek w pole nazwy i
kliknięciu w przycisk Submit spowodujemy załadowanie biblioteki DLL i uruchomienie
aplikacji nadzorującej IntraBob.exe. Przegląda ona formularz HTML i automatycznie
wypełnia swe opcje treścią żądania, nazwą modułu wykonywalnego (.EXE lub .DLL),
pozostawiając możliwość wpisania odpowiedniej informacji w pole PathInfo w celu
spowodowania żądanej akcji modułu WWW. Kartę opcji programu IntraBob przedstawia
rysunek 13.6.
SCAN
Tu proszę zeskanować rysunek 13.5 ze strony 576 oryginału
Rysunek 13.6 Opcje programu IntraBob
Jeżeli teraz wyślemy do serwera żądanie z pustym polem PathInfo, uruchomi się domyślna
akcja, reprezentowana przez komponent WebActionItem1 i wykonanie kodu biblioteki ISAPI
zatrzyma się na ustawionym wcześniej punkcie przerwania (rys. 13.7):
SCAN
Tu proszę zeskanować rysunek 13.6 ze strony 577 oryginału
Rysunek 13.7 Zatrzymanie wykonywania biblioteki DLL na punkcie przerwania
Początek Ostrzeżenia
Zdarza się, iż w czasie wykonywania programu IntraBob sygnalizowany jest następujący
wyjątek:
Project Intrabob.exe raised exception class Exception with message
"Only one data module per application". Process Stopped.
Use Step or Run to continue.
Przyczyną tego błędu jest sposób ładowania i rozładowywania przez C++Builder bibliotek
ISAPI. Funkcja DllEntryPoint() w głównym pliku *.cpp dokonuje mianowicie
bezwarunkowego kreowania egzemplarza modułu danych, niezależnie od przyczyny wywołania
określonej przez parametr reason:
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*)
{
try
{
Application->Initialize();
Application->CreateForm(__classid(TWebModule1), &WebModule1);
Application->Run();
}
catch (Exception &exception)
{
}
return 1;
}
Próba tworzenia modułu danych podejmowana jest więc nie tylko przy załadowaniu biblioteki
(reason == DLL_PROCESS_ATTACH) lecz także przy jej rozładowaniu (reason ==
DLL_PROCESS_DETACH), jak również podczas przyłączania (reason ==
DLL_THREAD_ATTACH) i odłączania (reason == DLL_THREAD_DETACH) jej od wątku.
Należy więc wyeliminować trzy ostatnie okoliczności, wzbogacając treść funkcji o jedną
instrukcję warunkową:
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*)
{
try
{
Application->Initialize();
if (reason == DLL_PROCESS_ATTACH)
Application->CreateForm(__classid(TWebModule1), &WebModule1);
Application->Run();
}
catch (Exception &exception)
{
}
return 1;
}
Koniec ostrzeżenia
Po ponownym uruchomieniu (F9) zatrzymanej w punkcie przerwania biblioteki otrzymamy stronę
stanowiącą odpowiedz na wysłane żądanie (rys. 13.8); zwróć uwagę, iż spacje zamienione zostały
na znak + , w podobny sposób rozmaite niedrukowalne znaki reprezentowane są w postaci
kodu szesnastkowego poprzedzonego znakiem % .
SCAN
Tu proszę zeskanować rysunek 13.7 ze strony 578 oryginału
Rysunek 13.8 Wynikowa strona HTML w oknie programu IntraBob
Komponenty producenci WebBrokera
Zadaniem komponentów producentów jest tworzenie wynikowych stron HTML według
specyficznych kryteriów. Komponenty te znajdują się na stronie Internet Palety
Komponentów; w kolejnych sekcjach zaprezentujemy teraz na praktycznych przykładach
zastosowanie niektórych z nich.
TPageProducer
Pod parametr Response funkcji zdarzeniowej podstawić można cokolwiek nawet kompletną
stronę WWW często zdarza się jednak, iż strona stanowiąca rezultat odpowiedzi powinna być
oparta na predefiniowanym szablonie, zaś realizacja żądania użytkownika powinna wówczas
polegać na wypełnieniu konkretnymi wartościami pewnych pól tego szablonu. Zadanie to spełnia
komponent TPageProducer.
Wspomniany szablon może być dostarczony komponentowi dwojako: albo w postaci pliku
dyskowego nazwa tego pliku jest wówczas reprezentowana przez właściwość HTMLFile
albo w postaci listy łańcuchów TStrings lista ta wskazywana jest wówczas przez
właściwość HTMLDoc. Właściwości HTMLFile i HTMLDoc wykluczają się nawzajem zmiana
jednej z nich powoduje automatyczne wyczyszczenie drugiej.
Szablon strony może zawierać dowolny tekst w formacie HTML, jak również specjalne znaczniki
rozpoczynające się od znaku # (tzw. invalid # tags); znaczniki te są właśnie wspomnianymi
polami szablonu i są ignorowane przez przeglądarki WWW, natomiast napotkanie takiego
znacznika przez komponent TPageProducer powoduje wygenerowanie zdarzenia
OnHTMLTag, którego obsługa powinna zastąpić ów znacznik konkretną zawartością.
W charakterze przykładu rozpatrzmy następujący szablon:
TPageProducer
<#Greeting> <#Name>
It s now <#Time> and we re working with a PageProducer
Poniższa funkcja zdarzeniowa zastępuje pole <#Name> imieniem Bob, w pole <#Time> wpisuje
aktualną datę i czas, zaś pole <#Greetings> zastępowane jest powitaniem stosownym do pory
dnia:
Wydruk 13.1 Przykład zastępowania pól szablonu
przez TPageProducer
void __fastcall TWebModule1::PageProducer1HTMLTag(TObject *Sender,
TTag Tag, const AnsiString TagString, TStrings *TagParams,
AnsiString &ReplaceText)
{
if (TagString == "Name")
ReplaceText = "Bob";
else
if (TagString == "Time")
ReplaceText = DateTimeToStr(Now());
else
if ((double)Time() < 0.5) // przed południem
ReplaceText = "Good Morning";
else
if ((double)Time() > 0.7) // po 16:48 (0.7 dnia)
ReplaceText = "Good Evening";
else
ReplaceText = "Good Afternoon";
}
Zastępowanie znacznika ustaloną a priori zawartością jest jednak mało ciekawe, wszak formularze
HTML przeważnie pytają użytkownika o imię i nazwisko. Moduł WWW posiada właściwość
Request, której wartość identyczna jest z wartością właściwości Request bieżącego
komponentu akcji, możemy więc w prosty sposób uzyskać dostęp do poszczególnych pól żądania
(za pośrednictwem podwłaściwości QueryFields lub ContentFields) to samo tyczy się
właściwości Response.
Oto więc bardziej rozbudowana obsługa zdarzenie OnHTMLTag:
Wydruk 13.2 Inny przykład zastępowania pól
szablonu przez TPageProducer
void __fastcall TWebModule1::PageProducer1HTMLTag(TObject *Sender,
TTag Tag, const AnsiString TagString, TStrings *TagParams,
AnsiString &ReplaceText)
{
if (TagString == "Name")
{
if (Request->Method == "POST")
ReplaceText = Request->ContentFields->Values["Name"];
else
ReplaceText = Request->QueryFields->Values["Name"];
}
else
if (TagString == "Time")
ReplaceText = DateTimeToStr(Now());
else
if ((double)Time() < 0.5)
ReplaceText = "Good Morning";
else
if ((double)Time() > 0.7)
ReplaceText = "Good Evening";
else
ReplaceText = "Good Afternoon";
}
Skojarzenie komponentu producenta z odpowiednim komponentem akcji dokonuje się za pomocą
właściwości Producer tego ostatniego. Jest to nowość wersji 5 C++Buildera, w poprzednich
wersjach kojarzenie to musiało być dokonywane jawnie w kodzie programu i polegało na
przepisaniu zawartości komponentu producenta do parametru Response funkcji zdarzeniowej
komponentu akcji. W przypadku komponentu wykonującego obsługę żądania /hello naszego
projektu wyglądałoby to następująco:
void __fastcall TWebModule1::WebModule1WebActionItem2Action(
TObject *Sender, TWebRequest *Request, TWebResponse *Response,
bool &Handled)
{
Response->Content = PageProducer1->Content();
}
Nowa możliwość wersji 5 eliminuje co prawda konieczność pisania dodatkowego kodu, lecz tym
samym uniemożliwia przechwycenie (np. przez ustawienie punktu przerwania) transferu danych
od komponentu producenta do komponentu akcji. W razie potrzeby zawsze można posługiwać się
rozwiązaniem tradycyjnym.
Wpisując nazwisko Bob Swart do formularza uruchamiającego bibliotekę ISAPI ujrzymy w
odpowiedzi stronę prezentowaną na rysunku 13.9; należy przy tym pamiętać o właściwym
ustawieniu wartości PathInfo (na /hello) bądz to w oknie programu IntraBob, bądz
wprost w treści formularza:
Zwróć uwagę, iż nazwa pola pobieranego z formularza tożsama jest z nazwą parametru zapytania
SQL. Wpisując w to pole np. 1221 otrzymamy listę wszystkich zamówień związanych z
kontrahentem o numerze 1221 w granicach określonych przez właściwość MaxRows
zawierającą ograniczenie na liczbę wyświetlanych wierszy. Nadając tej właściwości dużą wartość
zapewniamy sobie co prawda wyświetlenie wszystkich interesujących rekordów, lecz ryzykujemy
długie oczekiwanie na moment, gdy cokolwiek pojawi się w oknie przeglądarki wyświetlanie
tabeli nie zostanie bowiem rozpoczęte przed odczytaniem znacznika . Uwaga ta odnosi
się także do komponentu TDataSetTableProducer.
Rysunek 13.14 przedstawia wyświetlenie wyników zapytania w sytuacji, gdy w pole Custno na
formularzu wpisano 1221.
SCAN
Tu proszę zeskanować rysunek 13.13 ze strony 589 oryginału
Rysunek 13.14 Wynikowy zbiór parametryzowanego zapytania SQL wyświetlany za pomocą
komponentu TQueryTableProducer
Uzyskany wynik stwarza doskonałą okazję, by połączyć go z tym pokazanym na rysunku 13.13
tak by dla wskazanego kontrahenta wyświetlone zostały (w osobnym oknie) związane z nim
zamówienia. Wymaga to modyfikacji funkcji zdarzeniowej formatującej komórki tak, by
komórki w kolumnie CustNo zawierały hiperłącza, których klikanie powodować będzie
generowanie nowych żądań /query. Można także przekształcić każdą ze wspomnianych
komórek w formularz z ukrytym polem o nazwie Custno i predefiniowaną zawartością równą
numerowi kontrahenta. Te dwie możliwości hiperłącza i formularze rozróżniane są w treści
funkcji zdarzeniowej za pomocą symbolu kompilacji warunkowej LINK.
Oto zapowiadana funkcja w swej ostatecznej postaci:
void __fastcall TWebModule1::DataSetTableProducer1FormatCell(
TObject *Sender, int CellRow, int CellColumn, THTMLBgColor &BgColor,
THTMLAlign &Align, THTMLVAlign &VAlign, AnsiString &CustomAttrs,
AnsiString &CellData)
{
if ((CellColumn == 0) && (CellRow > 0)) // 1.kolumna - CustNo
CellData =
#ifdef LINK
// hiperłącze
"
CellData + "\">" + CellData + "";
#else
// formularz
(AnsiString)"
";
#endif
else
if (CellData == "") BgColor = "Silver";
else
if ((CellColumn == 6) && (CellData.Pos("US") > 0))
BgColor = "Red";
}
Na rysunku 13.15 widzimy znajomą kartotekę kontrahentów ze zmodyfikowaną kolumną
CustNo, rysunek 13.16 przedstawia natomiast listę zamówień wybranego kontrahenta.
SCAN
Tu proszę zeskanować rysunek 13.14 ze strony 591 oryginału
Rysunek 13.15 Wyświetlenie kartoteki kontrahentów za pomocą komponentu
TDataSetTableProducer, z hiperłączami w pierwszej kolumnie
SCAN
Tu proszę zeskanować rysunek 13.15 ze strony 591 oryginału
Rysunek 13.16 Wyświetlenie zamówień związanych z określonym kontrahentem, za pomocą
komponentu TQueryTableProducer
Jako że IntraBob potrafi śledzić jedynie skutki żądań generowanych przez formularze HTML, nie
będzie on w stanie śledzić skutków kliknięć w hiperłącza kontrahentów za to bez problemów
poradzi sobie z komórkami formularzami.
Zarządzanie stanem sesji
Pamiętasz wynik produkowany przez TDataSetTableProducer? Wspominaliśmy wówczas
o możliwości utrzymywania informacji o numerze bieżącego rekordu jako przykładowej
informacji o stanie dialogu użytkownika z serwerem. Sprawa ta jest o tyle interesująca, iż HTTP
jako protokół bezstanowy nie oferuje żadnych standardowych środków w tym zakresie, muszą
więc one być zapewnione za pomocą odrębnych technik.
Gruby URL
Najprostszym sposobem przechowania informacji o stanie jest uczynienie jej częścią URL
uczyniliśmy to już w przypadku hiperłączy w wyświetlanej kartotece kontrahentów
"
CellData + "\">" + CellData + "";
gdzie uczyniliśmy numer kontrahenta częścią URL. Na tej samej zasadzie, jeżeli chcielibyśmy
przeglądnąć pierwszy rekord tabeli, można by sformułować żądanie jako
Podobne podstrony:
13 00 Roboty specjalistyczne13 00 23 a3jjrjqbta5bc52vgazto6k3o2p5nm3odmyvc6i006 00 (13)TI 00 09 13 T pl(2)TI 00 10 13 T pl(2)WSM 00 13 plegzamin 00 06 13UAS 13 zaoer4p2 5 13więcej podobnych podstron