Wydawnictwo Helion
ul. Koœciuszki 1c
44-100 Gliwice
tel. 032 230 98 63
e-mail: helion@helion.pl
ASP.NET AJAX.
Programowanie
w nurcie Web 2.0
Autor: Christian Wenz
T³umaczenie: Marek Pa³czyñski
ISBN: 978-83-246-1494-3
Tytu³ orygina³u:
Programming ASP.NET AJAX:
Build rich, Web 2.0-style UI with ASP.NET AJAX
Format: 168x237, stron: 432
Wykorzystaj najlepsze rozwi¹zania technologii AJAX
i stwórz interaktywn¹ stronê internetow¹
•
Jak wykorzystywaæ dane serwerowe?
•
Jak tworzyæ i udostêpniaæ w³asne kontrolki?
•
Jak aktualizowaæ czêœæ strony w regularnych odstêpach czasu?
Zastanawia³eœ siê, dlaczego interaktywne witryny ciesz¹ siê dziœ tak¹ popularnoœci¹? Dzieje siê
tak g³ównie dlatego, ¿e wymagaj¹ one od u¿ytkowników wspó³uczestnictwa w tworzeniu
i rozwoju serwisu, a tym samym powoduj¹, ¿e abonenci maj¹ du¿y wp³yw na jego ostateczny
kszta³t. Dziêki temu ka¿dy odbiorca korzysta z atrakcyjnej witryny idealnie dopasowanej
do swoich potrzeb. To w³aœnie ASP.NET AJAX umo¿liwia projektowanie profesjonalnych,
interaktywnych stron WWW w duchu Web 2.0. Znawcy tematu zapewniaj¹, ¿e AJAX
jest rozwi¹zaniem przysz³oœciowym w dziedzinie projektowania serwisów internetowych.
O tym, jak za pomoc¹ tej technologii wdro¿yæ w swoim serwisie rozwi¹zania zgodne z filozofi¹
Web 2.0, dowiesz siê w³aœnie z tego podrêcznika.
W ksi¹¿ce „ASP.NET AJAX. Programowanie w nurcie Web 2.0” zamieszczono, oprócz
teoretycznych wiadomoœci, mnóstwo przyk³adów demonstruj¹cych dzia³anie najwa¿niejszych
mechanizmów œrodowiska ASP.NET AJAX. Przedstawione rozwi¹zania maj¹ bardzo ogólny
charakter, a zatem mo¿esz szybko dostosowaæ je do potrzeb w³asnej aplikacji. Korzystaj¹c
z tego podrêcznika, nauczysz siê m.in. projektowaæ w³asne kontrolki i udostêpniaæ je
w serwisie Toolkit, poznasz zasady korzystania ze standardowych bibliotek AJAX-a w innych
œrodowiskach (np. PHP). Bêdziesz umia³ zbudowaæ profesjonaln¹, dynamiczn¹ stronê
internetow¹, bazuj¹c¹ na platformie ASP.NET AJAX.
•
Struktura i architektura œrodowiska ASP.NET AJAX
•
JavaScript
•
Rozszerzenia ASP.NET AJAX
•
Us³ugi sieciowe
•
Odœwie¿anie czêœci strony — obiekt UpdatePanel
•
Lokalizacja i globalizacja aplikacji
•
ASP.NET Control Toolkit
•
Animacja na stronie WWW
•
Wi¹zanie i walidacja danych
•
Zachowania i komponenty
•
Dokumentacja klasy XMLHttpRequest i modelu DOM
P³yñ z nurtem nowoczesnoœci — twórz elektryzuj¹ce, interaktywne strony WWW!
3
Spis tre
ļci
Przedmowa ...............................................................................................................................9
I Podstawy ................................................................................................ 17
1. ASP.NET AJAX, Ajax i ASP.NET .................................................................................... 19
ASP.NET AJAX i Ajax
19
ASP.NET AJAX i ASP.NET
21
Wymagania wstöpne i instalacja ASP.NET AJAX
23
Struktura i architektura Ĉrodowiska ASP.NET AJAX
29
Pierwszy przykäad strony ASP.NET AJAX — Witaj uĔytkowniku
31
Kontrolka ScriptManager
35
Podsumowanie
37
Do dalszego czytania
37
2. JavaScript .....................................................................................................................39
Jözyk JavaScript
41
Programowanie obiektowe
51
Dostöp do elementów strony
54
Metody modelu DOM
58
Podsumowanie
59
Do dalszego czytania
59
3. Ajax .............................................................................................................................. 61
Obiekt XMLHttpRequest
61
Obiekt XMLDocument
71
JSON
76
Podsumowanie
79
Do dalszego czytania
79
4
_ Spis treļci
II Rozszerzenia ASP.NET AJAX ..................................................................81
4. Wykorzystanie rozszerze
ħ JavaScript ļrodowiska ASP.NET AJAX ...........................83
Skróty ASP.NET AJAX i funkcje pomocnicze
83
Rozszerzenia istniejñcych obiektów JavaScript
86
Techniki programowania obiektowego dla jözyka JavaScript w ASP.NET AJAX
87
Klienckie wersje klas .NET
98
Podsumowanie
102
Do dalszego czytania
102
5. Us
ĥugi sieciowe .......................................................................................................... 103
Obsäuga bäödów
103
Metody strony
107
Przechowywanie informacji o stanie sesji
110
Wymiana zäoĔonych struktur danych miödzy klientem i serwerem
115
Wykorzystanie usäug sieciowych z poziomu skryptu JavaScript
119
Podsumowanie
129
Do dalszego czytania
129
6. Od
ļwieżanie czýļci strony — obiekt UpdatePanel ..................................................131
Przeksztaäcenie fragmentu strony w aktualizowany obszar
132
Podsumowanie
145
Do dalszego czytania
146
7. Wykorzystanie us
ĥugi profili ASP.NET AJAX ............................................................ 147
Przygotowanie witryny
148
Dostöp do danych profilu
149
Dostöp do danych profilu zdefiniowanych w grupie
154
Podsumowanie
158
Do dalszego czytania
158
8. Wykorzystanie us
ĥugi uwierzytelniania ASP.NET AJAX .......................................... 159
Przygotowanie aplikacji
159
Logowanie i wylogowanie
162
Podsumowanie
168
Do dalszego czytania
168
9. Lokalizacja i globalizacja aplikacji ............................................................................ 169
Lokalizacja
170
Globalizacja i internacjonalizacja
182
Podsumowanie
186
Do dalszego czytania
186
Spis tre
ļci
_
5
III ASP.NET AJAX Control Toolkit ............................................................. 187
10. Korzystanie z pakietu Control Toolkit ...................................................................... 189
Instalacja pakietu Control Toolkit
189
Korzystanie z pakietu kontrolek
192
Podsumowanie
195
Do dalszego czytania
195
11. Animacja na stronie WWW ....................................................................................... 197
Platforma animacji
197
Mechanizm „przeciñgnij i upuĈè”
204
Podsumowanie
207
Do dalszego czytania
207
12. Automatyczne uzupe
ĥnianie wprowadzanych danych,
zwalczanie spamu i inne operacje ............................................................................209
Tworzenie harmonijkowych obszarów
209
Zachowanie wzglödnego poäoĔenia elementu
211
WyposaĔenie kontrolki TextBox w funkcjö automatycznego uzupeäniania danych 213
Doäñczenie kalendarza do pola tekstowego
220
Dynamiczne zwijanie pojedynczego panelu
221
WyĈwietlanie okna komunikatu
223
Zwalczanie spamu w blogach i na innych forach internetowych
226
Tworzenie zakäadek
228
Podsumowanie
230
Do dalszego czytania
230
13. Tworzenie i udost
ýpnianie wĥasnych kontrolek ...................................................... 231
Tworzenie wäasnych kontrolek ASP.NET AJAX
231
Doäñczenie komponentu do pakietu Control Toolkit
239
Podsumowanie
247
Do dalszego czytania
248
IV ASP.NET AJAX Futures ......................................................................... 249
14. Kontrolki klienckie ..................................................................................................... 251
Podstawy korzystania z kontrolek klienckich ASP.NET AJAX
251
Korzystanie z kontrolek ASP.NET AJAX
252
Obsäuga zdarzeþ kontrolek
267
Podsumowanie
271
Do dalszego czytania
271
6
_ Spis treļci
15. Wi
ézanie i walidacja danych ....................................................................................273
Wiñzanie danych
273
Walidacja danych
289
Podsumowanie
303
Do dalszego czytania
303
16. Zachowania i komponenty ........................................................................................305
Wykorzystanie zachowaþ
305
Wykorzystanie komponentów
317
Podsumowanie
319
Do dalszego czytania
319
17. Wykorzystanie danych serwerowych ...................................................................... 321
Kontrolka ListView
321
Utworzenie wäasnego Ēródäa danych
336
Podsumowanie
341
Do dalszego czytania
341
18. Animacje ....................................................................................................................343
Zastosowanie animacji
343
Wykorzystanie animacji do uzyskania efektu zanikania
344
Podsumowanie
354
Do dalszego czytania
354
19. Usprawnianie dzia
ĥania zakĥadek oraz przycisków „w przód” i „w tyĥ” ...............355
Poprawianie kodu
356
Usprawnianie zakäadek oraz przycisków „w przód” i „w tyä”
za pomocñ kontrolki UpdateHistory
358
Usprawnianie zakäadek oraz przycisków „w przód” i „w tyä”
za pomocñ kontrolek ASP.NET AJAX Futures
362
Podsumowanie
368
Do dalszego czytania
368
20. Rozszerzenie Web Parts ............................................................................................369
Wykorzystanie Ĉrodowiska ASP.NET AJAX z rozszerzeniem ASP.NET Web Parts 369
Podsumowanie
374
Do dalszego czytania
374
Spis tre
ļci
_
7
V Biblioteka Microsoft AJAX ...................................................................375
21. Wykorzystanie ASP.NET AJAX w po
ĥéczeniu z innymi technologiami sieciowymi ......377
Wykorzystanie rozwiñzaþ ASP.NET AJAX w aplikacji PHP
378
Podsumowanie
382
Do dalszego czytania
382
Dodatki ................................................................................................. 383
A Uruchamianie aplikacji ASP.NET AJAX .....................................................................385
B Dokumentacja klasy XMLHttpRequest ..................................................................... 397
C Dokumentacja modelu DOM .....................................................................................399
D Dokumentacja
ļrodowiska ASP.NET AJAX ...............................................................403
E Dokumentacja kontrolek ScriptManager, UpdatePanel, UpdateProgress i Timer ....407
Skorowidz ..............................................................................................................................411
103
ROZDZIA
Ĥ 5.
Us
ĥugi sieciowe
Usäuga sieciowa zostaäa wykorzystana juĔ w pierwszym rozdziale ksiñĔki w przykäadzie
aplikacji „Witaj Ĉwiecie”. Jej zadanie polegaäo wówczas na przekazywaniu danych miödzy
klientem i serwerem. Chcñc jednak skorzystaè ze wszystkich moĔliwoĈci, jakie daje poäñcze-
nie usäug sieciowych ze skryptami JavaScript, trzeba siö zapoznaè z kilkoma bardziej za-
awansowanymi sposobami wykorzystywania tego typu rozwiñzaþ. Zaliczajñ siö do nich
miödzy innymi: obsäuga bäödów, stosowanie osadzanych usäug sieciowych (metod usäug sie-
ciowych zawartych w kodzie strony .aspx, zwanych teĔ czasami metodami strony) oraz wy-
korzystanie usäug sieciowych i skryptów JavaScript bez wsparcia ze strony platformy .NET.
W tym rozdziale zostanñ przedstawione pewne szczególne rozwiñzania Ĉrodowiska ASP.NET
AJAX zwiñzane z obsäugñ usäug sieciowych, w tym procedury obsäugi bäödów oraz prze-
chowywanie informacji o stanie sesji. Tematyka rozdziaäu obejmuje równieĔ zasady odwoäy-
wania siö z poziomu skryptów JavaScript do usäug sieciowych, które nie zostaäy przygoto-
wane w
Ĉrodowisku ASP.NET.
Obs
ĥuga bĥýdów
W analizowanych wczeĈniej przykäadach zakäadaliĈmy, Ĕe wywoäania zdalnych metod zawsze
koþczñ siö poprawnie. Nie uwzglödnialiĈmy moĔliwoĈci wygenerowania wyjñtku.
Projektanci serwisów internetowych czösto pomijajñ procedury obsäugi bäödów w przypadku
odwoäaþ do usäug sieciowych udostöpnianych przez zdalne serwery (czyli serwery pracujñce
w innej domenie). Jednñ z przyczyn jest to, Ĕe usäugi sieciowe moĔna implementowaè na
podstawie róĔnych technologii, a kaĔda z technologii dysponuje wäasnym mechanizmem
zgäaszania wyjñtków, a niektóre z nich w ogóle nie generujñ wyjñtków.
W przypadku platformy ASP.NET AJAX i rozwiñzaþ Ajax praca z usäugami sieciowymi od-
biega nieco od standardowego modelu. Nie moĔna wywoäywaè bezpoĈrednio usäugi sieciowej,
poniewaĔ zabrania tego system bezpieczeþstwa. DomyĈlnie interpreter JavaScript i obiekt
XMLHttpRequest
pozwalajñ jedynie na odwoäania z uĔyciem adresów URI z tej samej dome-
ny, z której pochodzi strona. Zatem podczas pracy w Ĉrodowisku ASP.NET AJAX wywoäania
usäug sieciowych sñ kierowane do serwera w tej samej domenie. To z kolei oznacza, Ĕe sama
usäuga sieciowa opiera siö na technologii .NET (lub WCF — nowym modelu Windows
Communication Foundation). Zasady generowania wyjñtków sñ wiöc znane.
104
_
Rozdzia
ĥ 5. Usĥugi sieciowe
Zapewnienie dostöpu do wyjñtków generowanych przez usäugi sieciowe w skryptach Java-
Script naleĔy do zadaþ platformy ASP.NET AJAX. Aby sprawdziè dziaäanie opisywanego
mechanizmu, moĔemy utworzyè usäugö matematycznñ, która bödzie dzieliäa dwie liczby.
Doprowadzenie do wygenerowania wyjñtku nie bödzie trudne — wystarczy wymusiè dzielenie
przez zero, co powinno spowodowaè wywoäanie przez usäugö wyjñtku
DivideByZeroException
.
Kod usäug sieciowej (MathService.asmx) zostaä przedstawiony w przykäadzie 5.1. Analizujñc
treĈè przykäadu, warto zwróciè uwagö na atrybuty
[ScriptService]
i
[WebMethod]
, które
muszñ byè uwzglödnione w kaĔdej usäudze sieciowej ASP.NET AJAX.
Przykäad 5.1. Usäuga sieciowa generujñca wyjñtek
MathService.asmx
<%@ WebService Language="C#" Class="MathService" %>
using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
[WebService(Namespace = "http://hauser-wenz.de/AspNetAJAX/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService]
public class MathService : System.Web.Services.WebService {
[WebMethod]
public float DivideNumbers(int a, int b) {
if (b == 0) {
throw new DivideByZeroException( );
} else {
return (float)a / b;
}
}
}
Przygotujmy stronö, która wywoäa usäugö sieciowñ. Potrzebne bödñ dwa pola edycyjne,
w których uĔytkownik bödzie wpisywaä liczby do podzielenia oraz dwa obszary na dane
wyjĈciowe — jeden na wynik dziaäania matematycznego, a drugi na ewentualne komunikaty
o bäödach. Kod musi równieĔ obejmowaè przycisk wywoäujñcy funkcjö JavaScript, która na-
stöpnie wywoäa usäugö sieciowñ.
<nobr>
<input type="text" id="a" name="a" size="2" />
/
<input type="text" id="b" name="b" size="2" />
=
<span id="c" style="width: 50px;" />
</nobr>
<br />
<input type="button" value="Podziel liczby" onclick="callService(this.form);" />
<br />
<div id="output" style="width: 600px; height: 300px;">
</div>
SpoĈród kontrolek serwerowych na stronie trzeba umieĈciè komponent
ScriptManager
wraz
z osadzonym w jego treĈci odniesieniem do wykorzystywanej usäugi sieciowej.
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>
<asp:ServiceReference Path="MathService.asmx" />
</Services>
</asp:ScriptManager>
Obs
ĥuga bĥýdów
_ 105
Dziöki takiemu rozwiñzaniu wywoäania usäugi sieciowej mogñ byè realizowane za pomocñ
obiektu poĈredniczñcego o nazwie
MathService
, który zostanie wygenerowany automatycznie.
Podczas wywoäywania metody sieciowej konieczne jest zachowanie odpowiedniej kolejnoĈci
parametrów. Najpierw definiowane sñ parametry (lub parametr) przekazywane do metody
sieciowej, a nastöpnie funkcja zwrotna wykonywana po zakoþczeniu wywoäania metody.
Jednak tym razem do metody
DivideNumbers()
zostanie przekazany jeszcze jeden dodatko-
wy parametr. Za funkcjñ zwrotnñ, wykonywanñ po zakoþczeniu wywoäania, zostanie zdefi-
niowana jeszcze jedna funkcja zwrotna. Druga z funkcji zwrotnych bödzie wywoäywana
w przypadku wystñpienia bäödów (w tym równieĔ w przypadku upäyniöcia dopuszczalnego
czasu realizacji zadania).
function callService(f) {
document.getElementById("c").innerHTML = "";
MathService.DivideNumbers(
parseInt(f.elements["a"].value),
parseInt(f.elements["b"].value),
callComplete,
callError
);
}
Funkcja obsäugi bäödów otrzymuje obiekt bäödu zawierajñcy piöè metod:
get_exceptionType( )
Metoda ta udostöpnia informacje o typie wyjñtku.
get_message( )
Metoda ta zwraca komunikat o bäödzie zwiñzany z wyjñtkiem.
get_stackTrace( )
Metoda te zwraca informacje o stosie wywoäaþ funkcji.
get_statusCode( )
Metoda ta udostöpnia kod statusowy przekazany przez serwer.
get_timeOut( )
Metoda ta pozwala na ustalenie, czy zostaä przekroczony maksymalny czas realizacji za-
dania.
Informacje na temat bäödu sñ wyĈwietlane w obszarze elementu
<div>
, który zostaä utworzony
specjalnie w tym celu.
function callError(result) {
document.getElementById("output").innerHTML =
"<b>" +
result.get_exceptionType( ) +
"</b>: " +
result.get_message( ) +
"<br />" +
result.get_stackTrace( );
}
Przygotowanie pozostaäej czöĈci kodu nie powinno naströczaè wiökszych trudnoĈci. Gdy
wywoäanie usäugi sieciowej zakoþczy siö pomyĈlnie, wynik powinien zostaè wyĈwietlony
w obszarze elementu
<span>
. Peäna treĈè strony zostaäa przedstawiona w przykäadzie 5.2.
106
_
Rozdzia
ĥ 5. Usĥugi sieciowe
Przykäad 5.2. Strona wyĈwietlajñca wyjñtek wygenerowany przez usäugö MathService.asmx
Error.aspx
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>ASP.NET AJAX</title>
<script language="Javascript" type="text/javascript">
function callService(f) {
document.getElementById("c").innerHTML = "";
document.getElementById("output").innerHTML = "";
MathService.DivideNumbers(
parseInt(f.elements["a"].value),
parseInt(f.elements["b"].value),
callComplete,
callError);
}
function callComplete(result) {
document.getElementById("c").innerHTML = result;
}
function callError(result) {
document.getElementById("output").innerHTML =
"<b>" +
result.get_exceptionType() +
"</b>: " +
result.get_message() +
"<br />" +
result.get_stackTrace();
}
</script>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>
<asp:ServiceReference Path="MathService.asmx" />
</Services>
</asp:ScriptManager>
<div>
<nobr>
<input type="text" id="a" name="a" size="2" />
:
<input type="text" id="b" name="b" size="2" />
=
<span id="c" style="width: 50px;"></span>
</nobr>
<br />
<input type="button" value="Podziel liczby" onclick="callService(this.form);" />
<br />
<div id="output" style="width: 600px; height: 300px;">
</div>
</div>
</form>
</body>
</html>
Metody strony
_ 107
Podzielenie liczby 5 przez 6 daje spodziewany wynik 0.8333333. Jednak próba podzielenia
liczby 5 przez 0 powoduje wygenerowanie przez usäugö sieciowñ wyjñtku, a w konsekwencji
wyĈwietlenie komunikatu o bäödzie wraz ze stosem wywoäaþ funkcji (wyglñd strony zostaä
pokazany na rysunku 5.1).
Rysunek 5.1. WyĈwietlenie informacji na temat wyjñtku
Informacja na temat
(nie)wy
ļwietlania komunikatów o bĥýdach
WyĈwietlanie komunikatów o bäödach w aplikacji klienckiej jest doskonaäym rozwiñzaniem
na czas uruchamiania aplikacji. Stanowi jednak bardzo duĔe zagroĔenie w Ĉrodowisku uĔyt-
kowym. Komunikaty o bäödach mogñ bowiem zawieraè tajne dane, takie jak parametry ciñ-
gów poäñczenia. Nawet jeĈli nie sñ bezpoĈrednio wyĈwietlane w oknie przeglñdarki Ĉrodo-
wisko ASP.NET AJAX moĔe je przekazywaè do aplikacji klienckiej. Aby temu zapobiec,
naleĔy wykonaè dwie czynnoĈci. Po pierwsze trzeba sprawdziè, czy do przeglñdarki nie sñ
dostarczane szczegóäowe opisy bäödu (obejmujñce dane na temat stosu wywoäaþ funkcji).
Po drugie generujñc wyjñtek po stronie serwera naleĔy uwzglödniè w komunikacie moĔliwie
najmniejszñ iloĈè szczegóäowych informacji.
Metody strony
Prawdopodobnie wiökszoĈè programistów zgodzi siö z twierdzeniem, Ĕe umieszczanie
wszystkich metod sieciowych aplikacji w oddzielnym pliku jest doĈè uciñĔliwe. Pod wzglödem
struktury aplikacji taki sposób zarzñdzania plikami wydaje siö wäaĈciwy. Jednak w przypad-
ku nieskomplikowanych skryptów i aplikacji (takich jak wiökszoĈè opisywanych w ksiñĔce)
dodatkowy plik .asmx niepotrzebnie rozbudowuje projekt.
108
_
Rozdzia
ĥ 5. Usĥugi sieciowe
Przy niewiele wiökszym narzucie kodowym (lub nawet zmniejszeniu iloĈci kodu w pewnych
okolicznoĈciach) istnieje moĔliwoĈè zamieszczenia caäego skryptu w jednym miejscu — w gäów-
nym pliku .aspx (lub w zwiñzanym z nim pliku klasy). Procedura przygotowania opisywanego
rozwiñzania skäada siö z dwóch etapów. Pierwszy sprowadza siö do zaimportowania do pliku
strony przestrzeni nazw usäug sieciowych:
<%@ Import Namespace="System.Web.Services" %>
Drugi etap polega na doäñczeniu treĈci metody sieciowej do kodu strony. Metoda usäugi sie-
ciowej (a dokäadnie metoda dziaäajñca jak metoda sieciowa) musi zostaè oznaczona za pomocñ
atrybutu
[WebMethod]
— podobnie jak w pliku .asmx. Obsäuga osadzanych metod usäug sie-
ciowych w Ĉrodowisku ASP.NET AJAX ma równieĔ pewne ograniczenia. Oto one:
x
Metoda musi byè oznaczona za pomocñ atrybutu
ScriptMethod
, opisanego w przestrzeni
nazw
System.Web.Script.Services
.
x
Metoda musi byè zadeklarowana jako publiczna (
public
).
x
Metoda musi byè zadeklarowana jako statyczna (
static
).
Przykäad metody speäniajñcej wszystkie wymienione wymagania zostaä przedstawiony poniĔej:
<script runat="server">
[WebMethod]
[System.Web.Script.Services.ScriptMethod]
public static float DivideNumbers(int a, int b)
{
if (b == 0)
{
throw new DivideByZeroException( );
}
else
{
return (float)a / b;
}
}
</script>
ćrodowisko ASP.NET AJAX automatycznie wyszukuje wszystkie opisane w ten sposób metody
i doäñcza je do klasy klienckiej
PageMethods
. Zatem aby wywoäaè metodö strony, wystarczy
posäuĔyè siö zapisem
PageMethods.DivideNumbers()
zgodnie z poniĔszym przykäadem.
function callService(f) {
document.getElementById("c").innerHTML = "";
PageMethods.DivideNumbers(
parseInt(f.elements["a"].value),
parseInt(f.elements["b"].value),
callComplete,
callError);
}
Ostatnia czynnoĈè projektowa pola na wäñczeniu wywoäaþ do osadzonych metod usäug sie-
ciowych. W terminologii ASP.NET AJAX metody te sñ nazywane „metodami strony” (ang.
page methods). Ich wäñczenie wymaga przypisania wartoĈci
true
do wäaĈciwoĈci
EnablePage-
Methods
kontrolki
ScriptManager
:
<asp:ScriptManager ID="a1" runat="server" EnablePageMethods="true" />
Metody strony
_ 109
W przykäadzie 5.3 zostaä zamieszczony peäen kod strony ASP.NET, w której znajduje siö za-
równo treĈè samej strony, jak i metoda usäugi sieciowej.
Przykäad 5.3. Kod usäugi sieciowej i strony ASP.NET AJAX zapisane w jednym pliku
Inline.aspx
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Web.Services" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
[WebMethod]
[System.Web.Script.Services.ScriptMethod]
public static float DivideNumbers(int a, int b)
{
if (b == 0)
{
throw new DivideByZeroException();
}
else
{
return (float)a / b;
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>ASP.NET AJAX</title>
<script language="Javascript" type="text/javascript">
function callService(f) {
document.getElementById("c").innerHTML = "";
PageMethods.DivideNumbers(
parseInt(f.elements["a"].value),
parseInt(f.elements["b"].value),
callComplete,
callError);
}
function callComplete(result) {
document.getElementById("c").innerHTML = result;
}
function callError(result) {
document.getElementById("output").innerHTML =
"<b>" +
result.get_exceptionType() +
"</b>: " +
result.get_message() +
"<br />" +
result.get_stackTrace();
}
</script>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server"
110
_
Rozdzia
ĥ 5. Usĥugi sieciowe
EnablePageMethods="true">
</asp:ScriptManager>
<div>
<nobr>
<input type="text" id="a" name="a" size="2" />
:
<input type="text" id="b" name="b" size="2" />
= <span id="c" style="width: 50px;"></span>
</nobr>
<br />
<input type="button" value="Podziel liczby" onclick="callService(this.form);" />
<br />
<div id="output" style="width: 600px; height: 300px;">
</div>
</div>
</form>
</body>
</html>
Wynik wyĈwietlany po zaäadowaniu strony, wprowadzeniu dwóch wartoĈci i klikniöciu
przycisku Podziel liczby zostaä pokazany na rysunku 5.2.
Rysunek 5.2. Jeden plik, jedna usäuga sieciowa, jedna operacja dzielenia
Przechowywanie informacji o stanie sesji
Usäugi sieciowe zyskaäy sobie miano doskonaäej technologii, która nie ma nic wspólnego
z aplikacjami sieciowymi. Jednak od kiedy zostaäy zintegrowane z platformñ .NET i witrynami
ASP.NET, programiĈci zyskali moĔliwoĈè projektowania rozwiñzaþ, które znacznie wykraczajñ
poza funkcje samych usäug sieciowych.
Usäugi sieciowe platformy .NET pozwalajñ miödzy innymi na przetwarzanie informacji
o stanie sesji. Dane zapisane w sesji sñ (dziöki Ĉrodowisku ASP.NET AJAX) udostöpniane
nawet aplikacjom bazujñcym na technologii Ajax. Na przykäad Ĉrodowisko ASP.NET AJAX
gwarantuje róĔnym aplikacjom Ajax (uruchomionym na jednym serwerze) dostöp do danych
tego samego uĔytkownika.
Przechowywanie informacji o stanie sesji
_ 111
Zaimplementowanie opisywanego mechanizmu jest äatwiejsze niĔ jego omówienie. Za dostöp
do danych sesji odpowiada wäaĈciwoĈè
EnableSession
atrybutu
[WebMethod]
. Jej przezna-
czenie jest takie samo, jak w przypadku metody sieciowej aplikacji .NET.
[WebMethod(EnableSession=true)]
Po uwzglödnieniu wäaĈciwoĈci
EnableSession
moĔna bezpoĈrednio odwoäywaè siö do
obiektu
Session
platformy ASP.NET i zapisaè lub odczytywaè dane. PoniewaĔ metody sie-
ciowe muszñ mieè charakter metod statycznych, konieczne jest zastosowanie odwoäania
HttpContext.Current.Session
, a nie po prostu
Session
. Pierwsze z odwoäaþ odnosi siö jedynie
do obiektów bieĔñcej instancji klasy
Page
.
W nastöpnym fragmencie skryptu zostaäy zaprezentowane dwie funkcje. Pierwsza z nich za-
pisuje bieĔñcñ wartoĈè czasu w sesji. Natomiast druga oblicza róĔnicö miödzy czasem bieĔñ-
cym a znacznikiem czasu zapisanym w sesji. JeĈli w sesji nie zostaäa zapisana Ĕadna wartoĈè,
funkcja zwraca wartoĈè
-1
.
[WebMethod(EnableSession = true)]
[System.Web.Script.Services.ScriptMethod]
public static bool SaveTime( )
{
HttpContext.Current.Session["PageLoaded"] = DateTime.Now;
return true;
}
[WebMethod(EnableSession = true)]
[System.Web.Script.Services.ScriptMethod]
public static double CalculateDifference( )
{
if (HttpContext.Current.Session["PageLoaded"] == null) {
return -1;
} else {
DateTime then = (DateTime)HttpContext.Current.Session["PageLoaded"];
TimeSpan diff = DateTime.Now.Subtract(then);
return diff.TotalSeconds;
}
}
Powróèmy na chwilö do aplikacji dzielenia dwóch liczb. Do strony zwierajñcej kod aplikacji
zostanie dodana metoda
SaveTime()
, która zapisze wartoĈè czasu, wäaĈciwñ dla chwili äa-
dowania skryptu. Z kolei w momencie obliczania wyniku dzielenia wyznaczona zostanie
róĔnica miödzy czasem bieĔñcym a zarejestrowanym wczeĈniej. W ten sposób bödzie moĔna
ustaliè, ile czasu minöäo od pobrania strony do obliczenia wyniku dziaäania (które oczywiĈcie
moĔna równieĔ wykonaè w samym jözyku JavaScript; celem przykäadu jest jednak zademon-
strowanie innego rozwiñzania).
Kolejny fragment kodu JavaScript odpowiada za wywoäanie metody sieciowej (
SaveTime()
),
która zarejestruje czas w chwili pobrania strony. PoniewaĔ w operacji tej nie jest zwracana
Ĕadna wartoĈè wynikowa, funkcja zwrotna moĔe byè funkcjñ pustñ
function pageLoad( ){
PageMethods.SaveTime(doNothing, doNothing);
}
function doNothing(result) {
//nic :-)
}
112
_
Rozdzia
ĥ 5. Usĥugi sieciowe
Zgodnie z wczeĈniejszymi zaäoĔeniami konieczne jest równieĔ zdefiniowanie metody (
call-
Service()
), która wywoäa metodö
CalculateDifference()
usäugi sieciowej. Zamieszczony
poniĔej kod uwzglödnia dwa wywoäania metod sieciowych. Pierwsze odpowiada za oblicze-
nie róĔnicy czasu miödzy pobraniem strony a klikniöciem przycisku. Drugie natomiast po-
woduje wykonanie samego dziaäania matematycznego.
function callService(f) {
document.getElementById("c").innerHTML = "";
PageMethods.CalculateDifference(
showDifference,
callError);
PageMethods.DivideNumbers(
parseInt(f.elements["a"].value),
parseInt(f.elements["b"].value),
callComplete,
callError);
}
Potrzebny bödzie jeszcze pewien kod HTML, który pozwoli na wyĈwietlenie informacji
o czasie. Wykorzystamy do tego celu kontener
<div>
. NaleĔy pamiötaè, Ĕe wynik o wartoĈci
-1
oznacza, Ĕe w sesji nie zostaä zarejestrowany znacznik czasu i w zwiñzku z tym nie moĔna
obliczyè róĔnicy czasowej.
function showDifference(result) {
if (result != -1) {
document.getElementById("output").innerHTML =
"Formularz by
Ī wyŁwietlany przez " + result + " sekund";
}
}
Kompletny kod strony (treĈè HTML i skrypt niezbödny do zaimplementowania algorytmu)
zostaä przedstawiony w przykäadzie 5.4. Wszystkie zmiany w treĈci zostaäy odpowiednio
wyróĔnione. Aby aplikacja dziaäaäa poprawnie, trzeba pamiötaè o dodaniu atrybutu
Enable-
PageMethods="true"
do kodu kontrolki
ScriptManager
. Brak atrybutu uniemoĔliwia wy-
woäanie metody strony.
Przykäad 5.4. Wykorzystanie sesji w aplikacji ASP.NET AJAX i ASP.NET
WebServiceSession.aspx
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Web.Services" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
[WebMethod(EnableSession = true)]
[System.Web.Script.Services.ScriptMethod]
public static bool SaveTime()
{
HttpContext.Current.Session["PageLoaded"] = DateTime.Now;
return true;
}
[WebMethod(EnableSession = true)]
[System.Web.Script.Services.ScriptMethod]
public static double CalculateDifference()
{
Przechowywanie informacji o stanie sesji
_ 113
if (HttpContext.Current.Session["PageLoaded"] == null)
{
return -1;
} else {
DateTime then = (DateTime)HttpContext.Current.Session["PageLoaded"];
TimeSpan diff = DateTime.Now.Subtract(then);
return diff.TotalSeconds;
}
}
[WebMethod]
[System.Web.Script.Services.ScriptMethod] public float DivideNumbers(int a, int b)
{
if (b == 0)
{
throw new DivideByZeroException();
}
else
{
return (float)a / b;
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>ASP.NET AJAX</title>
<script language="Javascript" type="text/javascript">
function pageLoad() {
PageMethods.SaveTime(doNothing, doNothing, doNothing);
}
function doNothing(result) {
//nic :-)
}
function callService(f) {
document.getElementById("c").innerHTML = "";
PageMethods.CalculateDifference(
showDifference,
callError);
PageMethods.DivideNumbers(
parseInt(f.elements["a"].value),
parseInt(f.elements["b"].value),
callComplete,
callError);
}
function showDifference(result) {
if (result != -1) {
document.getElementById("output").innerHTML =
"Formularz by
Ĩ wyĿwietlany przez " + result + " sekund";
}
}
function callComplete(result) {
document.getElementById("c").innerHTML = result;
}
function callError(result) {
if (result == null) {
window.alert("B
Īîd!");
} else {
114
_
Rozdzia
ĥ 5. Usĥugi sieciowe
document.getElementById("output").innerHTML =
"<b>" +
result.get_exceptionType() +
"</b>: " +
result.get_message() +
"<br />" +
result.get_stackTrace();
}
}
</script>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server"
EnablePageMethods="true">
</asp:ScriptManager>
<div>
<nobr>
<input type="text" id="a" name="a" size="2" />
:
<input type="text" id="b" name="b" size="2" />
= <span id="c" style="width: 50px;"></span>
</nobr>
<br />
<input type="button" value="Podziel liczby" onclick="callService(this.form);" />
<br />
<div id="output" style="width: 600px; height: 300px;">
</div>
</div>
</form>
</body>
</html>
Podczas wykonywania metody
DivideNumbers()
moĔna zauwaĔyè nieco inne dziaäanie
przeglñdarki niĔ w poprzednich zadaniach. Po pierwsze, serwer dostarcza plik cookie zwiñ-
zany z sesjñ (o ile w pliku Web.config nie zostaäa wäñczona opcja zarzñdzania sesjñ bez uĔycia
plików cookie). JeĔeli w przeglñdarce zostaäa wäñczona opcja pytania o zezwolenie na przyjö-
cie pliku cookie, na ekranie powinno siö wyĈwietliè okno, zbliĔone do przedstawionego na
rysunku 5.3. Druga róĔnica wiñĔe siö z zachowaniem danych sesji pomiödzy odwoäaniami do
usäugi sieciowej (rysunek 5.4).
Rysunek 5.3. ćrodowisko ASP.NET przesyäa plik cookie zwiñzany z sesjñ dla danej strony
Wymiana z
ĥożonych struktur danych miýdzy klientem i serwerem
_ 115
Rysunek 5.4. Wykorzystanie sesji do przechowywania wartoĈci czasu (niezbödnej do obliczenia przerwy
miödzy pobraniem strony i wykonaniem dziaäania)
Wymiana z
ĥożonych struktur danych
mi
ýdzy klientem i serwerem
We wczeĈniejszych przykäadach analizowaliĈmy jedynie wymianö miödzy serwerem i klientem
ciñgów tekstowych i wartoĈci typów prostych (liczb, wartoĈci logicznych). Nic jednak nie stoi
na przeszkodzie, aby objñè tym mechanizmem równieĔ operacjö dostarczania bardziej zäoĔo-
nych struktur danych. Co prawda jözyk JavaScript nie moĔe konkurowaè z bogatszymi pod
wzglödem liczby typów jözykami platformy .NET, ale format JSON (opisany w rozdziale 3.)
zapewnia podstawowñ obsäugö tablic i obiektów.
ćrodowisko ASP.NET AJAX jest standardowo wyposaĔone w mechanizmy serializacji i dese-
rializacji danych JSON. JeĈli wiöc uwzglödnimy je w kodzie usäugi sieciowej zaprezentowanej
w przykäadach 5.1 i 5.2, bödziemy mogli udostöpniè nowñ metodö, która za jednym razem
zwróci dwie informacje — wynik dzielenia liczb oraz wartoĈè znacznika czasu serwerowego.
Aby wdroĔyè opisane rozwiñzanie, utworzymy w pliku MathService.asmx nowñ klasö, która
bödzie opisywaäa zwracany obiekt.
public class DivisionData
{
public float result;
public string calculationTime;
}
Powoäanie i zwrócenie obiektu bödzie naleĔaäo do metody przedstawionej poniĔej.
[WebMethod]
public DivisionData ExtendedDivideNumbers(int a, int b) {
if (b == 0) {
throw new DivideByZeroException( );
} else {
float res = (float)a / b;
string stamp = DateTime.Now.ToLongTimeString( );
DivisionData d = new DivisionData( );
d.result = res;
116
_
Rozdzia
ĥ 5. Usĥugi sieciowe
d.calculationTime = stamp;
return d;
}
}
Aby zwracany obiekt byä dostöpny dla kodu JavaScript, aplikacja ASP.NET AJAX musi
go przeksztaäciè (w procesie serializacji) w odpowiedni ciñg JSON. Za uĔycie wäaĈciwej
definicji obiektu odpowiada atrybut
GenerateScriptType
(zdefiniowany w przestrzeni
nazw
System.Web.Script.Services
[w której zostaäy zapisane równieĔ atrybuty
Script-
Service
i
ScriptMethod
]):
[System.Web.Script.Services.GenerateScriptType(typeof(DivisionData))]
Po stronie serwera nie trzeba wprowadzaè wiöcej zmian. Zaktualizowana treĈè pliku Math-
Service.asmx zostaäa zamieszczona w przykäadzie 5.5.
Przykäad 5.5. Zaktualizowany plik usäugi MathService
MathService.asmx
<%@ WebService Language="C#" Class="MathService" %>
using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
public class DivisionData
{
public float result;
public string calculationTime;
}
[WebService(Namespace = "http://hauser-wenz.de/AspNetAJAX/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService]
[System.Web.Script.Services.GenerateScriptType(typeof(DivisionData))]
public class MathService : System.Web.Services.WebService
{
[WebMethod]
public float DivideNumbers(int a, int b)
{
if (b == 0)
{
throw new DivideByZeroException();
}
else
{
return (float)a / b;
}
}
[WebMethod]
public DivisionData ExtendedDivideNumbers(int a, int b)
{
if (b == 0)
{
throw new DivideByZeroException();
}
else
Wymiana z
ĥożonych struktur danych miýdzy klientem i serwerem
_ 117
{
float res = (float)a / b;
string stamp = DateTime.Now.ToLongTimeString();
DivisionData d = new DivisionData();
d.result = res;
d.calculationTime = stamp;
return d;
}
}
}
Po stronie klienta deserializacja obiektu
DivisionData
jest realizowana w sposób automa-
tyczny. Obiekt bödñcy wynikiem wywoäania usäugi sieciowej ma te same wäaĈciwoĈci (
result
i
calculationTime
), jakie zostaäy zdefiniowane w obiekcie
DivisionData
. Instrukcje Java-
Script potrzebne do wywoäania zmodyfikowanej usäugi sieciowej zostaäy przedstawione
w przykäadzie 5.6.
Przykäad 5.6. Kod strony pobierajñcej z metody sieciowej bardziej rozbudowane obiekty
Complex.aspx
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>ASP.NET AJAX</title>
<script language="Javascript" type="text/javascript">
function callService(f) {
document.getElementById("c").innerHTML = "";
document.getElementById("output").innerHTML = "";
MathService.ExtendedDivideNumbers(
parseInt(f.elements["a"].value),
parseInt(f.elements["b"].value),
callComplete,
callError);
}
function callComplete(result) {
document.getElementById("c").innerHTML =
result.result +
" (obliczono o godzinie " +
result.calculationTime +
")";
}
function callError(result) {
document.getElementById("output").innerHTML =
"<b>" +
result.get_exceptionType() +
"</b>: " +
result.get_message() +
"<br />" +
result.get_stackTrace();
}
</script>
</head>
118
_
Rozdzia
ĥ 5. Usĥugi sieciowe
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>
<asp:ServiceReference Path="MathService.asmx" />
</Services>
</asp:ScriptManager>
<div>
<nobr>
<input type="text" id="a" name="a" size="2" />
:
<input type="text" id="b" name="b" size="2" />
=
<span id="c" style="width: 50px;"></span>
</nobr>
<br />
<input type="button" value="Podziel liczby" onclick="callService(this.form);" />
<br />
<div id="output" style="width: 600px; height: 300px;">
</div>
</div>
</form>
</body>
</html>
Sposób prezentacji wyniku dzielenia i czasu wygenerowania odpowiedzi zostaä pokazany na
rysunku 5.5.
Rysunek 5.5. WyĈwietlenie informacji dostarczonych przez serwer
Przechwytujñc ruch HTTP generowany przez skrypt, moĔemy sprawdziè, w jaki sposób zäo-
Ĕona struktura danych zostaäa przeksztaäcona w blok danych JSON (rysunek 5.6).
Opisane do tej pory funkcje Ĉrodowiska ASP.NET AJAX zwiñzane z usäugami sieciowymi
byäby niezwykle trudne do zaimplementowania za pomocñ samego jözyka JavaScript. Plat-
forma ASP.NET AJAX doskonale integruje siö z usäugami sieciowymi .NET i stanowi bardzo
uĔyteczny pomost miödzy technologiñ JavaScript (po stronie klienckiej) i technologiñ ASP.NET
(po stronie serwera).
Wykorzystanie us
ĥug sieciowych z poziomu skryptu JavaScript
_ 119
Rysunek 5.6. ZäoĔona struktura danych po serializacji do formatu JSON
Wykorzystanie us
ĥug sieciowych
z poziomu skryptu JavaScript
Zapewniane przez Ĉrodowisko ASP.NET AJAX mechanizmy odwoäaþ do usäug sieciowych sñ
niezwykle uĔyteczne, poniewaĔ automatycznie realizujñ wszystkie zwiñzane z tñ operacjñ
zadania. Zdarzajñ siö jednak sytuacje, w których nie moĔna ich zastosowaè. Przykäadem moĔe
byè koniecznoĈè odwoäania siö do usäugi sieciowej (w tej samej domenie), która nie zostaäa
napisana dla platformy .NET, lecz opiera siö na innych rozwiñzaniach serwerowych, takich
jak PHP lub Java. Innym powodem bywa równieĔ polityka firmy dotyczñca stosowania mo-
duäów zewnötrznych producentów lub brak akceptacji dla okreĈlonej umowy licencyjnej.
PoniewaĔ zakres tematyczny ksiñĔki wykracza poza samo korzystanie ze Ĉrodowiska
ASP.NET AJAX i obejmuje wszystkie zagadnienia zwiñzane z tworzeniem aplikacji Ajax na
platformie ASP.NET, omówione zostanñ tutaj takĔe zasady wywoäywania zdalnych usäug
sieciowych z poziomu skryptu JavaScript.
Zanim przystñpimy do szczegóäowego analizowania stosownych mechanizmów, warto sobie
przypomnieè, Ĕe model zabezpieczeþ jözyka JavaScript zabrania wykonywania skryptów po-
chodzñcych z róĔnych domen. Oznacza to, Ĕe nie moĔna odwoäaè siö do zdalnych witryn za
pomocñ instrukcji JavaScript (korzystajñcych z obiektu
XMLHttpRequest
).
120
_
Rozdzia
ĥ 5. Usĥugi sieciowe
Istniejñ dwie metody programowego wywoäywania usäug sieciowych w jözyku JavaScript.
Pierwsza z nich polega na zastosowaniu obiektu
XMLHttpRequest
. Natomiast w drugiej za-
käada siö przygotowanie wäasnego Ĕñdania HTTP SOAP i samodzielnñ interpretacjö danych
zwracanych przez serwer. Druga metoda jest doĈè skomplikowana i bardzo podatna na bäödy.
Znacznie lepszym rozwiñzaniem jest wykorzystanie mechanizmów wbudowanych w prze-
glñdarkñ oraz oficjalnych dodatków do przeglñdarki,
Niestety, dwie najpowszechniej stosowane aplikacje — Internet Explorer i Mozilla (czyli Firefox,
Epiphany, Camino itd.) — majñ zaimplementowane dwie zupeänie róĔne procedury wywo-
äywania usäug sieciowych. W rezultacie programista musi powielaè kod zapewniajñcy obsäugö
kaĔdej z przeglñdarek. W koþcowej czöĈci podrozdziaäu zostaäo jednak przedstawione roz-
wiñzanie, które pozwala na poäñczenie obydwu modeli, a tym samym na opracowanie skryptu
(bardziej lub mniej) niezaleĔnego od rodzaju oprogramowania klienckiego.
Us
ĥugi sieciowe w przeglédarkach Internet Explorer
Kilka lat temu firma Microsoft rozpoczöäa prace na kodem skryptowym, który umoĔliwiaäby
wywoäywanie usäug sieciowych z poziomu samej przeglñdarki. Zgodnie z zaäoĔeniami kod
taki powinien powoäywaè obiekt
XMLHttpRequest
, definiowaè niezbödne nagäówki HTTP dla
Ĕñdania SOAP, przygotowywaè treĈè samego Ĕñdania, nastöpnie oczekiwaè na odpowiedĒ
SOAP i przeksztaäcaè wynik do formatu wäaĈciwego do dalszego przetwarzania w instruk-
cjach JavaScript. Ponadto powinien umoĔliwiaè interpretowanie informacji generowanych
w jözyku opisu usäug sieciowych (WSDL — ang. Web Service Description Language) oraz gene-
rowanie lokalnego obiektu poĈredniczñcego.
Idea nie jest skomplikowana, ale implementacja tak. Ostateczna wersja mechanizmu (wersja
1.0.1.1120) skäada siö z niemal 2300 wierszy kodu. Niestety, w 2002 roku firma Microsoft
przerwaäa prace nad komponentem komunikacji z usäugami sieciowymi. Szkoda, gdyĔ do
dzisiaj jest on wykorzystywany i dziaäa poprawnie. Na szczöĈcie jest jeszcze dostöpny w ar-
chiwach MSDN pod adresem http://msdn.microsoft.com/archive/en-us/samples/internet/behaviors/
library/webservice/default.asp.
Aby z niego skorzystaè, trzeba pobraè plik webservice.htc i zapisaè w katalogu, w którym prze-
chowywane sñ skrypty przykäadów. Rozszerzenie .htc oznacza kontrolkö HTML (HTML control),
zwanñ teĔ funkcjñ (ang. behavior) przeglñdarki Internet Explorer. Do zaäadowania pliku säuĔy
niestandardowa instrukcja stylu CSS, obsäugiwana jedynie w aplikacjach Internet Explorer.
<div id="WebService" style="behavior:url(webservice.htc);"></div>
Nazwa podana jako wartoĈè atrybutu
id
moĔe byè wykorzystana w skrypcie w JavaScript
zarówno do odwoäania do samej kontrolki HTML, jak i do odwoäania do usäugi sieciowej
z niñ zwiñzanej.
Powiñzanie kontrolki z usäugñ wymaga zdefiniowania odsyäacza do opisu WSDL danej usäugi
sieciowej. Wykorzystuje siö do tego celu metodö
useService()
zapisanñ w pliku .htc. Konieczne
jest równieĔ okreĈlenie niepowtarzalnego identyfikatora, który umoĔliwi póĒniejsze odwoäy-
wanie siö do danej usäugi sieciowej.
WebService.useService("MathService.asmx?WSDL", "MathService");
Wykorzystanie us
ĥug sieciowych z poziomu skryptu JavaScript
_ 121
Po tych operacjach moĔna wywoäaè usäugö. KolejnoĈè parametrów przekazywanych do me-
tody
callService()
— odpowiadajñcej za wywoäanie usäugi sieciowej — róĔni siö od stoso-
wanej w obiektach poĈredniczñcych ASP.NET AJAX. Oto lista tych parametrów:
x
referencja do metody zwrotnej,
x
nazwa wywoäywanej metody sieciowej,
x
parametry przekazywane do usäugi sieciowej.
Rozwiñzanie to nie zapewnia obsäugi bäödów (w przeciwieþstwie do mechanizmów ASP.NET
AJAX, które dostarczajñ wyjñtki do skryptu klienckiego).
W przypadku odwoäania do usäugi
MathService
podzielenie dwóch liczb wymagaäoby wy-
konania nastöpujñcej instrukcji:
WebService.MathService.callService(
callComplete,
"DivideNumbers",
6, 7);
Funkcja zwrotna otrzymuje wówczas obiekt, którego atrybut
value
zawiera wynik zwrócony
przez usäugö sieciowñ.
function callComplete(result) {
document.getElementsById("c").innerHTML = result.value;
}
Peäny kod aplikacji zostaä zamieszczony w przykäadzie 5.7.
Przykäad 5.7. Wywoäanie usäugi sieciowej w przeglñdarce Internet Explorer
MathServiceInternetExplorer.htm
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>ASP.NET AJAX</title>
<script language="Javascript" type="text/javascript">
function callService(f) {
document.getElementById("c").innerHTML = "";
WebService.useService("MathService.asmx?WSDL", "MathService");
WebService.MathService.callService(
callComplete,
"DivideNumbers",
f.elements["a"].value, f.elements["b"].value);
}
function callComplete(result) {
document.getElementById("c").innerHTML = result.value;
}
</script>
</head>
<body>
<div id="WebService" style="behavior:url(webservice.htc);">
</div>
<form method="post" onsubmit="return false;">
<div>
<nobr>
122
_
Rozdzia
ĥ 5. Usĥugi sieciowe
<input type="text" id="a" name="a" size="2" />
:
<input type="text" id="b" name="b" size="2" />
=
<span id="c" style="width: 50px;"></span>
</nobr>
<br />
<input type="button" value="Podziel liczby" onclick="callService(this.form);" />
</div>
</form>
</body>
</html>
JeĈli kontrolka HTML usäugi sieciowej nie zostanie zdefiniowana na poczñtku sekcji
<body>
, przeglñdarka moĔe wygenerowaè niepokojñce komunikaty o bäödach,
wäñcznie z informacjñ o tym, Ĕe obiekt
WebService
nie zostaä zdefiniowany (mimo
prawidäowego dziaäania instrukcji
window.alert(WebService)
).
Us
ĥugi sieciowe w przeglédarkach Mozilla
Obsäuga usäug sieciowych zostaäa zaimplementowana równieĔ w wydawanych ostatnio wer-
sjach przeglñdarek Mozilla. Ma ona charakter wbudowanego rozszerzenia. Niestety, kompo-
nent odpowiedzialny za komunikacjö z usäugami sieciowymi najwyraĒniej nie zyskaä szcze-
gólnego zainteresowania u osób skupionych wokóä projektu Mozilla, choè trzeba przyznaè, Ĕe
poprawnie wykonuje swoje zadanie. W rezultacie nie towarzyszy mu Ĕadna dokumentacja,
a informacje na temat jego uĔycia sñ czösto sprzeczne. Rozwiñzanie prezentowane w dalszej
czöĈci punktu pozwala na realizacjö zadnia, ale wymaga dodania sporej iloĈci kodu.
Za komunikacjö z usäugami sieciowymi odpowiada klasa
SOAPCall
. PoniewaĔ opiera siö ona
na standardzie SOAP 1.1, programista musi zdefiniowaè nagäówek
SOAPAction
(dostöpny
w formie wäaĈciwoĈci klasy
SOAPClass
) oraz adres URL pliku usäugi sieciowej. Oto instrukcje
charakterystyczne dla omawianego przykäadu:
var soapcall = new SOAPCall( );
soapcall.actionURI = "http://hauser-wenz.de/AspNetAJAX/DivideNumbers";
soapcall.transportURI =
"http://localhost:1234/AJAXEnabledWebSite1/MathServiceDocEnc.asmx";
WartoĈè wäaĈciwoĈci
transportURI
musi bezwzglödnie odpowiadaè adresowi URL.
Trzeba wiöc pamiötaè o dostosowaniu ciñgu URI (szczególnie numeru portu, jeĈli do
uruchamiania aplikacji jest wykorzystywany serwer testowy Ĉrodowiska Visual Studio
lub Visual Web Developer) do ustawieþ lokalnego systemu.
Wszystkie parametry przekazywane do usäugi sñ zmiennymi typu
SOAPParameter
. W kon-
struktorze klasy parametru naleĔy wskazaè wartoĈè parametru, a nastöpnie jego nazwö.
var p1 = new SOAPParameter(6, "a");
var p2 = new SOAPParameter(7, "b");
Bardzo waĔne jest wykonanie nastöpnej czynnoĈci. Jej ewentualne pominiöcie spowoduje, Ĕe
Ĕñdanie SOAP zostanie przesäane do serwera (odebrana zostanie równieĔ wartoĈè wyniku),
ale nie bödñ do niego doäñczone parametry. W przypadku dzielenia liczb oznaczaäoby to nie-
zamierzone wygenerowanie wyjñtku dzielenia przez zero (ang. divie by zero).
Wykorzystanie us
ĥug sieciowych z poziomu skryptu JavaScript
_ 123
Zadanie polega na osobistym ustaleniu wäaĈciwego kodowania dla wartoĈci liczbowych.
W tym celu naleĔy zaäadowaè odpowiedniñ przestrzeþ nazw, która obejmuje typ SOAP
integer
. Nastöpnie trzeba okreĈliè wartoĈè wäaĈciwoĈci
schemaType
wszystkich parametrów,
które powinny byè przekazane do usäugi sieciowej, przypisujñc im wygenerowane typy danych.
Kod realizujñcy opisane zadania zostaä zamieszczony poniĔej.
var senc = new SOAPEncoding( );
assenc = senc.getAssociatedEncoding(
"http://schemas.xmlsoap.org/soap/encoding/",
false);
var scoll = assenc.schemaCollection;
var stype = scoll.getType(
"integer",
"http://www.w3.org/2001/XMLSchema");
p1.schemaType = stype;
p2.schemaType = stype;
Kolejna czynnoĈè polega na przygotowaniu wywoäania usäugi sieciowej. SäuĔy do tego
metoda
encode()
, wymagajñca przekazania do niej kilku parametrów, zgodnie z poniĔszym
przykäadem.
soapcall.encode(
0, //warto
Łð domyŁlna dla protokoĪu SOAP 1.1
"DivideNumbers", //nazwa metody sieciowej
"http://hauser-wenz.de/AspNetAJAX/", //przestrze
Ĭ nazw
0, //liczba dodatkowych nag
Īówków
new Array( ), //dodatkowe nag
Īówki
2, //liczba parametrów
new Array(p1, p2) //parametry
);
W koþcu moĔna wywoäaè usäugö sieciowñ (w sposób asynchroniczny), wykonujñc metodö
asyncInvoke()
. Parametrem metody jest referencja funkcji zwrotnej.
soapcall.asyncInvoke(callComplete);
Funkcja zwrotna otrzymuje trzy parametry:
x
dokument XML bödñcy wynikiem wywoäania usäugi sieciowej,
x
obiekt
SOAPCall
(gdyby konieczne byäo przeanalizowanie nagäówków SOAP),
x
kod statusowy HTTP dla wywoäania.
Ostatni etap procedury sprowadza siö do wyodröbnienia danych wynikowych z treĈci do-
starczonego dokumentu XML. Przyjrzyjmy siö zatem odpowiedzi XML zwracanej po odwo-
äaniu siö do usäugi
MathService
— dane te moĔna pozyskaè za pomocñ programu Fiddler
(dla systemu Windows) (http://www.fiddlertool.com/fiddler) lub rozszerzenia przeglñdarki
Mozilla Live HTTP Headers (http://livehttpheaders.mozdev.org/):
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap
.org/soap/envelope/">
<soap:Body>
<DivideNumbersResponse xmlns="http://hauser-wenz.de/AspNetAJAX/">
<DivideNumbersResult>0.857142866</DivideNumbersResult>
</DivideNumbersResponse>
</soap:Body>
</soap:Envelope>
124
_
Rozdzia
ĥ 5. Usĥugi sieciowe
Wiöcej informacji na temat sposobu analizowania Ĕñdaþ HTTP (wysyäanych przez
aplikacje Ajax) oraz debugowania aplikacji Ajax znajduje siö w dodatku A.
Analizujñc dane w formacie XML, nietrudno zauwaĔyè, Ĕe wyodröbnienie wartoĈci
0.857142866
wymaga przeprowadzenia nastöpujñcych operacji:
x
odwoäania siö do wäaĈciwoĈci
body
, która zapewni dostöp do elementu
<soap:Body>
;
x
odwoäania siö do wäaĈciwoĈci
firstChild
, która zapewni dostöp do elementu
<Divide-
NumbersResponse>
;
x
ponownego wykorzystania wäaĈciwoĈci
firstChild
do pobrania elementu
<DivideNum-
bersResult>
;
x
wykorzystania po raz trzeci wäaĈciwoĈci
firstChild
, aby uzyskaè dostöp do wözäa tek-
stowego w elemencie
<DivideNumbersResult>
;
x
wykorzystania wäaĈciwoĈci
data
do pobrania ciñgu zapisanego w wöĒle tekstowym.
TreĈè skryptu JavaScript niezbödnego do wyodröbnienia wyniku z dokumentu dostarczonego
przez usäugö sieciowñ zostaäa zamieszczona poniĔej.
function callComplete(result, soapcall, status) {
document.getElementById("c").innerHTML =
result.body.firstChild.firstChild.firstChild.data;
}
ãñczñc wszystkie opisane fragmenty skryptów, uzyskujemy kod przedstawiony w przykäa-
dzie 5.8. Trzeba jednak pamiötaè, Ĕe aplikacja bödzie dziaäaäa zgodnie z oczekiwaniami tylko
wtedy, gdy system bödzie miaä dostöp do internetu — przeglñdarki Mozilla muszñ mieè do-
stöp do informacji na temat schematów SOAP.
Przykäad 5.8. Wywoäanie usäugi sieciowej w przeglñdarce Mozilla
MathServiceMozilla.htm
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>ASP.NET AJAX</title>
<script language="Javascript" type="text/javascript">
function callService(f) {
document.getElementById("c").innerHTML = "";
var soapcall = new SOAPCall( );
soapcall.actionURI = "http://hauser-wenz.de/AspNetAJAX/DivideNumbers";
soapcall.transportURI="http://localhost:1041/AJAXEnabledWebSite1/MathService.asmx";
var p1 = new SOAPParameter(parseInt(f.elements["a"].value), "a");
var p2 = new SOAPParameter(parseInt(f.elements["b"].value), "b");
var senc = new SOAPEncoding( );
assenc = senc.getAssociatedEncoding(
"http://schemas.xmlsoap.org/soap/encoding/",
false);
var scoll = assenc.schemaCollection;
var stype = scoll.getType(
"integer",
Wykorzystanie us
ĥug sieciowych z poziomu skryptu JavaScript
_ 125
"http://www.w3.org/2001/XMLSchema");
p1.schemaType = stype;
p2.schemaType = stype;
soapcall.encode(
0, //warto
Łð domyŁlna dla protokoĪu SOAP 1.1
"DivideNumbers", //nazwa metody sieciowej
"http://hauser-wenz.de/AspNetAJAX/", //przestrze
Ĭ nazw
0, //liczba dodatkowych nag
Īówków
new Array( ), //dodatkowe nag
Īówki
2, //liczba parametrów
new Array(p1, p2) //parametry
);
soapcall.asyncInvoke(callComplete);
}
function callComplete(result, soapcall, status) {
document.getElementById("c").innerHTML =
result.body.firstChild.firstChild.firstChild.data;
}
</script>
</head>
<body>
<form method="post" onsubmit="return false;">
<div>
<nobr>
<input type="text" id="a" name="a" size="2" />
:
<input type="text" id="b" name="b" size="2" />
=
<span id="c" style="width: 50px;"></span>
</nobr>
<br />
<input type="button" value="Podziel liczby" onclick="callService(this.form);" />
</div>
</form>
</body>
</html>
Us
ĥugi sieciowe w obydwu przeglédarkach
Przeglñd technik korzystania z usäug sieciowych z poziomu skryptu JavaScript w przeglñ-
darkach Internet Explorer i Mozilla zakoþczymy rozwiñzaniem, które äñczy obydwie metody
na jednej stronie. W tym celu musimy przede wszystkim ustaliè, w jaki sposób rozpoznawa-
ny bödzie rodzaj przeglñdarki. Zgodnie z informacjami zamieszczonymi w rozdziale 2, naj-
korzystniejsze wydaje siö sprawdzenie zestawu funkcji przeglñdarki, a nie ich typu. Zasada
ta zostaäa wykorzystana podczas opracowywania kodu z przykäadu 5.9 (jej opis znajduje siö
w rozdziale 2, w czöĈci dotyczñcej powoäywania obiektu
XMLHttpRequest
). Rozwiñzanie po-
lega na utworzeniu obiektu wäaĈciwego dla jednej przeglñdarki. JeĈli to siö uda, dalsza czöĈè
kodu zostanie wykonana zgodnie z zaäoĔeniami. W przeciwnym przypadku wykorzystane
zostanñ instrukcje wäaĈciwe dla drugiej przeglñdarki. Poszczególne wywoäania zostaäy za-
pisane w dwóch zagnieĔdĔonych konstrukcjach
try
…
catch
.
126
_
Rozdzia
ĥ 5. Usĥugi sieciowe
Dost
ýp do zdalnych usĥug sieciowych
w przegl
édarkach Mozilla
Model zabezpieczeþ przeglñdarki Mozilla umoĔliwia odwoäania do zdalnych usäug sieciowych.
Wykonanie skryptu wiñĔe siö jednak z wyĈwietleniem okna dialogowego, w którym uĔyt-
kownik musi zezwoliè na takñ operacjö (rysunek 5.7). Wymagane jest w tym przypadku
uprawnienie
UniversalBrowserRead
, oznaczajñce, Ĕe przeglñdarka moĔe pobieraè dane
z dowolnego serwera (wäñczajñc w to zarówno serwery zdalne, jak i lokalny system plików).
netscape.security.PrivilegeManager.enablePrivilege(
"UniversalBrowserRead");
Rysunek 5.7. ēñdanie zwiökszenia poziomu uprawnieþ w przeglñdarce Firefox
DomyĈlna konfiguracja przeglñdarek Mozilla i Firefox (a takĔe kilku innych) powoduje na-
danie wspomnianego uprawnienia tylko w dostöpie do plików lokalnych (z definicjñ protokoäu
file://
). Mechanizm ten znajduje wiöc zastosowanie gäównie w aplikacjach intranetowych.
Wyglñd okna dialogowego z Ĕñdaniem zwiökszenia poziomu uprawnieþ zostaä pokazany na
rysunku 5.7.
Przykäad 5.9. Wywoäanie usäugi sieciowej w dowolnej z przeglñdarek Internet Explorer i Mozilla
MathService.htm
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>ASP.NET AJAX</title>
<script language="Javascript" type="text/javascript">
function callService(f) {
document.getElementById("c").innerHTML = "";
try {
WebService.useService("MathService.asmx?WSDL", "MathService");
WebService.MathService.callService(
callComplete,
"DivideNumbers",
parseInt(f.elements["a"].value), parseInt(f.elements["b"].value));
} catch (e) {
try {
var soapcall = new SOAPCall( );
soapcall.actionURI = "http://hauser-wenz.de/AspNetAJAX/DivideNumbers";
Wykorzystanie us
ĥug sieciowych z poziomu skryptu JavaScript
_ 127
soapcall.transportURI = "http://localhost:1041/AJAXEnabledWebSite1/
MathService.asmx";
var p1 = new SOAPParameter(parseInt(f.elements["a"].value), "a");
var p2 = new SOAPParameter(parseInt(f.elements["b"].value), "b");
var senc = new SOAPEncoding( );
assenc = senc.getAssociatedEncoding(
"http://schemas.xmlsoap.org/soap/encoding/",
false);
var scoll = assenc.schemaCollection;
var stype = scoll.getType(
"integer",
"http://www.w3.org/2001/XMLSchema");
p1.schemaType = stype;
p2.schemaType = stype;
soapcall.encode(
0, //warto
Łð domyŁlna dla protokoĪu SOAP 1.1
"DivideNumbers", //nazwa metody sieciowej
"http://hauser-wenz.de/AspNetAJAX/", //przestrze
Ĭ nazw
0, //liczba dodatkowych nag
Īówków
new Array( ), //dodatkowe nag
Īówki
2, //liczba parametrów
new Array(p1, p2) //parametry
);
soapcall.asyncInvoke(callComplete);
} catch (e) {
window.alert("Twoja przegl
ìdarka nie jest obsĨugiwana.");
}
}
}
function callComplete(result, soapcall, status) {
if (result.value != null) {
document.getElementById("c").innerHTML = result.value;
} else {
document.getElementById("c").innerHTML =
result.body.firstChild.firstChild.firstChild.data;
}
}
</script>
</head>
<body>
<div id="WebService" style="behavior: url(webservice.htc);">
</div>
<form method="post" onsubmit="return false;">
<div>
<nobr>
<input type="text" id="a" name="a" size="2" />
:
<input type="text" id="b" name="b" size="2" />
= <span id="c" style="width: 50px;" ></span>
</nobr>
<br />
<input type="button" value="Podziel liczby" onclick="callService(this.form);" />
</div>
</form>
</body>
</html>