Sunteți pe pagina 1din 135

Site-uri web dinamice ı̂n ASP.

NET

Ph.D. Lucian Sasu

5 octombrie 2009
2
Cuprins

1 Aplicaţii Web 7
1.1 Servere şi aplicaţii Web . . . . . . . . . . . . . . . . . . . . . . 7
1.2 Modele clasice de realizare a aplicaţiilor Web . . . . . . . . . . 10
1.2.1 Comunicarea cu serverul . . . . . . . . . . . . . . . . . 11
1.2.2 Limbajul HTML . . . . . . . . . . . . . . . . . . . . . 14
1.2.3 Limbajul XHTML . . . . . . . . . . . . . . . . . . . . 15
1.3 Formulare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.3.1 Text . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.3.2 Password . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.3.3 Checkbox . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.3.4 Butoane radio . . . . . . . . . . . . . . . . . . . . . . . 21
1.3.5 Butonul de Submit . . . . . . . . . . . . . . . . . . . . 21
1.3.6 Butonul de reset . . . . . . . . . . . . . . . . . . . . . 22
1.3.7 File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
1.3.8 Câmpuri ascunse . . . . . . . . . . . . . . . . . . . . . 22
1.3.9 Image . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
1.3.10 Butoane . . . . . . . . . . . . . . . . . . . . . . . . . . 24
1.3.11 Textarea . . . . . . . . . . . . . . . . . . . . . . . . . . 24
1.3.12 Select . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

2 ASP.NET 27
2.1 Generalităţi . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.1.1 Ce e greşit la ASP-ul clasic? . . . . . . . . . . . . . . . 27
2.1.2 Ce aduce ASP.NET? . . . . . . . . . . . . . . . . . . . 29
2.2 Despre ASP.NET 3.5 . . . . . . . . . . . . . . . . . . . . . . . 30
2.3 Tipuri de fişiere ı̂n ASP.NET . . . . . . . . . . . . . . . . . . 30
2.4 Modelul de cod . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.5 Controale Web şi controale HTML . . . . . . . . . . . . . . . 36
2.6 Forme Web . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.6.1 Postback . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.6.2 Menţinerea stării prin Viewstate . . . . . . . . . . . . . 39

3
4 CUPRINS

2.6.3 Utilitatea conceptului de controale . . . . . . . . . . . 41


2.6.4 Ciclul de viaţă al unei pagini (variantă sumară) . . . . 43

3 Forme Web, gestiunea stării 47


3.1 Forme Web (continuare) . . . . . . . . . . . . . . . . . . . . . 47
3.1.1 Colecţia de controale din pagină . . . . . . . . . . . . . 47
3.1.2 Request . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3.1.3 Response . . . . . . . . . . . . . . . . . . . . . . . . . 48
3.1.4 Server . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
3.2 Tipuri de controale server . . . . . . . . . . . . . . . . . . . . 49
3.2.1 Controale server HTML . . . . . . . . . . . . . . . . . 50
3.2.2 Controale Web server . . . . . . . . . . . . . . . . . . . 53
3.3 Gestiunea stării ı̂n ASP.NET . . . . . . . . . . . . . . . . . . . 53
3.3.1 ViewState . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.3.2 Query String . . . . . . . . . . . . . . . . . . . . . . . 57
3.3.3 Cross page posting . . . . . . . . . . . . . . . . . . . . 58
3.3.4 Cookies . . . . . . . . . . . . . . . . . . . . . . . . . . 58
3.3.5 Sesiunea . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.3.6 Application . . . . . . . . . . . . . . . . . . . . . . . . 59

4 Validatoare, manipulatoare, controale 61


4.1 Controale de validare a intrării . . . . . . . . . . . . . . . . . . 61
4.1.1 Paşii de validare . . . . . . . . . . . . . . . . . . . . . . 62
4.1.2 Clasa BaseValidator . . . . . . . . . . . . . . . . . . 62
4.1.3 Validatorul RequiredFieldValidator . . . . . . . . . 63
4.1.4 Validatorul RangeValidator . . . . . . . . . . . . . . . 64
4.1.5 Validatorul CompareValidator . . . . . . . . . . . . . 65
4.1.6 Validatorul RegularExpressionValidator . . . . . . . 66
4.1.7 Controlul CustomValidator . . . . . . . . . . . . . . . 66
4.1.8 Controlul ValidationSummary . . . . . . . . . . . . . . 67
4.1.9 Grupuri de validare . . . . . . . . . . . . . . . . . . . . 67
4.2 Manipulatoare HTTP . . . . . . . . . . . . . . . . . . . . . . . 68
4.3 Controale utilizator . . . . . . . . . . . . . . . . . . . . . . . . 71

5 Teme, pagini master, navigare 73


5.1 Cascading Style Sheets . . . . . . . . . . . . . . . . . . . . . . 73
5.2 Teme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
5.3 Pagini master . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
5.4 Navigare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
CUPRINS 5

6 Legarea la date 83
6.1 Legarea la date simple . . . . . . . . . . . . . . . . . . . . . . 84
6.2 Legarea la date multiple . . . . . . . . . . . . . . . . . . . . . 86
6.3 Controale de date “bogate” . . . . . . . . . . . . . . . . . . . . 88
6.4 Controale de surse de date . . . . . . . . . . . . . . . . . . . . 89
6.5 Ciclul de viaţă al unei pagini Web . . . . . . . . . . . . . . . . 89
6.6 Sursa de date SqlDataSource . . . . . . . . . . . . . . . . . . . 91
6.6.1 Selectarea de ı̂nregistrări . . . . . . . . . . . . . . . . . 91
6.6.2 Dezavantaje ale SqlDataSource . . . . . . . . . . . . . 92
6.7 Sursa de date ObjectDataSource . . . . . . . . . . . . . . . . . 92

7 Controale de lucru cu datele 95


7.1 Controlul GridView . . . . . . . . . . . . . . . . . . . . . . . . 95
7.1.1 Formatare . . . . . . . . . . . . . . . . . . . . . . . . . 95
7.1.2 Stiluri . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
7.1.3 Selectarea de linie din GridView . . . . . . . . . . . . . 96
7.1.4 Sortare . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
7.1.5 Paginare . . . . . . . . . . . . . . . . . . . . . . . . . . 97
7.1.6 Template-uri . . . . . . . . . . . . . . . . . . . . . . . 97
7.2 Controalele FormView, DetailsView . . . . . . . . . . . . . . . 97

8 JavaScript şi Ajax 99


8.1 Elemente de bază JavaScript . . . . . . . . . . . . . . . . . . . 99
8.2 Blocuri de script . . . . . . . . . . . . . . . . . . . . . . . . . 101
8.3 Manipularea elementelor HTML . . . . . . . . . . . . . . . . . 103
8.4 Utilizare de JavaScript pentru ı̂ncărcare asincronă a paginilor . 103
8.5 Includerea blocurilor JavaScript din code-behind . . . . . . . . 106
8.6 Atacuri de injectare de script . . . . . . . . . . . . . . . . . . 107
8.7 JavaScript pentru controale utilizator . . . . . . . . . . . . . . 109
8.8 Ajax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
8.8.1 Obiectul XMLHttpRequest . . . . . . . . . . . . . . . . 115
8.8.2 Trimiterea cererii . . . . . . . . . . . . . . . . . . . . . 115
8.8.3 Procesarea răspunsului . . . . . . . . . . . . . . . . . . 116
8.9 Un exemplu Ajax . . . . . . . . . . . . . . . . . . . . . . . . . 116

9 ASP.NET membership 121


9.1 Configurarea autentificării prin forme . . . . . . . . . . . . . . 123
9.2 Crearea sursei de date . . . . . . . . . . . . . . . . . . . . . . 124
9.3 Configurarea stringului de conexiune şi a furnizorului de “membership” 124
9.4 Crearea de utilizatori . . . . . . . . . . . . . . . . . . . . . . . 129
9.5 Crearea unei pagini de login . . . . . . . . . . . . . . . . . . . 130
6 CUPRINS

9.5.1 Controlul Login . . . . . . . . . . . . . . . . . . . . . . 130


9.5.2 Controlul LoginStatus . . . . . . . . . . . . . . . . . . 131
9.5.3 Controlul LoginView . . . . . . . . . . . . . . . . . . . 131
9.5.4 Controlul PasswordRecovery . . . . . . . . . . . . . . 131
9.5.5 Controlul ChangePassword . . . . . . . . . . . . . . . . 132
9.6 Gestiunea utilizatorilor prin API-ul de “membership” . . . . . 132
9.6.1 Aducerea listei de utilizatori . . . . . . . . . . . . . . . 132
9.6.2 Obţinerea detaliilor despre un utilizator . . . . . . . . 133
9.6.3 Modificarea datelor unui utilizator . . . . . . . . . . . 133
9.6.4 Crearea şi ştergerea unui utilizator . . . . . . . . . . . 133
9.6.5 Validarea unui utilizator . . . . . . . . . . . . . . . . . 134
Capitolul 1

Aplicaţii Web

1.1 Servere şi aplicaţii Web


În ingineria softului, o aplicaţie Web1 este un program care este stocat
pe un server Web şi accesat prin intermediul unui browser folosind reţeaua.
La un capăt al acestei comunicaţii se află un server (program dedicat)
care acceptă interogări, formulate prin protocolul HTTP; serverul poate să
servească pagini web statice (ficşiere scrise ı̂n limbaj HTML) sau resurse
statice (imagini, fişiere PDF etc). Exemple de servere folosite ı̂n prezent:
• Apache - cel mai larg răspândit, la ora actuală; poate rula pe o mul-
titudine de sisteme de operare; conform Netcraft, ı̂n septembrie 2009,
54.48% din site-uri rulau Apache
• IIS = Internet Information Services, creat de către Microsoft pentru a
fi folosit pe sistemele de operare Windows; disponibil pe Windows 2000
(IIS 5), Windows XP Professional (IIS 5.1), Windows Server 2003 (IIS
6), Windows 2008 (IIS 7.0 şi 7.5), Windows Vista anumite versiuni
(IIS 7.0), Windows 7 anumite versiuni (IIS 7.5). IIS este folosit ı̂n
dezvoltarea aplicaţiilor ASP.NET. Conform Netcraft, ı̂n luna precizată
mai sus 19.91% din site-uri rulau o variantă de IIS.
Varianta cea mai simplă este aceea ı̂n care se servesc doar resurse statice
- cum ar fi un site de prezentare a unei instituţii, prezentări de rezultate
sau statistici etc, deci situaţii ı̂n care conţinutul se schimbă relativ rar. Pa-
ginile Web sunt scrise ı̂ntr–un limbaj numit HTML. Figura 1.1 ilustrează
arhitectura unui web server şi legătura cu browserul.
Există ı̂nsă situaţii ı̂n care conţinutul se modifică foarte des – de exemplu
pentru pagina unui magazin de cărţi, ı̂n care apar permanent noi publicaţii,
1
În original: Web application sau webapp.

7
8 CAPITOLUL 1. APLICAŢII WEB

Figura 1.1: Arhitectura unui server web simplu

altele se epuizează, se introduc noi categorii de literatură, se stabilesc şi se


modifică preţuri, se introduc promoţii etc. A crea pagini web statice nu este
o soluţie, deoarece atât de multe modificări nu pot fi operate ı̂n timp real cu
costuri reduse. Ca atare, serverul de Web poate de asemenea să găzduiască
o aplicaţie scrisă ı̂ntr-un limbaj (alegeri populare: PHP, Java, ASP.NET cu
C#/VB.NET etc), aplicaţie care produce pagini Web dinamice; ea poate să
preia date dintr-o sursă de date - de regulă, o bază de date relaţională - şi să
producă pagini HTML actualizate. Datele actualizate vor fi afişate după ce s-
a făcut modificarea, la o nouă cerere a paginii respective. Întrucât conţinutul
paginilor se generează dinamic, acestea se mai numesc “pagini dinamice”.
Dinamicitatea se referă la conţinutul care se actualizează automat, pe baza
unor calcule sau preluări din surse de date.
Exemple clasice de aplicaţii Web sunt:

• webmail – permite citirea poştei electronice folosind o interfaţă web;


exemple: Yahoo! Mail, Google Mail;

• magazine electronice, site-uri de licitaţii – permit prezentarea de pro-


duse; utilizatorul poate să cumpere, să ı̂şi alcătuiască un coş de cumpărături
etc; exemplu: Amazon, eBay;

• forumuri – permit membrilor unei comunităţi să comunice; mesajele


sunt vizibile de către toţi membrii comunităţii şi pot să primească
răspuns, de asemenea tot pe forum; exemplu: Comunitatea SQL Server,
stackoverflow;

• motoare de căutare: Google, Clusty;


1.1. SERVERE ŞI APLICAŢII WEB 9

• site-uri ce oferă informaţii sau predicţii: mersul trenurilor, predicţii


despre evoluţia preţului la bilete de avion.

La celălalt capăt se află un browser (fie el Internet Explorer, Mozilla


Firefox, Safari, Google Chrome sau Opera - cele mai utilizate la ora actuală).
Tot ceea ce trebuie să facă utilizatorul este să indice care este adresa de Web
la care se află pagina de interes.
Acest tip de comunicare este numit client–server: clientul este browse-
rul, serverul este cel care răspunde prin furnizarea unei pagini web (dacă se
poate). Browserul este de fapt un aşa numit thin client (client subţire); el nu
necesită instalare locală a unor componente (cu excepţia browserului, utiliza-
bil pentru toatre site-urile), nici drepturi de administrator pe maşina pe care
lucrează2 ; tot ceea ce trebuie să facă este să formuleze cereri către server, pe
baza acţiunilor utilizatorului uman şi să primească răspunsul de la server,
pentru a-l ı̂nfăţişa sub forma unei pagini. Simplitatea modului de operare
precum şi lipsa unor manevre complicate de instalare de către utilizator fac
aceast tip de aplicaţii foarte populare. Se mai consideră aici şi omniprezenţa
browserelor – un aspect care a contribuit decisiv la acceptarea pe scară largă
a aplicaţiilor web.
Tot ca un avantaj al acestor aplicaţii web ar fi faptul că dacă se doreşte
schimbarea unei părţi din aplicaţii, această modificare se face ı̂ntr-un singur
loc — pe server. Clientul (browserul) este insensibil la această manevră şi nu
trebuie actualizat ı̂n niciun fel. Este o uriaşă diferenţă faţă de tehnologia thick
client (client gras) prin care utilizatorul primea un kit de instalare pentru
calculatorul lui, iar aplicaţia nou instalată se conecta la sever; modificarea
aplicaţiei putea să ı̂nsemne dezinstalarea versiunii vechi şi instalarea celei
noi, cu potenţiale pericole: versiuni incorecte de fişiere DLL (aşa numitul
DLL hell), dificultatea distribuirii şi a mentenanţei.
În sfârşit, trebuie spus că nu contează pe ce fel de platformă sau sistem de
operare rulează browserul, deoarece paginile Web reprezintă doar text care
sunt ı̂nţelese de către orice sistem. În paranteză fie spus, există totuşi mici
diferenţe ı̂ntre modul ı̂n care browserele desenează pagina pe ecran, datorită
faptului că HTML reprezintă nişte specificaţii despre cum ar trebui să fie
desenat un anumit cod HTML, dar cei care creează browsere nu iau asta ca
pe o obligaţie.
Ca un neajuns al acestui tip de aplicaţii: dacă reţeaua devine nedispo-
nibilă, aplicaţia web nu poate fi accesată. Rezolvarea acestei situaţii se face
printr-ul al treilea tip de aplicaţii (pe lângă cele thin şi thick): smart applica-
2
Cu excepţia cazurilor ı̂n care este nevoie de instalarea unor plugin-uri pentru rularea
continutului pe client, cum ar fi Silverlight sau Adobe Flash. După instalarea acestora,
drepturile de administrator nu mai sunt necesare.
10 CAPITOLUL 1. APLICAŢII WEB

tions. De exemplu, un client de email de tipul Outlook sau Mozilla Thunderbird


care poate să lucreze şi deconectat de la reţea (citire de emailuri, ştergere,
pregătire de emailuri pentru a fi trimise, cătare etc). Subiectul nu este tratat
ı̂n acest curs.

1.2 Modele clasice de realizare a aplicaţiilor Web


Modelul de realizare a aplicaţiior Web a cunoscut o intreagă evoluţie a
tehnologiilor de programare la nivel de server. Putem enumera astfel:

1. soluţia CGI (Common Gateway Interface) reprezintă o serie de script-


uri executate pe serverul web. Acestea pot fi scrise ı̂n orice limbaj
de programare (interpretat sau compilat) cu respectarea următoarelor
restricţii: programul scrie datele la ieşirea standard şi generează an-
tete care permit browserului să interpreteze corect ieşirea scriptului,
conform specificaţiilor HTTP. Se pot folosi limbaje precum bash, Perl,
C/C++, Delphi. Neajunsul CGI-urilor ı̂l reprezintă faptul creării unui
nou proces pe serverul Web pentru fiecare cerere, ceea ce la un număr
mare de cereri este prohibitiv.

2. soluţia ISAPI (Internet Server API) reprezintă o alternativa CGI pe


platformă Windows. Dezvoltatorii Win32 pot scrie un program care să
comunice direct cu aceasta interfaţa pentru a face orice lucru posibil cu
CGI, pot folosi ISAPI pentru a obţine date din formulare şi pentru a
trimite conţinut HTML la client. Codul la nivel de server poate fi scris
ı̂n oricare limbaj cu suport pentru DLL-uri Windows, precum C/C++,
Visual Basic, rezultatul compilării fiind un fişier .dll. Faţa de CGI,
ISAPI rulează ı̂n acelaşi spaţiu de adrese cu serverul HTTP, are acces
la toate resursele serverului HTTP, pot fi incluse mai multe task-uri
ı̂ntr-un .dll şi nu creează procese adiţionale pentru rezolvarea cererilor
clienţilor Web.

3. soluţia PHP (1994) sau ASP (1996) marchează un salt ı̂n dezvoltarea
aplicaţiilor Web. Deşi diferă din punct de vedere al sintaxei, ambele
limbaje sunt interpretate, codul lor fiind stocat ı̂n fişiere externe cu
extensia .php/.asp. De fapt, ASP nu ofera un limbaj nou, ci se ba-
zeaza pe limbajele VBScript şi JScript. Un fişier PHP/ASP poate fi
combinat cu date de tip text, marcatori HTML şi comenzi script, ı̂n
momentul execuţiei, ı̂n urma cererii unui client Web, fişierul este pro-
cesat, fiecare script din cadrul lui este interpretat şi rezultatul execuţiei
este ı̂ntrodus ı̂napoi ı̂n fişierul static HTML ı̂nainte ca rezultatul să fie
1.2. MODELE CLASICE DE REALIZARE A APLICAŢIILOR WEB 11

trimis către browser. Mai mult, ı̂n sprijinul programatorului, limbajele


pun la dispoziţia acestuia o serie de metode şi obiecte care uşurează lu-
crul cu cookie-uri, cu bazele de date, care preiau elegant intrările unui
formular HTML şi le procesează pe server, care preiau informaţii des-
pre utilizator (clientul Web), care trimit informaţii la utilizator, care
stochează informaţii despre sesiunea unui utilizator, care partajează
informaţii ı̂ntre utilizatorii unei aplicaţii etc.

4. JSP (Java Server Pages) face parte din familia Java şi reprezintă o
tehnologie care permite crearea de aplicaţii Web independente de plat-
formă. JSP separă interfaţa utilizator de conţinutul generat dinamic
permiţând schimbarea ı̂ntregului şablon al site-ului WEB fără a al-
tera informaţiile afişate. Tehnologia utilizează marcatori XML şi scrip-
turi scrise ı̂n limbajul de programare Java pentru a incapsula logica
aplicaţiei care generează conţinutul paginilor WEB. JSP-urile sunt o
extensie a tehnologiei Java Servlet. Servlet-ele sunt independente de
platformă 100% şi reprezintă module la nivel de server care se integrează
ı̂n cadrul unei aplicaţii Web şi care pot fi utilizate pentru a extinde
capabilităţile unui server WEB. Tehnologia JSP şi servlet-urile oferă
o alternativă pentru crearea aplicaţiilor WEB faţă de alte limbaje de
scripting/programare a aplicaţiilor WEB, oferind independenţa de plat-
formă, performanţă, separarea logicii aplicaţiei de partea de interfaţă
utilizator, administrare uşoară şi extensibilitate.

Punctele de mai sus au ı̂n comun faptul că serverul Web prezintă o
interfaţă de comunicare cu procese externe. Separarea software-ul ı̂n diferite
procese oferă avantajul de modularizare şi uşurează procesul de integrare,
ı̂n acelaşi timp extinzând funcţiile web server-ului. Aceste procese externe
realizează componenta logică a activităţii. Configuraţia descrisă este redată
ı̂n figura 1.2.
Figura 1.3 arată modul ı̂n care Serverul IIS utilizează ASP.NET pentru a
servi pagini web dinamice; mai departe, codul ASP.NET este responsabil de
a lua legătura cu surse de date pentru a furniza conţinutul actual al paginilor
dinamice.

1.2.1 Comunicarea cu serverul


Paginile web sunt de obicei transmise şi primite prin intermediul Inter-
netului via HTTP3 (nesecurizat) sau HTTPS4 (securizat). Dezvoltarea pro-
3
Hypertext Transfer Protocol.
4
Secure HTTP.
12 CAPITOLUL 1. APLICAŢII WEB

Figura 1.2: Server Web cu interfaţă spre procese externe.

Figura 1.3: Server IIS folosind ASP.NET pentru generarea de pagini Web
dinamice
1.2. MODELE CLASICE DE REALIZARE A APLICAŢIILOR WEB 13

tocolului HTTP a fost supervizată de către World Wide Web Consortium,


versiunea actuală la care s-a ajuns este HTTP 1.1.
HTTP este un standard de comunicare ce permite vehicularea de cereri
şi răspunsuri ı̂ntre un client şi un server. Clientul - de regulă un browser, dar
poate să fie şi un robot de descărcare a paginilor sau o aplicaţie utilizator
- face o cerere către un sever, folosind o adresă web. De regulă, această
cerere HTTP (protocol la nivelul aplicaţie din stiva de protocoale TCP/IP)
se foloseşte de o conexiune TCP; portul spre care se face cererea este 80,
implicit folosit pentru HTTP. Când primeşte cererea, serverul răspunde cu:

• un cod de stare(de exemplu, “HTTP/1.1 200 OK” dacă resursa cerută


este disponibilă); o listă a codurilor este dată ı̂n [1], [4];

• un mesaj care este resursa cerută (pagina web sau imagine sau docu-
ment etc), sau un mesaj de eroare sau altă informaţie.

Browserul primeşte aceste răspunsul de la server şi ı̂l afişează; e de la sine


ı̂nţeles că browserul trebuie să implementeze cât mai fidel acest protocol.
Mai ı̂n detaliu, o cerere arată astfel:

• linie de cerere, precum GET /images/logo.gif HTTP/1.1, care cere o


resursă numită /images/logo.gif de pe server

• antete, precum Accept-Language: en

• o linie goală

• un mesaj opţional

Cele mai importante metode de cerere prin care un client solicită ceva de
la server sunt: GET şi POST. Acestea vor fi exemplificate ı̂n secţiunea 1.3.
Un aspect esenţial al comunicării prin HTTP este ı̂nsă faptul că HTTP
este un protocol fără stare. Asta ı̂nseamnă că ori de câte ori un client efec-
tuează o cerere către un server, acesta răspunde cererii după care “uită” ceea
ce a comunicat cu clientul; serverul nu mentine nicio legătură cu browserul
şi nu menţine resursele necesare pentru a răspunde clientului (cu o excepţie
minoră, mecansimul de keep-alive care permite reutilizarea unei conexiuni
pentru alte cereri); dacă apar modificări ale paginii sau a surselor de date pe
baza cărora s-a construit pagina, atunci browserul nu este notificat ı̂n niciun
fel.
În acest mod serverul, neţinând resurse ocupate după efectuarea răspunsului,
poate să fie utilizat de cât mai mulţi clienţi simultan. Trăsătura menţionată
trebuie privită ca o caracteristică voită a protocolului şi nu ca o lipsă.
14 CAPITOLUL 1. APLICAŢII WEB

Menţinerea stării dintre cereri consecutive venite de la acelaşi client este


totuşi dorită de către utilizatori sau programatori ı̂n majoritatea cazurilor
şi acest lucru se poate emula prin utilizarea de cookie–uri, câmpuri ascunse,
sesiuni.

1.2.2 Limbajul HTML


Limbajul HTML5 este limbajul predominant folosit pentru crearea pa-
ginilor Web. El descrie structura, conţinutul şi modul de apariţie al unei
pagini; poate să includă referinţe către imagini, legături către alte pagini,
frame-uri, formulare etc. Un exemplu simplu este:

<html>
<head>
<title>Hello HTML</title>
</head>
<body>
<p>Hello World!!</p>
<a href="http://google.com" target="_blank">Google is
your friend</a>
</body>
</html>

Conţinutul unui document HTML – care după cum se observă, este un


document de tip text – se creează pe baza unor elemente (<p>, <a> etc)
care sunt definite de către limbaj. Un element are de regulă o etichetă de
deschidere şi una de ı̂nchidere (dar ı̂n HTML acest lucru nu e ı̂ntotdeauna
cerut); elementele pot avea atribute specificate prin nume şi cărora li asociază
valori:

<span id="anId" class="aClass" style="color:blue;"


title="Hypertext Markup Language">HTML</span>

Un tutorial bun pentru ı̂ncepători se găseşte la W3Schools.com. De exem-


plu, pentru includerea unei imagini se foloseşte secvenţa:

<img src="boat.gif" alt="Big Boat">

Browserul, după primirea fişierului HTML va efectua o parsare a codului;


la ı̂ntâlnirea elementului img se face o cerere de tip GET pentru a obţine
imaginea de pe server.
5
Hypertext Markup Language.
1.2. MODELE CLASICE DE REALIZARE A APLICAŢIILOR WEB 15

1.2.3 Limbajul XHTML


Una din criticile care s–au adus lui HTML este favorizează combinarea
conţinutului efectiv al unei pagini cu indicaţii despre cum să arate acest
conţinut ı̂n pagină. Suplimentar, faptul că ı̂n HTML nu se respectă nişte
reguli de bună formare face parsarea conţinutului greoaie şi poate duce la
diferenţe de afişare ı̂n browsere. S-a dorit obţinerea unui limbaj pentru care
interpretarea să fie mai uşoară, să se poată separa structura de conţinut şi
de asemenea să semene ı̂ncă cu limbajul HTML cu care creatorii de pagini
web erau obişnuiţi. Prin preluarea trăsăturilor limbajului XML s-a ajuns la
realizarea unui limbaj hibrid — XHTML6 . Chiar dacă mai există pagini scrise
ı̂n limbaj HTML, există o presiune constantă de a produce numai conţinut
XHTML; chiar şi uneltele de dezvoltare se conformează acestei tendinţe.
Implicit, conţinutul generat de ASP.NET este de tip XHTML. Asta ı̂nseamnă
că elementele de marcare satisfac regulile:

• etichetele şi numele de atribute trebuie să fie scrise cu litere mici

• toate elementele trebuie să fie ı̂nchise, fie cu un tag (etichetă) de ı̂nchidere


dedicat (<p></p>) fie folosind un tag gol care se ı̂nchide singur (<br />).

• toate atributele trebuie să aibe valorile incluse ı̂ntre ghilimele sau apos-
troafe (id="errorLabel").

• atributul id trebuie să fie folosit ı̂n locul atributului name (dar controa-
lele ASP.NET generate vor avea amândouă aceste atribute, din motive
de compatibilitate cu codul JavaScript existent).

• elementele trebuie să fie imbricate corect: <p><a href="...">..</a></p>


şi nu <p><a href="...">..</p></a>.

• documentele XHTML trebuie să aibe un unic element rădăcină; acesta


este dat de eticheta html.

XHTML de asemenea ı̂nlătură suportul pentru câteva mecanisme exis-


tente ı̂n HTML, precum frame-urile sau precizări stilistice care nu utilizează
CSS, sau folosirea atributului target pentru link-uri7 .
Orice document XHTML trebuie să ı̂nceapă cu o declaraţie de tip de do-
cument (vezi [3], secţiunea de DTD), care defineşte tipul de XHTML folosit.
Pentru a specifica varianta XHTML 1.1 se foloseşte:
6
Extensible HTML.
7
Pentru deschiderea paginii indicate prin link ı̂ntr-o nouă fereastră se poate folosi cod
JavaScript: onclick="window.open(this.href,’_blank’);return false;".
16 CAPITOLUL 1. APLICAŢII WEB

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"


"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

iar elementul rădăcină are forma:

<html xmlns="http://www.w3.org/1999/xhtml" >


...
</html>

adică se cere declararea unui spaţiu de nume.


Există posibilitatea de a nu urma specificaţiile stricte ale lui XHTML 1.1
şi a folosi ı̂n schimb nişte canoane mai relaxate. De exemplu, se poate utiliza
XHTML 1.0 tranzitional care impune regulile structurale din XHTML dar de
asemenea permite folosirea mecanismelor de formatare care au fost ı̂nlocuite
ı̂n XHTML 1.1 cu stiluri CSS. Declaraţia de tip de document arată astfel:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"


"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

Chiar şi această variantă poate părea prea strictă, deoarece ı̂mpiedică
utilizarea frame-urilor. Dacă se doreşte utilizarea acestora cu regulile date
de XHTML atunci se poate folosi XHTML 1.0 frameset declarat ca:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"


"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">

Modul ı̂n care se generează conţinutul unei pagini de către ASP.NET este
dependent de tipul de browser care a făcut cererea.

1.3 Formulare
Formularul reprezintă modalitatea cea mai utilizată de trimitere de informaţii
dinspre browser spre server. Formularul conţine elemente care de regulă su-
portă specificarea de valori de către utilizator (cum ar fi numele şi parola pe
un formular de loginare, sau răspunsurile la un test grilă). În momentul ı̂n
care se doreşte, formularul – adică setul de input-uri utilizator – este trimis
către server. Demn de menţionat este că dacă formularul conţine şi alte ele-
mente (imagini, link–uri, text etc) acestea nu sunt trimise la server, deoarece
ele nu poartă informaţie de la utilizator.
Un formular este definit cu eticheta form:
1.3. FORMULARE 17

1 <form name="myForm" method="get" action="default.aspx"


2 id="myForm">
3 Nume: <input name="txtNume" type="text"
4 id="txtNume" />
5 <br />
6 Parola: <input name="txtParola" type="password"
7 id="txtParola" />
8 <br />
9 <input type="submit" name="butonTrimitere"
10 value="Trimite" id="butonTrimitere" />
11 </form>

(elementele de tip input sunt prezentate ı̂n cele ce urmează).


Pagina desenată ı̂n browser arată ca ı̂n figura 1.4.

Figura 1.4: Pagina web cu formular

Atributul action arată care este pagina către care se va trimite conţinutul
formularului. În exemplul de mai sus este vorba de pagina default.aspx
aflată pe acelaşi server.
Atributul method arată care din metodele GET (implicită) şi POST sunt
folosite pentru a trimite datele către server. Valorile posibile sunt get şi
post (nu contează dacă se scrie cu litere mari sau mici). Cu get toate datele
din formular sunt adăugate la URL-ul specificat de atributul action, astfel
că la apăsarea butonului de trimitere ı̂n adresă va apărea (fragment):

http://localhost:61561/TestGet/default.aspx?txtNume=lmsasu&
txtParola=myPassword&butonTrimitere=Trimite

Se observă că conţinutul formularului care poartă datele introduse de


utilizator sunt trimise ı̂n clar. Acelaşi mecanism este folosit de exemplu de
către pagina prin care se accesează motorul de căutare Google. Avantajul
18 CAPITOLUL 1. APLICAŢII WEB

acestei metode este că se poate pune un bookmark, sau poate fi folosit ca
legătură ı̂ntr-un document.
http://www.google.ro/search?hl=ro&q=data+mining&btnG=
C%C4%83utare+Google&meta=
Partea de după semn de ı̂ntrebare din URL se numeşte query string.
Trebuie spus aici că există situaţii ı̂n care folosirea metodei get este con-
traindicată; lungimea maximă a URL-ului este limitată de către browsere, ı̂n
principal8 . De exemplu, pentru Internet Explorer lungimea maximă a URL-
ului este de 2083 caractere, iar lungimea maximă a căii (http://www.site.com/
asta/este/in/cale) este de 2048 caractere; de asemenea, serverul sau proxy-
uri intermediare pot să limiteze lungimea maximă a unui URL – vezi figura
1.5; detalii suplimentare şi teste - aici.

Figura 1.5: Eroare semnalată de serverul Web: URL-ul este prea lung.

Dacă se specifică metoda post, atunci setul de date din formular se in-
clude ı̂n corpul formularului şi trimis mai departe la server; URL-ul nu va
fi modificat. Mai există o diferenţă semantică ı̂ntre get şi post: get este
considerat “idempotent”, adică nu modifică starea serverului la trimiteri re-
petate ale aceleiaşi interogări, pe când post e gândit pentru a produce efecte
colaterale pe server – tranzacţii financiare sau modificări de date.
În interiorul unui formular pot fi folosite următoarele elementele de tip
input:
8
Specificaţia nu impune o lungime maximă pentru un URL
1.3. FORMULARE 19

• text

• password

• checkbox

• radio

• submit

• reset

• file

• hidden

• image

• button

Se folosesc cu forma generală: <input type="..." .../>. Sunt prezentate


pe scurt ı̂n cele ce urmează. Pe lângă acestea mai sunt disponibile:

• textarea

• select

1.3.1 Text
Se foloseşte pentru crearea unor căsuţe text, al căror conţinut va fi furnizat
de utilizator:

1 <html>
2 <body>
3 <form action="default.aspx">
4 First name:
5 <input type="text" name="firstname">
6 <br />
7 Last name:
8 <input type="text" name="lastname">
9 <br />
10 <input type="submit" />
11 </form>
12 </body>
13 </html>
20 CAPITOLUL 1. APLICAŢII WEB

Figura 1.6: Modul ı̂n care arată un input de tip text.

Ieşirea este dată ı̂n figura 1.6. Query string-ul ı̂n acest caz arată astfel:
firstname=Rafael&lastname=Popescu.
Poate fi calificat ca fiind readonly (readonly="readonly") şi i se poate
specifica lungimea maximă a şirului acceptat: maxlength="35". De aseme-
nea poate să reacţioneze la evenimente. O listă completă a atributelor şi
evenimentelor poate fi găsită aici.

1.3.2 Password
Seamănă cu cele de tip text, cu diferenţa că ı̂n momentul ı̂n care cineva
scrie ı̂n interiorul lui, caracterele sunt suprascrise cu un caracter inert – vezi
listingul de la pagina 16 şi figura 1.4. De reţinut că deşi parola nu apare ı̂n
clar pe ecran, ea este trimisă totuşi aşa cum este scrisă; o eventuală criptare
sau folosire de protocol securizat (HTTPS) rămâne sarcina celui care creează
aplicaţia.
Aşa cum s–a spus la ı̂nceputul secţiunii 1.3, query string-ul asociat este:
txtNume=lmsasu&txtParola=myPassword
Documentaţia completă despre acest tip se găseşte aici.

1.3.3 Checkbox
Checkbox-urile sunt folosite atunci când utilizatorul trebuie să specifice
dintr-un set de opţiuni ne–mutual excluzive.

1 <form>
2 I have a bike:
3 <input type="checkbox" name="vehicle" value="Bike">
4 <br />
5 I have a car:
6 <input type="checkbox" name="vehicle" value="Car">
7 <br />
8 I have an airplane:
9 <input type="checkbox" name="vehicle" value="Airplane">
1.3. FORMULARE 21

10 <input type="submit" />


11 </form>

Ieşirea este dată ı̂n figura 1.7. Dacă se bifează Car şi Airplane atunci pe
server va ajunge un query string de forma vehicle=Car&vehicle=Airplane.
Documentaţia completă se găseşte aici.

Figura 1.7: Modul ı̂n care arată un input de tip checkbox.

1.3.4 Butoane radio


Permite alegerea a cel mult una din mai mult opţiuni. Codul HTML şi
modul ı̂n care arată sunt date mai jos.

1 <form>
2 <input type="radio" name="sex" value="male">Male
3 <br />
4 <input type="radio" name="sex" value="female">Female
5 <br />
6 <input type="submit" />
7 </form>

Figura 1.8: Modul ı̂n care arată un input de tip buton radio.

Mai multe butoane radio sunt considerate din acelasi grup dacă au aceeaşi
valoare a atributului name. Dacă se bifează una din ele, se trimite query
string-ul: sex=male. Documentaţia completă se găseşte aici.

1.3.5 Butonul de Submit


În exemplele anterioare formularele aveau un buton a cărui apăsare de-
termină trimiterea formularului către server. Acesta este creat cu:
22 CAPITOLUL 1. APLICAŢII WEB

1 <form>
2 ...
3 <input type="submit" />
4 </form>

Implicit, textul de pe el apare ca “Submit Query”. Dacă se doreşte, textul se


poate modifica folosind atributul value. Detalii se găsesc aici.

1.3.6 Butonul de reset


Se obţine cu input-ul de forma: <input type="reset" /> şi realizează
un buton pe care scrie textul “Reset” şi care va repune valorile din formular
la cele cu care au venit de pe server; nu se face trimiterea formularului la
server. Vizual, arată ca şi un buton de tip Submit.

1.3.7 File
Cu secvenţa:

1 <form action="default.aspx">
2 <input id="myFile" type="file"/>
3 <br />
4 <input type="submit" value="Incarca">
5 </form>

rezultă un formular de ı̂ncărcare de fişier care arată precum cel din figura
1.9. Detalii se găsesc aici.

1.3.8 Câmpuri ascunse


Deseori este nevoie ca ı̂n cadrul formularului să existe nişte date care să
vină de pe server şi să fie trimise ı̂napoi odată cu restul datelor din formular.
Aceste câmpuri sunt considerate ascunse, ı̂n sensul că nu au o apariţie vizuală
pe ecran. Ele se produc cu un input de forma:

<input id="myHiddenField" type="hidden"


value="valoare invizibila"/>

Acest mecanism este extrem de util pentru a memora valori auxiliare pe client
(browser). Detalii se găsesc aici.
1.3. FORMULARE 23

Figura 1.9: Input-ul de tip file, după apăsarea butonului Browse.

1.3.9 Image
Input-ul de tip imagine creează un control imagine care, atunci când este
apăsat trimite formularul către server. Locaţia imaginii este definită prin
atributul src:
1 <form action="default.aspx">
2 <input id="adresa" type="text" />
3 <br />
4 <input type="image" alt="text alternativ"
5 src="btn.png" height="20px"/>
6 </form>
cu ieşirea dată ı̂n figura 1.10

Figura 1.10: Input de tip imagine

Detalii despre acest tip de input se găsesc aici.


24 CAPITOLUL 1. APLICAŢII WEB

1.3.10 Butoane
Există un control de tip buton, care arată precum cel de Submit sau
Reset, a cărui apăsare va fi procesată exclusiv prin cod JavaScript, ı̂n brow-
ser. În exemplul de mai jos la apăsarea butonului se depune o valoare ı̂n
textbox-ul formularului, se afişează o fereastră pe ecran şi apoi se face sub-
miterea formularului, totul prin cod JavaScript (dar oricare din acţiuni este
opţională):

1 <html>
2 <head>
3 <script type="text/javascript">
4 function myJSFunction() {
5 var hiddenElement = document.getElementById("adresa");
6 if (hiddenElement != null) {
7 hiddenElement.value = "valoare setata din myJSFunction";
8 }
9 window.alert("functia myJSFunction s-a terminat");
10 document.getElementById("formular").submit();
11 }
12 </script>
13 </head>
14 <body>
15 <form id="formular" action="HTMLPage.htm">
16 <input id="adresa" type="text" size="40" />
17 <br />
18 <input id="myButton" type="button" value="button"
19 onclick="myJSFunction();"/>
20 </form>
21 </body>
22 </html>

Rezultatul este dat ı̂n figura 1.11.

1.3.11 Textarea
Input-ul de tip text este convenit a conţine o singură linie. Pentru cazul
ı̂n care se doreşte existenţa unei căsuţe text cu mai multe linii se poate folosi
elementul textarea:

1 <form id="myForm">
2 <textarea id="zonaText" cols="20" rows="4"></textarea>
1.3. FORMULARE 25

Figura 1.11: Input de tip buton. Este arătată starea după apăsarea butonu-
lui.

3 <br />
4 <input type="submit" />
5 </form>

cu reprezentarea din figura 1.12. Detalii despre ce atribute şi evenimente

Figura 1.12: Zonă de text cu 20 de coloane şi 4 linii.

suportă elementul textarea - aici.

1.3.12 Select
Elementul select permite alegerea dintr-o lista derulantă a uneia sau a
mai multor opţiuni. Lista de opţiuni este formată din perechi de forma cheie-
valoare, cheia (invizibilă ı̂n pagina desenată de browser) fiind cea care se
trimite către server, iar valoarea este folosită pentru a face alegerea efectivă.
Se poate specifica care este eleemntul implicit selectat:
1 <form method="get" action="default.aspx" id="myForm">
2 <select name="judete">
3 <option value="33">Vrancea</option>
4 <option value="14" selected="selected">Brasov</option>
26 CAPITOLUL 1. APLICAŢII WEB

5 <option value="18">Sibiu</option>
6 <option value="21">Teleorman</option>
7 </select>
8 <br />
9 <input type="submit"/>
10 </form>

Query string-ul asociat este judete=14, iar reprezentarea este dată ı̂n figura
1.13.

Figura 1.13: Reprezentarea controlului de selectare.

Este posibilă şi selectarea mai multor opţiuni simultan, dacă ı̂n cadrul
elementului select se specifică şi atributul multiple:

1 <select name="judete" multiple="multiple">


2 <option value="33">Vrancea</option>
3 <option value="14">Brasov</option>
4 <option value="18">Sibiu</option>
5 <option value="21">Teleorman</option>
6 </select>

Dacă se specifică acest atribut şi niciuna din opţiuni nu este selectată,
atunci reprezentarea din browser este cea din figura 1.14, iar query string-ul
va arăta astfel: judete=33&judete=18.

Figura 1.14: Element option cu selectare multiplă.

Detalii - aici.
Capitolul 2

ASP.NET

2.1 Generalităţi
ASP.NET este o platformă pentru aplicaţii web dezvoltată şi distribuită
de către Microsoft, pe care dezvoltatorii o folosesc pentru a contrui site-uri
web dinamice şi servicii web. A fost lansat ı̂n ianuarie 2002 cu versiunea 1.0
a .NET Framework şi este succesorul tehnologiei Active Server Pages (ASP)
dezvoltată tot de Microsoft. ASP.NET a fost construit pe Common Language
Runtime (CLR), ceea ce permite programatorilor să scrie cod folosind orice
limbaj suportat de .NET.
Merită menţionat că ASP.NET este o tehnologie freeware de la Microsoft.
ASP.NET poate fi folosit pentru a crea orice tip de aplicaţie web, ı̂ncepând de
la mici site-uri personale până la cele de tip business. Strictul necesar pentru
a programa cu ASP.NET este reprezentat de .NET Framework, care este
gratuit, şi de Visual Web Developer Express Edition, de asemenea gratuit.
Aplicaţiile ASP.NET sunt aplicaţii web .NET complete care se execută
ca şi cod compilat gestionat de .NET runtime. ASP.NET foloseşte de ase-
menea capabilităţile complete a .NET Framework-ului – o gamă de clase la
fel de uşor de ı̂nteles ca şi cele pentru o aplicaţie Windows obişnuită. În
esenţă, ASP.NET ı̂ncearcă să estompeze linia ı̂ntre dezvoltarea de aplicaţii
şi dezvoltarea web prin preluarea instrumentelor şi a tehnologiilor folosite de
programatorii de aplicaţii desktop.

2.1.1 Ce e greşit la ASP-ul clasic?


Spagetti code
Considerăm urmatorul exemplu, ı̂n care un dropdown list HTML este
populat cu rezultatul unei interogări a unei baze de date:

27
28 CAPITOLUL 2. ASP.NET

<%
Set dbConn = Server.CreateObject("ADODB.Connection")
Set rs = Server.CreateObject("ADODB.Recordset")
dbConn.Provider = "sqloledb"
dbConn.Open "Server=SERVER_NAME; Database=Pubs;
Trusted_Connection=yes"
%>
<select name="cboAuthors">
<%
rs.Open "SELECT * FROM Authors", dbConn, 3, 3
Do While Not rs.EOF
%>
<option value="<%=rs("au_id")%>">
<%=rs("au_lname") & ", " & rs("au_fname")%>
</option>
<%
rs.MoveNext
Loop
%>
</select>

Acest exemplu are nevoie de 19 linii de cod doar pentru a afişa un control
HTML simplu. Dar ceea ce este mai grav e modul ı̂n care stilul de codi-
ficare diminuează performanţele aplicaţiei deoarece amestecă codul HTML
cu cod Visual Basic. Când pagina este procesată de către ASP ISAPI (In-
ternet Server Application Programming Interface), motorul de scripting de
pe server trebuie să se activeze şi să se dezactiveze de mai multe ori pentru
a gestiona chiar şi o singură cerere. Acest lucru creşte cantitatea de timp
necesară pentru a procesa ı̂ntreaga pagină şi a o trimite la client. Mai mult,
paginile web scrise ı̂n acest stil pot atinge uşor dimensiuni dificil de stapânit.
Dacă se mai adaugă şi propriile componente COM (necesare pentru a furniza
funcţionalitatea pe care ASP nu o poate asigura) coşmarul managementului
codului creşte.
Indiferent ce abordare se foloseşte, codul devine ı̂ncurcat, lung şi greu
de depanat. În ASP.NET, aceste probleme sunt reduse ca posibilitate de
apariţie. Paginile de web sunt scrise folosind conceptele programării orientate
pe obiecte tradiţionale. Paginile de web ASP.NET conţin controalele care
pot fi programate ı̂n mod similar cu aplicaţiile desktop. Aceasta ı̂nseamnă
că nu mai este nevoie de combinarea unui set de marcaje HTML şi cod
inline. Dacă se optează pentru abordarea code behind atunci când se creaza
paginile ASP.NET, atunci codul şi marcajul sunt de fapt plasate ı̂n două
2.1. GENERALITĂŢI 29

fişiere diferite, ceea ce simplifică ı̂ntreţinerea codului şi permite separarea


sarcinii de design a paginii web de cea destul de dificilă a programării.

Limbajele de scriptare
La momentul creării sale, ASP părea o soluţie perfectă pentru dezvol-
tatorii de aplicaţii desktop care vroiau să treacă la dezvoltare web. Mai
degrabă decât să necesite ı̂nvăţarea unui limbaj de programarea cu totul
nou, ASP a permis dezvoltatorilor să folosească limbaje familiare cum ar fi
VBScript. Folosind deja popularul COM (Component Object Model) ca o
coloană vertebrală, aceste limbaje au acţionat ca un vehicul pentru accesarea
componentelor şi resurselor de pe server. Dar deşi ASP era uşor de ı̂nteles
pentru dezvoltatorii care erau deja obişnuiţi cu limbaje precum VBScript,
această familiaritate a avut un preţ. Deoarece ASP a fost bazat pe tehno-
logii vechi, ce au fost iniţial create pentru aplicaţii desktop, acestea nu au
putut sa funcţioneze la fel de bine ı̂n noul mediu de dezvoltare web.
Performanţa nu a fost singura problemă. Fiecare obiect sau variabilă uti-
lizată ı̂n ASP clasic era creată ca un tip de dată variant. Asa cum mare
parte din programatorii in Visual Basic ştiu, tipul de dată variant este slab
tipizat, având nevoie de cantităţi mari de memorie, iar tipul exact este cu-
noscut doar la momentul rulării. De aici rezultă că au performanţe mult mai
slabe decât variabilele explicite. De asemenea, cu acest tip de variabile era
imposibil de creat un IDE1 care să ofere facilitaţi de debugging, IntelliSense
(recunoasterea elementelor lexicale) şi verificarea erorilor.

2.1.2 Ce aduce ASP.NET?


Majorele diferenţe dintre ASP.NET şi platformele de dezvoltare existente
includ următoarele:

• ASP.NET propune un modelul de programare ı̂n ı̂ntregime obiect-orientat,


care include o arhitectură bazată pe evenimente şi controale, care ı̂ncurajează
ı̂ncapsularea şi refolosirea codului;
• ASP.NET oferă posibilitatea de a programa ı̂n orice limbaj acceptat de
.NET;
• ASP.NET sprijină ideea de executare rapidă: paginile şi componentele
ASP.NET sunt compilate (fie la prima cerere, fie la instalarea site-ului
pe server), ı̂n loc de a fi interpretate de fiecare dată când acestea sunt
utilizate; a se vedea figura 2.1.
1
Integrated Development Environment, mediu integrat de dezvoltare.
30 CAPITOLUL 2. ASP.NET

• modul de programare este orientat pe obiecte; orice pagină este văzută


ca un obiect, derivat din clasă pusă la dispoziţie de .NET Framework;

• are un mecanism ı̂ncorporat care permite păstrarea stării paginii (chiar


dacă protocolul de comunicare HTTP este fără stare);

• ASP.NET permite dezoltarea de aplicaţii web bazate pe evenimente,


similar cu situaţia ı̂ntâlnită ı̂n cadrul aplicaţiilor de tip forme Windows;

• este senzitiv la browserul pentru care se face trimiterea codului HTML,


alegând automat codul optimizat pentru o colecţie de browsere;

• ASP.NET este găzduit de către CLR; aceasta aduce beneficii notabile


precum: managementul automat al memoriei prin garbage collection;
tipuri de date sigure, verificabile; manipularea structurată a erorilor;
fire de execuţie - pentru fiecare cerere făcută de către un client, se
lansează un fir de execuţie separat;

• uşor de distribuit2 şi configurat.

2.2 Despre ASP.NET 3.5


2.3 Tipuri de fişiere ı̂n ASP.NET
Într–o aplicaţie web sunt disponibile următoarele tipuri de fişiere:
• fişiere cu extensia aspx — pagini web asp.net; ele conţin interfaţa uti-
lizator şi opţional cod (JavaScript, C#);

• fişiere cu extensia ascx — controale utilizator ASP.NET; reprezintă


controale personalizate, dezvoltate de programatori, prin care se ex-
tinde paleta de controale cu care vine ASP.NET; aceste controale pot
fi adăugate ı̂n pagini aspx;

• fişiere cu extensia asmx sau svc — servicii web ASP.NET (prima exten-
sie) sau servicii implementate prin Windows Communication Founda-
tion (WCF);

• web.config — fişier de configurare a aplicaţiei Web pentru ASP.NET;


conţine stringuri de conexiune către baza de date, configurări relativ la
managementul stării, al securităţii etc;
2
În sens de: deploy.
2.3. TIPURI DE FIŞIERE ÎN ASP.NET 31

Figura 2.1: Compilarea ı̂n două trepte pentru aplicaţie ASP.NET [2].
32 CAPITOLUL 2. ASP.NET

• global.asax — fişier global al aplicaţiei; se foloseşte pentru a implementa


cod de reacţie la evenimente ale aplicaţiei, precum pornirea sau oprirea
ei;

• fişiere cu extensia cs — (dacă este vorba de o aplicaţie ASP.NET cu


C#), este partea de “code–behind” care prelucrează pe server eveni-
mentele survenite pe partea de browser şi ciclul de viaţă al paginilor.
Se obţine astfel separarea părţii de interfaţă utilizator de cea de cod
executat pe server;

• fişiere cu extensia ashx — HTTP handlers, pentru returnarea de conţinut


care să nu fie pagină web;

• fişiere cu extensia axd — folosite pentru urmărirea aplicaţiei (tracing);

• fişiere cu extensia master — pentru master pages;

• fişiere cu extensia resx — pentru internaţionalizarea şi localizarea aplicaţiilor


ASP.NET;

• fişiere cu extensia sitemap — pentru crearea structurii site-ului;

• fişiere cu extensia skin — pentru crearea de teme (“skin–uri”).

2.4 Modelul de cod


O pagină web aspx este compusă din cod ASP.NET de forma:

• cod HTML; acesta este interpretat de către browser;

• controale web; acestea se recunosc prin tagurile care incep cu prefixul


asp:, ca de exemplu:

<asp:CheckBox id="myCheckBox" Text="vreau bicicleta"


runat="server" />
<asp:TextBox id="usernameText" Text="Nume de utilizator"
runat="server" />

Controalele web sunt procesate pe server şi vor produce cod HTML
care este inteligibil pentru un browser.

• cod C# sau VB.NET


2.4. MODELUL DE COD 33

Acest ultim cod este executat pe server (deci browserul nu trebuie să aibe
niciun tip de suport pentru executarea de cod .NET); relativ la locaţia ı̂n
care se poate depune acest cod, avem posibilităţile:
1. cod inline, adică scris ı̂n interiorul paginii aspx; utilizat pentru pagini
web simple;

2. cod “ı̂n spate” 3 , ı̂ntr-un document separat cu extensia cs (dacă este cod
C#). Este modelul preferat pentru dezvoltarea de pagini.
Alegerea ı̂ntre aceste două modalităţi este posibilă la crearea unei noi pagini
web, prin checkbox-ul “Place code in separate file”.
Pentru primul caz, codul paginii aspx este:
1 <%@ Page Language="C#" %>
2
3 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
4 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
5
6 <script runat="server">
7
8 protected void myButton_Click(object sender, EventArgs e)
9 {
10 myLabel.Text = "Buton apasat la: " +
11 DateTime.Now.ToLongTimeString();
12 }
13 </script>
14
15 <html xmlns="http://www.w3.org/1999/xhtml">
16 <head runat="server">
17 <title></title>
18 </head>
19 <body>
20 <form id="form1" runat="server">
21 <div>
22 <asp:Button ID="myButton" runat="server" Text="Apasati"
23 onclick="myButton_Click" />
24 <asp:Label ID="myLabel" runat="server"></asp:Label>
25 </div>
26 </form>
27 </body>
3
În original: code behind.
34 CAPITOLUL 2. ASP.NET

28 </html>

Pentru code behind, se obţin două fişiere: pagina DemoCodeBehind.aspx


şi fişierul sursă DemoCodeBehind.aspx.cs.

1 <%@ Page Language="C#" AutoEventWireup="true"


2 CodeFile="DemoCodeBehind.aspx.cs" Inherits="DemoCodeBehind" %>
3
4 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
5 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
6
7 <html xmlns="http://www.w3.org/1999/xhtml">
8 <head runat="server">
9 <title></title>
10 </head>
11 <body>
12 <form id="form1" runat="server">
13 <div>
14 <asp:Button ID="myButton" runat="server" Text="Apasati"
15 onclick="myButton_Click" />
16 <asp:Label ID="myLabel" runat="server"></asp:Label>
17 </div>
18 </form>
19 </body>
20 </html>

Partea de code behind este scrisă astfel:

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Web;
5 using System.Web.UI;
6 using System.Web.UI.WebControls;
7
8 public partial class DemoCodeBehind : System.Web.UI.Page
9 {
10 protected void Page_Load(object sender, EventArgs e)
11 {
12
13 }
14 protected void myButton_Click(object sender, EventArgs e)
2.4. MODELUL DE COD 35

15 {
16 myLabel.Text = "Buton apasat la: " +
17 DateTime.Now.ToLongTimeString();
18 }
19 }

Browserul va primi acelaşi cod, deoarece interpretarea părţii de C# şi


ASP.NET se face de către server. Este indicată folosirea variante de code–
behind, deoarece asta permite separarea părţii de layout (ce este ı̂n sarcina
unui designer) de cea de cod (sarcină de programator). În restul lucrării ne
vom referi doar la modelul de code–behind.
Conectarea dintre pagina aspx şi code–behind se face prin intermediul
directivei Page de pe primul rând. Remarcăm că se specifică unde se află
pagina de code–behind (atributul CodeFile); de asemenea faptul că pagina
aspx este de fapt un obiect, derivat din clasa care conţine code-behind (atri-
butul Inherits). Controalele declarate ı̂n pagina aspx vor fi create automat,
ı̂ntr-o clasă parţială, care completează ı̂n mod transparent codul din fişierul
DemoCodeBehind.aspx.cs. Astfel, pentru declaraţia din pagina aspx:

<asp:Label ID="myLabel" runat="server"></asp:Label>

serverul va genera automat declaraţia de câmp:

protected global::System.Web.UI.WebControls.Label myLabel;

Acest lucru era vizibil ı̂n ASP.NET 1.1, prin cod generat automat de către
designer ı̂n fişierul de code behind; pentru versiunea ASP.NET 2.0 sau mai
nouă, această declaraţie este generată automat de către server4 . Se remarcă
aici calificarea cu protected a câmpului, pentru a fi disponibil şi ı̂n pagina
aspx derivată. O vedere a acestui cod se poate obţine din Visual Studio,
meniul View→Wiew Code Gen File.
Evenimentele (precum apăsarea de buton, schimbarea itemului curent
selectat dintr-o listă de opţiuni, ı̂ncărcarea paginii) sunt tratate prin metode
ı̂n partea de code–behind. În exemplul dat mai sus se observă că evenimentul
de apăsare a butonului este procesat prin metoda (ı̂n acest context: event
handler, manipulator de metodă) myButton_Click, iar legătura se specifică
prin atributul onclick din pagina aspx. Se poate face şi ataşarea event
handler–ului prin cod:
4
Codul dat a fost preluat din locaţia ı̂n care modulul de ASP.NET ı̂şi depo-
zitează aceste fişiere, %systemroot%\Microsoft.NET\Framework\v2.0.50727\Temporary
ASP.NET files\[nume aplicatie]. Fişierul din care s-a luat declaraţia este o clasă parţială,
care completează clasa DemoCodeBehind.
36 CAPITOLUL 2. ASP.NET

myButton.Click += myButton_Click;

Se remarcă de asemenea şi metoda Page_Load care este moştenită din


clasa de bază System.Web.UI.Page. Apelul automat al metodei face parte
din ciclul de viaţă al unei pagini, subiect tratat ulterior.

2.5 Controale Web şi controale HTML


Un control Web reprezintă o clasă (exemplu: System.Web.UI.WebControls.Label)
care poate fi utilizată pe parte de code–behind; are un aspect vizual, ge-
nerându–se cod HTML de către server, cod care poate fi interpretat de brow-
ser; are de asemenea aspect comportamental – evenimente – şi proprietăţi –
de exemplu un obiect de tip Label poate să suporte modificări prin lăţime,
text, culoare, vizibilitate. Avantajul acestor controale este că permit genera-
rea de cod HTML complex, fără ca programatorul să fie interesat de detalii:
eticheta de mai sus va genera un element de tip <span> pentru browser.
Exemplul devine mai elocvent pentru controale Web de tipul Calendar sau
formă de login. In plus, se creează programatorului iluzia că modul de dezvol-
tare al aplicaţiilor web este foarte asemănător cu cel folosit pentru aplicaţiile
de tip Windows Forms, unde există o abundenţă de controale.
Din exemplul <asp:Label...> de mai sus, se observă atributul runat
cu valoarea server care este mandatoriu (ca prezenţă şi valoare) pentru
controalele web. Valoarea atributului id reprezintă numele variabilei cu care
se accesează controlul pe parte de server.
La cealaltă extremă se află codul HTML simplu, care este direct interpre-
tat de către browser. Problema cu acest cod este că nu poate fi accesat de
către code behind. Pentru a se permite accesarea programatică, s–a introdus
conceptul de control HTML. Un control HTML se obţine din codul HTML
prin adăugarea a două atribute: runat cu valoarea server şi id având drept
valoare un nume de variabilă:

<input id="myHiddenField" type="hidden" runat="server"/>

Accesarea lui ı̂n code behind se face cu:

myHiddenField.Value = "valoare setata de pe code behind";

De remarcat că şi pentru aceste controale HTML se face interpretare pe


server, deoarece codul din browser nu prezintă atributul runat; runat este
interpretat de către mediul ASP.NET şi este eliminat la trimiterea spre brow-
ser.
2.6. FORME WEB 37

2.6 Forme Web


O fomă web (denumirea consacrată sub care sunt cunoscute paginile web
aspx) reprezintă partea centrală a unei aplicaţii ASP.NET. Există două părţi
ale unei astfel de forme web:

• partea care se afişează pe client – cod HTML, rezultat prin executarea


codului ASP.NET

• partea care se află pe server, adică pagina aspx, clasa de code behind
şi cod .NET apelat de către programator

Evident, trebuie să existe o comunicare ı̂ntre aceste două părţi. Spre exem-
plu, dacă este vorba de o pagină care permite unui utilizator să ı̂şi facă
cont, datele specificate ı̂n pagina web sunt trimise prin formular (a se ve-
dea 1.3) ı̂napoi către server; acesta este numit ı̂n jargon “postback”; serverul
recepţionează acest formular şi poate să execute o secvenţă de paşi: vali-
darea datelor din formular, crearea contului. Pagina apelată va formula un
răspuns (cod HTML) pe care ı̂l va trimite browserului. După aceasta, ciclul
se poate relua. Trimiterea formularului ete un mecanism de bază specificat
de protocolul HTTP; ASP.NET nu aduce nimic nou aici.
Unul din mecansimele esenţiale pentru o uşoară dezvoltare a unui for-
mular este păstrarea stării. Deşi foloseşte mecanism clasic (câmpuri as-
cunse), modul transparent ı̂n care este integrat ı̂n framework duce la re-
ducerea substanţială a codului. În lipsa acestui mecansim, ı̂n Java sau PHP
trebuie făcută o implementare manuală.

2.6.1 Postback
Să presupunem că avem pagina default.aspx care conţine anumite date
(de exemplu, lista studenţilor din Facultatea de Matematică şi Informatică).
Atunci următorul scenariul poate avea loc:

1. Un utilizator cere prin browser această pagină. Serverul IIS primeşte


cererea, determină faptul că este o pagină aspx şi invocă motorul ASP.NET
pentru a o executa. Motorul creează pagina şi controalele conţinute, se
execută codul de iniţializare – detalii la secţiunea 2.6.4 – apoi pagina
este creeată ca şir de caractere HTML şi returnată clientului. Obiectul
pagină şi controalele incluse sunt distruse (adică devin neutilizabile; să
ne amintim că protocolul de comunicare este fără stare, deci pe server
nu e obligatoriu să menţină resursele folosite anterior).
38 CAPITOLUL 2. ASP.NET

2. O dată pagina trimisă către browser, este rândul utilizatorului să pro-
ducă acţiunea de trimitere ı̂napoi a paginii, de exemplu prin apăsarea
unui buton de tip Submit.

3. Formularul din pagină este trimis ı̂napoi la server, către pagina defa-
ult.aspx (acesta este comportamentul implicit pentru ASP.NET, dar se
poate modifica). Motorul ASP.NET primeşte iarăşi cerere şi recreează
pagina (deoarece pagina anterioară, de la pasul 1 a fost dealocată) de
pe server; această refacere va restabili totuşi valorile controalelor la
starea anterioară, prin mecansimul de viewstate – secţiunea 2.6.2.

4. ASP.NET determină care a fost acţiunea care a determinat trimiterea


ı̂napoi şi execută event handlerele asociate (metoda myButton_Click,
de exemplu). Un asemenea event handler execută o operaţie de modifi-
care a conţinutului formei web (myLabel.Text=...), eventual apelând
la cod pentru extragere de date de pe un server de baze de date, etc.

5. Pagina default.aspx cu conţinutul astfel modificat este transformată


ı̂n cod HTML şi trimisă serverului. Obiectele folosite pentru crearea
acestui răspuns sunt disponibilizate. Ciclul se reia de la pasul 2.

Postback-ul poate fi declanşat şi de o altă acţiune decât apăsarea unui bu-
ton de tip submit; de exemplu, la adăugarea de control web de tip dropdown
list, se poate specifica ı̂n Visual Studio, prin proprietatea de tip boolean
AutoPostback dacă modificarea itemului curent selectat duce la trimiterea
automată a formularului către server. Acest mecanism este util pentru cazul
ı̂n care există un dropdown list cu judeţe; alegerea unui anumit judeţ necesită
modificarea unui alt dropdown list care conţine localităţile din judeţul curent
selectat. Efectul se obţine prin cod JavaScript care acţionează pe client, cod
este injectat automat de către framework–ul ASP.NET, sub forma:

1 <script type="text/javascript">
2 //<![CDATA[
3 var theForm = document.forms[’form1’];
4 if (!theForm) {
5 theForm = document.form1;
6 }
7 function __doPostBack(eventTarget, eventArgument) {
8 if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
9 theForm.__EVENTTARGET.value = eventTarget;
10 theForm.__EVENTARGUMENT.value = eventArgument;
11 theForm.submit();
2.6. FORME WEB 39

12 }
13 }
14 //]]>
15 </script>

Se observă că avem tot o trimitere de formular, dar dictată de cod. __EVENTTARGET
şi __EVENTARGUMENT sunt două câmpuri ascunse ce sunt populate cu detalii
despre cine a produs acest postback şi eventualele detalii aferente. Dropdownlist-
ul este generat ı̂n cod HTML astfel:

1 <select name="DropDownList2"
2 onchange="javascript:setTimeout
3 (’__doPostBack(\’DropDownList2\’,\’\’)’, 0)"
4 id="DropDownList2">

În alte cadre de lucru pentru crearea de pagini web dinamice (Java, ASP,
PHP) un astfel de cod cade ı̂n sarcina programatorului. Trebuie ı̂nsă zis că
postback-ul este disponbil doar pentru controale web.
Pentru a determina pe parte de code behind dacă cererea e postback sau
nu, se poate folosi proprietatea de tip boolean Page.IsPostBack; aceasta
are valoarea true dacă este un postback, false altfel. Mecanismul este util
pentru că de regulă la postback se face umplerea controalelor cu valori dintr-
o sursă de date, ı̂n timp ce la postback acest lucru (de regulă costisitor) se
poate evita dacă se foloseşte mecanismul de viewstate.

2.6.2 Menţinerea stării prin Viewstate


În lipsa mecanismului de Viewstate (care poate fi inhibat la nivel de
control), la fiecare postback s-ar reface pagina la starea iniţială, pierzându–
se valorile depuse anterior ı̂n controale. De exemplu, dacă la procesarea
anterioară (a doua cerere către pagină) s–a setat conţinutul unei etichete
la o anumită valoare, la a treia cerere această valoare s–ar pierde. Este
explicabil, deoarece HTTP este protocol fără stare, iar serverul nu menţine
de la o cerere la alta valorile actuale ale controalelor. De regulă, asemenea
efect este nedorit.
Mecansimul prin care se păstrează ultima valoare a unui control (fie el
web sau HTML) este viewstate. Pentru fiecare web control se poate seta
valoarea EnableViewState la true (de fapt, valoarea implicită), prin asta
semnalându–se că se va menţine starea. Menţinerea se face prin intermediul
unui câmp ascuns numit __VIEWSTATE ı̂n cod HTML şi accesat cu variabila
ViewState pe parte de code behind. Pentru un control HTML trebuie să se
specifice enableviewstate=“true”.
40 CAPITOLUL 2. ASP.NET

În codul HTML generat pentru browser conţine un string cu o codificare


Base64 a stării controalelor (conţine starea tuturor controalelor care au ex-
primată opţiunea de viewstate). Acest câmp este procesat de către server pe
partea de code behind, ı̂n mod automat, o dată la postback, apoi ı̂nainte de
trimitere a răspunsului către client.
Modul ı̂n care lucrează viewstate–ul pentru prima cerere şi pentru postback–
urile ulterioare este redat ı̂n figurile 2.2 şi 2.3 ([2]).

Figura 2.2: Modul de lucru pentru viewstate la prima cerere [2].

În acest fel eliberarea resurselor necesitate de către server pentru procesa-
rea paginii pot fi eliberate fără problemă; apare ı̂nsă cazul unui câmp ascuns
care va fi plimbat permanent ı̂ntre client şi server, ceea ce duce la ı̂ngreunarea
comunicării (pagină mai mare → trimitere mai lentă). De regulă, pentru o
pagină Web se folosesc o mulţime de controale, care implicit au partea de
viewstate neinhibată, deci se poate ajunge la dimeniuni mari pentru câmpul
ascuns; se mai ia aici ı̂n calcul şi expandarea intrinsecă indusă de codificarea
Base64.
Pentru contracararea acestui efect, reamintim că sunt controale a căror
stare nu se schimbă ı̂n timp (e.g. butoane), sau a căror stare este refăcută de
fiecare dată, pentru care dezactivarea opţiunii de viewstate poate ı̂nsemna
o economie importantă de spaţiu. De asemenea, evitarea stabilirii valorii
pentru controale prin cod (şi precizarea lor la designul paginii aspx) reduce
viewstate-ul.
Există chiar situaţii când pagină cu o dimensiune prea mare a câmpurilor
ascunse sunt blocate de către proxy-uri. Pentru aceasta, viewstate–ul poate
fi spart ı̂n mai multe bucăţi; detalii se găsesc ı̂n [2], pagina 81 (View State
2.6. FORME WEB 41

Figura 2.3: Modul de lucru pentru viewstate pentru postback [2].

Chunking).
În final, menţionăm că viewstate se poate folos nu doar de către con-
troalele din pagină, ci şi de către programator, permiţându–se adăugarea şi
regăsirea unor perechi de forma (cheie, valoare).

2.6.3 Utilitatea conceptului de controale


Să considerăm formă web:

1 <%@ Page Language="C#" AutoEventWireup="true"


2 CodeFile="UtilitateControale.aspx.cs"
3 Inherits="UtilitateControale" %>
4
5 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
6 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
7
8 <html xmlns="http://www.w3.org/1999/xhtml">
9 <head runat="server">
10 <title></title>
11 </head>
12 <body>
13 <form id="form1" runat="server" method="get">
42 CAPITOLUL 2. ASP.NET

14 <div>
15 <asp:Label ID="Lable1" runat="server" Text="Nume">
16 </asp:Label>
17 <asp:TextBox ID="txtName" runat="server">
18 </asp:TextBox>
19 <br />
20 <asp:CheckBox ID="chkApples" runat="server"
21 Text="Vreau mere" />
22 <br />
23 <asp:CheckBox ID="chkPears" runat="server"
24 Text="Vreau pere" />
25 <br />
26 <asp:Button ID="btnSend" runat="server"
27 onclick="btnSend_Click"
28 Text="Trimite" />
29 </div>
30 </form>
31 </body>
32 </html>

La trimiterea conform selecţiei din figura 2.4, query string-ul arată astfel
(fragment, am omis viewstate-ul şi alte câmpuri ascunse):

txtName=Rafael&chkApples=on&btnSend=Trimite

Figura 2.4: Pagină web obţinută cu controale web.

În code–behind, valoarea controalelor se poate accesa cu5 :

1 if (Request.Form["chkApples"] == "on"
2 && Request.Form["chkPears"] != "on") ...
5
Dacă transmiterea de parametri se face cu metoda get şi nu cu post, atunci ı̂n loc de
Request.Form se foloşeste Request.QueryString.
2.6. FORME WEB 43

Este ı̂nsă mai natural a se interpreta cele două căsuţe de checkbox ca nişte
componente care au o proprietate, Checked, de tip boolean; de asemenea,
căsuţa de text poate să fie accesată foarte uşor:
1 string name = txtName.Text;
2 if (chkApples.Checked && !chkPears.Checked)
3 {
4 txtName.Text += " vrea doar mere";
5 }
Astfel, consultarea valorilor de pe formular este simplă, ı̂n maniera de reali-
zare a aplicaţiilor de tip Windows Forms. În cele de mai sus, Request este
un obiect de tip HttpRequest pus la dispoziţie prin moştenire din clasa Page,
pentru a avea acces la datele cererii dinspre client către server.
Acelaşi mecanism este valabil şi pentru controalele HTML:
1 Nume<input id="txtName2" type="text" runat="server" /><br />
2 <input name="chkApples2" type="checkbox" runat="server"
3 id="chkApples2" /> Vreau mere<br />
4 <input name="chkApples2" type="checkbox" runat="server"
5 id="chkPears2" /> Vreau pere
cu procesarea pe code behind (atenţie la modul de accesare a textului din
input–ul de tip text):
1 if (chkApples2.Checked == true && chkPears2.Checked == false)
2 {
3 txtName2.Value += " vrea doar mere";
4 }

2.6.4 Ciclul de viaţă al unei pagini (variantă sumară)


Paşii parcurşi de la primirea unei cereri pentru o formă web până la
furnizarea răspunsului către client sunt:
1. iniţializarea paginii
2. instanţierea codului utilizator
3. validare
4. procesarea de evenimente
5. legarea automată la date
6. disponibilizarea de resurse
44 CAPITOLUL 2. ASP.NET

Iniţializarea paginii
La acest pas se creează pagina ASP.NET (se instanţiază atât clasa co-
respunzătoare porţiunii de code–behind, cât şi cea corespunzătoare paginii
aspx); controalele sunt instanţiate. Dacă pagina nu este prima dată cerută de
către acest browser (adică dacă este un postback), atunci se face restaurarea
stării din viewstate. Aici se apelează de către framework metoda Page.Init.

Iniţializarea codului utilizator


Este apelată metoda Page.Load (de regulă ea este scrisă Page_Load ı̂n
code behind). Aici de regulă se scrie cod care face ı̂ncărcarea datelor ı̂n
pagină.

Validările
Uneori se cere ca valorile furnizate de către utilizator ı̂n browser să satis-
facă anumite cerinţe. Procesul de validare (care se poate efectua şi de către
codul JavaScript de pe client, dar nu obligatoriu) trebuie să fie ı̂ndeplinit pen-
tru a se asigura că valorile cu care urmează să se lucreze au relevanţă. În caz
contrar, pagina se poate retrimite pentru comletarea adecvată a intrărilor.

Procesarea de evenimente
Sunt executate metodele care funcţionează pe post de event handler-i.
Sunt două feluri de procesăei:

• răspuns imediat, dacă este vorba de apăsare de buton sau postback


automat

• rs̆puns de tip schimbare: dacă s–a stabilit un event handler pentru un


anumit control, dar nu s-a precizat AutoPostback=true, şi ı̂n plus se
detectează (prin comparaţie cu valorile din viewstate) că a apărut o
schimbare ı̂n starea controlului, atunci se exectuă acest event handler.

Procesarea evenimentelor de tip schimbare se face ı̂nainte de procesarea eve-


nimentelor de tip răspuns imediat.

Legare automată la date


Fenomenul se va detalia ı̂ntr–un capitol ulterior. Este vorba de con-
troale care se leagă la surse de date. Acestea ı̂şi pot reı̂mprospăta automat
conţinutul, după ce eventualele modificări / adăugări / ştergeri de date.
2.6. FORME WEB 45

Disponibilizarea resurselor
Se generează pagina HTML car eva fi trimisă către browser. Obiectele
instanţiate pentru această cerere devin neutilizabile.
46 CAPITOLUL 2. ASP.NET
Capitolul 3

Forme Web, gestiunea stării

3.1 Forme Web (continuare)


3.1.1 Colecţia de controale din pagină
O pagină reprezintă un container pentru controalele dispuse pe ea; un
control poate de asemenea să fie de tip container (e.g. Panel, MultiView).
Regăsirea unui control “copil” conţinut se face fie prin iterarea peste colecţia
Controls, fie prin folosirea metodei FindControl(string id). Proprietatea
şi metoda amintită sunt moştenite din clasa System.Web.UI.Control, bază
a tot ceea ce ı̂nseamnă controale server.

3.1.2 Request
Proprietatea Request reprezintă obiect de tipul System.Web.HttpRequest
asociat cererii HTTP efectuate de browser. Există următoarele metode şi
proprietăţi (lista este incompletă):

• Browser — permite accesarea descrierii proprietăţilor browserului folo-


sit de client (tipul, numele, versiunea, platforma, capabilităţi etc);

• Cookies – permite accesarea cookie-urilor trimise de site către browser;

• Form — permite accesarea formularului şi a conţinutului său;

• QueryString — pentru accesarea parametrilor interogării făcute prin


get;

• URL, URLReferrer — adresa curentă, respectiv adresa paginii din care


s–a făcut cererea curentă;

47
48 CAPITOLUL 3. FORME WEB, GESTIUNEA STĂRII

• UserHostName, UserHostAddress — adresa şi numele calculatorului


de pe care s–a făcut interogarea;

• UserLanguages — lista de limbi pe care o are setată browserul clientu-


lui; utilă pentru internaţionalizare.

Pentru o listă completă a metodelor şi proprietăţilor, invităm consultarea


MSDN–ului.

3.1.3 Response
Deşi rar folosit, acest obiect, instanţă a clasei System.Web.HttpResponse
dă acces la câteva mecanisme, printre care:

• setarea de cookie-uri ce sunt trimise spre client – via proprietatea


Cookies;

• saltul la o pagină nouă – via metoda Redirect; ı̂n felul acesta, de pe


server se trimite un mesaj browserului prin care este instruit să ceară
pagina indicată de server; se poate face redirectare la orice fel de pagină
(chiar şi la una de tip htm), aflată oriunde (chiar şi pe un alt server);

• determinarea stării de conectare a clientului – prin interogarea pro-


prietăţii boolene IsClientConnected; dacă răspunsul este “false”,
atunci se poate renunţa la o procesare consumatoare de resurse pe
partea de server;

• trimiterea de conţinut, altceva decât şir de caractere – via proprieta-


tea ContentType; tipurile posibile de conţinut sunt prevăzute de către
specificaţiile MIME type.

• lucrul direct cu fluxul de ieşire către client, prin proprietatea OutputStream.

3.1.4 Server
Obiectul Server, de tip System.Web.HttpServerUtility dă acces la me-
tode auxiliare pentru procesarea de cereri web. Deosebit de utile sunt me-
todele HtmlEncode şi duala ei HtmlDecode. Dacă codul ASP.NET postează
conţinut de tip şir de caractere ı̂n pagină, este obligatoriu a se face codificarea
lor conform canoanelor HTML, altfel caractere precum < sau > pot produce
ravagii ı̂n pagină; ele trebuie codificate ca fiind &lt; respectiv &gt;.
Similar, substituirea corectă a caracterelor ı̂n adree, se folosesc metodele
UrlEncode şi UrlDecode. Pentru determinarea căii fizice corespunzătoare
3.2. TIPURI DE CONTROALE SERVER 49

unui fişier de pe site se foloseşte metoda MapPath(). În sfârşit, pentru


transferarea la o altă pagină, fără a se face redirectare prin browser (ca la
Response.Redirect), se poate folosi Server.Transfer(), cu restricţia ca
transferul să se facă doar către pagină ASP.NET de pe acelaşi server.

3.2 Tipuri de controale server


Există două tipuri majore de controale, aşa cum s-a specificat şi ı̂n 2.5:
controale server HTML şi controale server Web. Acestea din urmă cunosc
specializările: controale “bogate”, de validare, de date, de navigare, de login,
părţi Web, pentru ASP.NET AJAX şi de tip ASP.NET mobil.
Toate controalele de tip server au ca principală trăsătură faptul că se pot
procesa ca obiecte pe server, având deci un tip de date reprezentat pe plat-
forma .NET. Clasa de bază pentru orice control este clasa System.Web.UI.Control,
care e derivată de clasa System.Web.UI.WebControls.WebControl, repre-
zentând baza pentru controalele de tip server şi System.Web.UI.HtmlControls.HtmlControls,
bază pentru controalele server HTML. Clasa Control conţine şi transmite
derivaţilor proprietăţile şi metodele:

• ClientID – proprietate ce returnează identificatorul fiecărui control din


pagina HTML; acesta va putea fi utilizat ı̂n JavaScript, prin functia
document.getElementById();

• Controls – proprietate care returnează o colecţie de controale copil ale


obiectului curent. De exemplu, un control declarat cu asp:TextBox
este copil al formularului;

• EnableViewState – returnează sau setează o valoare logică specificând


dacă ultima stare setată pentru un control va fi sau nu salvată ı̂n
ViewState (proprietate a paginii, care va fi transformată ı̂n câmpul
ascuns __VIEWSTATE).

• ID – este identificatoru controlului, adică numele de variabilă cu care


se poate acces ı̂n code behind controlul respectiv;

• Page – obiectul pagină care conţine controlul curent;

• Parent – controlul ı̂n care este copil controlul curent;

• Visible – proprietate cu valoare logică, specificând dacă controlul este


vizibil sau nu;

• DataBind() – se face popularea controlului la surse de date;


50 CAPITOLUL 3. FORME WEB, GESTIUNEA STĂRII

• FindControl() – se caută recursiv un control cu un anumit nume ı̂n


controlul curent;

• HasControl() – returnează true sau false, arătând dacă controlul


curent are sau nu controale copil;

• Render() – se scrie codul HTML asociat controlului curent; metoda


se apelează de regulă de către motorul de ASP.NET şi nu direct de
programator.

3.2.1 Controale server HTML


Sunt din trei categorii mari: controale de tip input (e.g. butoane, check-
box, căsute de text etc), controale de tip container (formular, tabel, textarea
etc) şi grupul format din HtmlImage, HtmlLink, HtmlTitle. Clasa de bază
este HtmlControl, iar proprietăţile expuse sunt:

• Attributes – proprietate care permite accesarea sau adăugarea de atri-


bute;

• Disabled – returnează sau setează starea de inhibare a controlului;


dacă are valoarea true, va fi afişat pe pagină, dar cu culoare gri deschis;

• Style – permite accesarea sai modificarea stilurilor CSS pentru con-


trolul curent;

• TagName – returnează numele controlului.

Un control de tip HtmlContainerControl reprezintă un control HTML


care are atât etichetă de deschidere, cât şi de ı̂nchidere, cum ar fi a sau img.
Pe lângă cele moştenite din HtmlControl, mai avem proprietăţile:

• InnerHtml – returnează sau setează textul HTML dintre etichetele de


deschidere şi ı̂nchidere. Toate caracterele sunt lăsate aşa cum se dau;
facilitatea este folosită de regulă pentru includerea de conţinut HTML;

• InnerText – ca mai sus, cu deosebirea că textul setat suferă modificarea


caracterelor care au semnificaţie HTML predefinită (<, >, ghilimele etc)
sunt ı̂nlocuite cu codificări corespunătoare (&lt;, &gt;, &quot; etc).

Clasa HtmlInputControl este folosită ca bază pentru controalele care per-


mit interacţiunea cu utilizatorul, cum ar fi <input type="text">, <input
type="submit">. Faţă de cele moştenite din HtmlInputControl, se mai pun
la dispoziţie proprietăţile:
3.2. TIPURI DE CONTROALE SERVER 51

• Type – obţine tipul unui obiect HtmlInputControl;

• Value – obţine sau setează conţinutul pentru controlul curent.

Exemplu: să presupunem că avem codul HTML:

1 <input type="text" runat="server" id="myTextBox" />

iar pe partea de code-behind codul (fragment):

1 protected void Page_Load(object sender, EventArgs e)


2 {
3 myTextBox.Style["font-size"] = "30px";
4 myTextBox.Style["color"] = "red";
5
6 myTextBox.Value = "Introduceti numele";
7 myTextBox.Attributes["onfocus"] =
8 "alert(’numele de familie’)";
9 }

La rulare codul HTML generat va fi (fagment):

1 <input name="myTextBox" type="text" id="myTextBox"


2 style="font-size:30px;color:red;" value="Introduceti numele"
3 onfocus="alert(’numele de familie’)" />

cu efectul din figura 3.1.

Figura 3.1: Setarea de proprietăţi pentru controale server HTML

Controalele server HTML pot manipula două evenimente pe parte de


server:

• ServerClick – este un click procesat de partea de code-behind; de


exemplu, pentru codul:
52 CAPITOLUL 3. FORME WEB, GESTIUNEA STĂRII

1 <a href="http://www.google.com" runat="server"


2 id="myAnchor" onserverclick="MyServerClick">apasa aici</a>

şi implementarea ı̂n partea de code-behind:

1 protected void MyServerClick(object sender, EventArgs e)


2 {
3 Response.Redirect("http://www.clusty.com");
4 }

nu se va deschide pagina de Google, ci cea setată de către codul C#.


Efectul se obţine deoarece apăsarea pe link nu e procesată de către
browser ı̂n modul natural, ci are loc trimiterea formularului ı̂napoi către
server, aşa cum se poate deduce din codul HTML rezultat:

1 <a id="myAnchor" href="javascript:__doPostBack(’myAnchor’,’’)">


2 apasa aici</a>

Acest tip de eveniment este disponibil doar pentru o parte din controa-
lele server HTML.
• ServerChange – se declanşează atunci când se modifică conţinutul unui
control poate avea stări diferite: text, checkbox etc. De exemplu:

1 <input id="myCheckbox" type="checkbox"


2 runat="server" onserverchange="MyServerChange"/>
3 <br />
4 <asp:Label ID="myLabel" runat="server" Text=""></asp:Label>
5 <br />
6 <input type="submit" id="btnSubmit"/>

cu partea de code-behind (fragment):

1 protected void MyServerChange(object sender, EventArgs e)


2 {
3 myLabel.Text = myCheckbox.Checked.ToString();
4 }

La apăsarea butonului de tip Submit se va executa pe server metoda


MyServerChange. În acest caz, schimbarea stării căsuţei de opţiune nu
duce automat la trimiterea formularului către server, după cum se poate
constata din inspectarea codului HTML obţinut - lipseşte procesarea
unui eveniment de tip JavaScript.
3.3. GESTIUNEA STĂRII ÎN ASP.NET 53

3.2.2 Controale Web server


Reprezintă clase din spaţiul de nume System.Web.UI.WebControls, gândite
pentru a extinde gama de controale HTML; un control de tip Web server are
nu doar o reprezentare grafică mai bogată – datorită unui cod bogat de HTML
care se generează automat – ci şi un comportament mai orientat către comu-
nicarea cu serverul. Clasa de bază este WebControl, care expune mult mai
multe proprietăţi şi metode decât analoagele lor de tip HTML server control.
Spre exemplu, se pune la dispoziţie proprietatea Font prin care se specifică
proprietăţile fontului curent: corp de literă, culoare, decorare, mărime etc.
Se elimină astfel necesitatea de a şti foarte bine numele atributelor CSS şi
valorile lor, deoarece clasele folosite sunt făcute astfel ı̂ncât să furnizeze doar
valorile acceptabile.
Toate controalele se declară ı̂n pagina aspx cu sintaxa <asp:numeControl>,
au automat proprietatea runat cu valoarea setată pe server – mai mult, dacă
atributul lipseşte, se declară eroare de compilare.
Spre deosebire de controalele HTML server, fiecare control server Web
dispune de metoda Focus() care poate fi utilizată pe parte de code-behind
pentru a selecta acel control ca fiind cel curent. Pentru o căsuţă de text
cu identificatorul myTextbox, apelul myTextbox.Focus() duce la injectarea
următoarei secţiuni ı̂n codul paginii HTML rezultate:

1 <script type="text/javascript">
2 <!--
3 WebForm_AutoFocus(’myTextbox’);// -->
4 </script>

unde funcţia JavaScript WebForm_AutoFocus provine dintr-un fişier de re-


surse pus la dispoziţie de către ASP.NET, numit WebResource.axd.
Dacă pe un formular sunt dispuse mai multe butoane, este posibilă spe-
cificarea unui buton ca fiind implicit – adică apăsarea lui tastei Enter să fie
interpretată ca apăsarea pe acest buton. Specificarea lui se face prin interme-
diul formularului, care prin atributul DefaultButton poate specifica ID-ul
butonului care va fi considerat ca implicit. De asemenea, dacă se folosesc
panouri (<asp:Panel>), acestea putând grupa mai multe butoane, se poate
specifica la nivel de panou care e butonul implicit.

3.3 Gestiunea stării ı̂n ASP.NET


HTTP este un protocol care nu păstrează starea. Motorul de ASP.NET
distruge obiectele pe care le-a format pentru a răspunde cererii clientului. De
54 CAPITOLUL 3. FORME WEB, GESTIUNEA STĂRII

multe ori, ı̂nsă, este nevoie ca ı̂ntre două cereri succesive o anumită informaţie
să fie păstrată şi recuperată dacĕ este nevoie de ea. Exemplul clasic este coşul
de cumpărături pe care ı̂l pune la dispoziţie un site de tip comerţ electronic:
vizitatorul poate să adauge din pagini diferite obiectele pe care le cumpără;
la orice moment, acest coş este disponbibil pentru a fi inspectat sau modificat
de acel cumpărător.
Managementul stării ı̂n ASP.NET se poate face cu:

1. ViewState

2. Query String

3. Cookie-uri

4. Sesiunea (Session)

5. Application

6. Profilul

7. Caching

Vom prezenta doar primele cinci metode. O comparaţie a lor se găseşte ı̂n
tabelele 3.1 şi 3.2.

Tabela 3.1: Opţiuni de management al stărilor (1).

View State Query String Cookie-uri utilizator


Tipuri de date Tot ce e Un număr limitat Şiruri de caractere
permise serializabil de caractere
Stocare Un câmp ascuns Adresa URL din Memoria (RAM/HDD)
ı̂n pagină browser a calc. clientului
Durată de Permanent reţinut se pierde când se Setat de programator
viaţă la postback-uri introduce o nouă
adresă
Vizibilitate Pagina curentă Pagina ţintă Întreaga aplicaţie
ASP.NET
Securitate Uşor de citit, Vizibil ı̂n clar, Nesigur, poate fi
relativ greu de modif. uşor de modificat modificat de utiliz.
poate fi criptat
Performanţă Nu afectează serverul Bună, deoarece datele Bună, deoarece
dar poate ı̂ngreuna sunt puţine datele sunt puţine
comunicaţia
3.3. GESTIUNEA STĂRII ÎN ASP.NET 55

Tabela 3.2: Opţiuni de management al stărilor (2).

Sesiune Application
Tipuri de date Orice tip serializabil Orice tip .NET
permise
Stocare Memoria serverului sau Memoria serverului
bază de date
Durată de Expiră după un ∆t de la Durata de viaţă
viaţă ultima accesare a serverului a aplicaţiei
Vizibilitate În toată aplicaţia, În toată aplicaţia,
ı̂n cadrul unei sesiuni global
Securitate Sigur, deoarece datele nu Foarte sigure, deoarece datele nu
sunt trimise la client sunt trimise la client
Performanţă Sesiuni mari → server lent Multe date → server lent
Utilizare Stocare de cumpărături Variabile globale

3.3.1 ViewState
Stochează datele ı̂n interiorul paginii curente, sub forma unui câmp as-
cuns. La orice trimitere de formular c’u atre server, acest câmp este proiectat
ı̂napoi. Este de regulă folosit pentru a stoca ultima valoare a unui control (şi
se include aici şi setare privind stilul controlului, nu doar conţinutul); poate
fi de asemenea ı̂mbogăţit cu perechi de forma “cheie–valoare”, care ulterior
pot fi modificate sau şterse.

1 ViewState["Counter"] = 1;
2 ...
3 int counter;
4 if (ViewState["Counter"] != null)
5 {
6 counter = (int)ViewState["Counter"];
7 }

Cheia este de tip şir de caractere; se poate folosi drept valoare orice tip de
date serializabile (care are atributul [Serializable] definit atât pentru el
cât şi pentru clasele din care este wderivat, direct sau nu).
Exemplu: clasa Customer definită de programator precum:
1 [Serializable]
2 public class Customer
3 {
56 CAPITOLUL 3. FORME WEB, GESTIUNEA STĂRII

4 public string FirstName;


5 public string LastName;
6 public Customer(string firstName, string lastName)
7 {
8 FirstName = firstName;
9 LastName = lastName;
10 }
11 }

poate fi instanţiată şi stocată ı̂n ViewState:

1 Customer cust = new Customer("Rafael", "Popescu");


2 ViewState["CurrentCustomer"] = cust;

respectiv refăcut cu:

1 Customer cust = ViewState["CurrentCustomer"] as Customer;

Aici este important faptul că câmpurile FirstName şi LastName sunt seriali-
zabile (proprietate implicită a clasei String). Dacă sunt câmpuri dintr-un tip
neserializabil, sau dacă nu se doreşte serializarea lor, atunci acestea trebuie
să fie prefixate cu atributul [NonSerializable].
Situaţiile ı̂n care nu se recomandă folosirea ViewState-ului pentru menţinerea
datelor sunt:

• datele au caracter confidenţial; codificarea prin Base64 este reversibilă


şi uşor de citit; mai mult, se poate modifica acest conţinut cu un efort
mediu (vezi [2]);

• se foloseşte o informaţie care trebuie să fie accesibilă pentru mai multe


pagini; ı̂n acest caz sunt mai indicate sesiunea, query string-ul sau
cookie-urile;

• datele ce trebuie stocate sunt voluminoase, ceea ce ı̂ngreunează comunicaţia


spre şi dinspre server (ca parte a formularului, acest câmp este plimbat
permanent).

Pentru ı̂mbunătăţirea timpilor de răspuns, se poate elimina acest views-


tate atunci când nu este cu adevărat nevoie de el. Asemenea cazuri sunt:

• controlul nu ı̂şi modifică niciodată starea (conţinut, caracteristici vizu-


ale);

• controlul este repopulat la fiecare postback;


3.3. GESTIUNEA STĂRII ÎN ASP.NET 57

• este un control de tip input, a cărui valoare este setată de utilizator,


deci oricum serverul le primeşte.

Pentru a se omite salvarea automată ı̂n ViewState pentru starea unui


control, se poate specifica proprietatea EnableViewState cu valoarea false,
sau se poate specifica la nivel de pagină, fie prin modificarea ei ca anterior
la nivel de pagină, fie la directivă:

1 <%@ Page Language="C#" EnableViewState="false" ... %>

Securitatea ViewState-ului poate fi privită din 2 perspective: ne intere-


sează să nu se poată face modificarea conţinutului (chiar dacă acest conţinut
poate fi uşor descifrat), sau chiar să nu se poată descifra ce s–a salvat.
Primul aspect este automat rezolvat, deoarece o dată cu valorile pere-
chilor cheie–valoare serializată se salvează şi o sumă de control asociată
conţinutului. Când se reprimeşte la server câmpul ascuns, se recalculează
această sumă de control; dacă nu coincide cu valoarea din ViewState, ı̂nseamnă
că s–a intervenit ı̂n el.
Dacă se vrea ca ViewState-ul să fie codificat, atunci se va specifica ı̂n
directiva Page:

1 <%@Page ViewStateEncryptionMode="Always" ... %>

În loc de Always se mai poate utiliza Never sau Auto.

3.3.2 Query String


Din adresa http://www.google.ro/search?q=data+mining&lang=ro, qu-
ery string-ul este partea de după semnul de ı̂ntrebare. Reprezentarea se face
sub forma variabila=valoare, iar mai multe astfel de perechi pot fi separate
prin caracterul &.
Există următoarele limitări ale unui query string:

• Informaţia este limitată la şiruri de caractere, care trebuie să conţine


caractere valide pentru un URL.

• Informaţia este vizibilă pentru oricine;

• Pot apărea modificări neanuţate ale componentelor din QS; de exemplu,


ı̂n loc de lang se poate decide să se folosească doar l.

• lungimea maximă a acestui URL este limitată; a se revedea 1.3.

Un QS poate fi folosit din partea de code behind astfel:


58 CAPITOLUL 3. FORME WEB, GESTIUNEA STĂRII

1 Response.Redirect("newpage.aspx?recordID=" + recordID.ToString());

Trebuie permanent avut grijă ca adresa astfel formată să conţină mereu ca-
ractere valide pentru un URL, adică un set destul de redus. Acest lucru se
obţine prin apelul metodei Server.UrlEncode(detaliu).

3.3.3 Cross page posting


[2], pag 233 si urm

3.3.4 Cookies
Cookie-urile utilizator reprezintă fişiere care sunt menţinute de către cli-
ent (browser) fie ı̂n memoria sa (şi deci disponibile doar pe durata cât brow-
serul este deschis), fie pe harddisk (şi deci pot fi valabile la repornirea brow-
serului). Reprezintă perechi de forma cheie–valoare, ambele de tip string.
Un cookie este trimis automat de către browser serverului şi apoi ı̂napoi.
Crearea unui cookie şi ataşarea lui la răspuns se face astfel:

1 HttpCookie cookie = new HttpCookie("Preferences");


2 cookie["LanguagePref"] = "English";
3 cookie["Country"] = "US";
4 Response.Cookies.Add(cookie);

Mai sus s-a definit un cookie care va fi stocat doar ı̂n browser; dacă
se doreşte ca acesta să fie stocat pe HDD, se va specifica o dată limită de
valabilitate:

1 cookie.Expires = DateTime.Now.AddYears(1);

Redobândirea unui cookie se face prin:

1 HttpCookie cookie = Request.Cookies["Preferences"];


2 string language;
3 if (cookie != null)
4 {
5 language = cookie["LanguagePref"];
6 }
3.3. GESTIUNEA STĂRII ÎN ASP.NET 59

3.3.5 Sesiunea
Sesiunea nu este parte a HTTP-ului, dar este un concept implementat de
către orice framework pentru crearea de pagini web dinamice ı̂l implemen-
tează. O sesiune este caracteristică unui browser, reprezintă un container de
elemente structurate ca şi “cheie–valoare”, unde cheia este de tip string iar
valoarea orice obiect serializabil. De regulă, se menţine ı̂n memoria serverului
câte o sesiune, instanţiată la fiecare inceput de sesiune. Alternativ, se poate
specifica faptul că sesiunile să fie memorate ı̂ntr–o bază de date.
Fiecare sesiune este identificată de un şir de 120 de biţi lungime, care
se salvează de regulă ı̂ntr–un cookie pe browser; la orice cerere efectuată de
către browser, se trimite automat şi acest cookie, ceea ce permite serverului
să determine care este sesiunea ataşată browserului; pe partea de code behind
se poate accesa conţinutul sesiunii. Dacă se suspectează că clientul nu are
mecansimul de cookie permis (sau browserul nu permite aşa ceva), atunci se
poate face codificarea acestui identificator de sesiune prin intermediul URL–
ului, setare disponibilă din fişierul de configurare web.config; sau se poate
folosi autodetectare, deci să se decidă automat care din variante să se aplice.
Adăugarea (sau suprascriere) şi accesarea conţinutului din sesiune se fac
precum urmează (exemplu):
1 Session["ProductsDataSet"] = dsProducts;
2 dsProducts = Session["ProductsDataSet"] as DataSet;
Sesiunea este pierdută ı̂ntr–una din următoarele situaţii:
• utilizatorul ı̂nchide browserul; aceasta duce la pierderea cookie-ului care
conţine indentificatorul de sesiune;
• sesiunea expira; implicit, ı̂n ASP.NET după 20 de minute de la ultima
accesare dinspre clientul care a determinat crearea sesiunii, se dealocă
automat această sesiune de către server, pentru a nu irosi resursele; va-
loarea de timeout se poate specifica ı̂n fişierul de configurare web.config;
• dacă programatorul apelează ı̂n code behind Session.Abandon().
Dacă cineva urmăresşte comunicaţia pe reţea, poate să obţină acest co-
okie, şi deci acces la sesiune. Pentru aceasta se poate folosi Secure Socket
Layer (SSL).

3.3.6 Application
Obiectul Application este unic pentru toată lumea, deci poate să conţină
nişte valori care sunt de interes general: contoare – de exemplu numărul de
60 CAPITOLUL 3. FORME WEB, GESTIUNEA STĂRII

utilizatori curent loginaţi – sau seturi de date care nu se modifică – dicţionare


de traducere.

1 protected void Page_Load(Object sender, EventArgs e)


2 {
3 // Acquire exclusive access.
4 Application.Lock();
5 int count = 0;
6 if (Application["HitCounterForOrderPage"] != null)
7 count = (int)Application["HitCounterForOrderPage"];
8 count++;
9 Application["HitCounterForOrderPage"] = count;
10 // Release exclusive access.
11 Application.UnLock();
12 lblCounter.Text = count.ToString();
13 }
Capitolul 4

Validatoare, manipulatoare de
HTTP, controale utilizator

4.1 Controale de validare a intrării


În cazul formularelor, o parte considerabilă o reprezintă controalele de
preluare a datelor de la utilizator. Pentru aceste date preluate un minim de
reguli se cere, de obicei, să fie respectat. De exemplu, pentru un nume de
utilizator, se cere ca acesta să fie nevid; pentru o adresă de email sau un
număr de telefon, acesta trebuie să aibe un anumit format.
Validarea se poate face ı̂n două locuri: pe server (la postback) sau pe
client, ı̂nainte de trimiterea formularului care le conţine. A doua variantă
este, evident, preferată, deoarece se scuteşte un drum până la server, pentru
ca acolo să se constate că datele sunt invalide. Am prefera deci ca ı̂n codul
HTML care se trimite clientului să existe invocare de cod JavaScript care
să verifice, pe cât posibil, dacă regulile de validare a datelor de intrare sunt
satisfăcute.
Pentru a evita scrierea acestui cod de validare manual, s–au introdus
următoarele controale de validare:

• <asp:RequiredFieldValidator> - verifică dacă un câmp de intrare are


conţinut;

• <asp:RangeValidator> - determină dacă valoarea specificată ı̂ntr–un


control de intrare are valoarea cuprinsă ı̂ntre două margini precizate;

• <asp:CompareValidator> - verifică dacă valoarea unui control este ı̂ntr–


o relaţie anume cu altă valoare;

61
62CAPITOLUL 4. VALIDATOARE, MANIPULATOARE, CONTROALE

• <asp:RegularExpressionValidator> - verifică dacă valoarea unui control


satisface o anumită expresie regulată;

• <asp:CustomValidator> - pentru cazurile ı̂n care logica de validare nu


se regăseşte printre controalele enumerate anterior;

• <asp:ValidationSummary> - arată un sumar al erorilor de validare


pentru controalele cărora li s-a asociat validator

Pentru un control se poate specifica mai mult de un validator; de exemplu,


pentru un textbox se poate asocia un validator de tip RequiredFieldValidator
care verifică dacă conţinutul căsuţei de text este nevid; suplimentar, se poate
asocia un validator de expresie regulată, pentru a vedea dacă conţinutul sa-
tisface un anumit format. Este chiar cerut a se asocia “validatorul de câmp
cerut”, deoarece pentru un RegularExpressionValidator (ca de altfel şi
pentru RangeValidator şi CompareValidator), testul de validare se consi-
deră trecut dacă intrarea este lăsată goală.
Controalele de preluare a datelor care sunt validate sunt: TextBox, Lis-
tBox, DropDownList, RadioButtonList, HtmlInputText, HtmlTextArea, Ht-
mlSelect. Când se validează un control de tip listă (care conţine particula
“List” ı̂n denumire), se validează proprietatea Value pentru obiectul ListItem
curent selectat.

4.1.1 Paşii de validare


Validarea se porneşte atunci când utilizatorul apasă un buton (sau link
sau ImageButton) care determină trimiterea formularului către server. Fie-
care componentă pomenită are o proprietate numită CausesValidation de
tip boolean care dacă are valoarea true atunci se va face validarea paginii.
Validarea se face iterând peste controale şi verificând dacă sunt ı̂ndeplinite
condiţiile specificare. Pentru validatoarele care nu sunt respectate, se re-
trimite pagina, cu indicarea cazurilor care nu au fost respectate. Scenariul
descris corespunde situaţiei ı̂n care ı̂n browserul folosit pentru afişare nu se
permite executare de cod JavaScript; dacă se permite şi validatoarele nu sunt
respectate, atunci pagina nici măcar nu mai ajunge să fie trimisă către server,
ci se va afişa direct mesajul de eroare pentru fiecare validator nevalidat ı̂n
parte.

4.1.2 Clasa BaseValidator


Toate validatoarele sunt derivate din clasa BaseValidator. Proprietăţile
esenţiale propagate ı̂n derivaţi sunt:
4.1. CONTROALE DE VALIDARE A INTRĂRII 63

• ControlToValidate – id-ul controlului pentru care se aplică validarea;


este necesar a fi specificat;

• Display – cum anume se afişează mesajul de eroare; dacă este specifi-


cată valoarea Static, atunci spaţiul necesar afişării este prealocat; dacă
este specificată valoarea Dynamic, atunci la afişarea acestui conţinut
se va produce deplasarea elementelor ce-i urmează pentru a face loc
conţinutului;

• EnableClientScript – pentru valoarea true, se va permite apelarea


de cod JavaScript pe client, pentru a verifica validitatea;

• Enabled – specifică dacă este o validare activată sau nu;

• ErrorMessage – mesajul de eroare care va fi afişat ı̂ntr–un control


ValidationSumary, dacă există aşa ceva;

• Text – textul de eroare care se va afişa dacă validatorul nu este respec-


tat;

• ValidationGroup – se referă la posibilitatea de grupare de validatori


ı̂n mai multe grupuri;

4.1.3 Validatorul RequiredFieldValidator


Verifică dacă un control are sau nu conţinut introdus. Implicit, se va
verifica dacă textul este vid (sau conţine doar spaţii); dacă se doreşte ca
această comparaţie să se facă cu un şir de caractere specificat de utilizator,
atunci valoarea aceasta se poate da ı̂n propietatea InitialValue. Dacă
conţinutul textbox-ului coincide cu această valoare iniţială (după aplicarea
unei operaţii de Trim pe conţinutul controlului), atunci se raportează eroare
de validare.
Exemplu:

1 <asp:TextBox ID="txtUserName" runat="server"></asp:TextBox>


2 <asp:RequiredFieldValidator ID="RequiredFieldValidatorUserName"
3 runat="server" ControlToValidate="txtUserName"
4 ErrorMessage="Numele lipseste">
5 *
6 </asp:RequiredFieldValidator>

Dacă se trimite formularul prin apăsarea de buton iar căsuţa de text este
goală, atunci se va afişa o steluţă roşie ı̂n dreptul căsuţei – figura 4.1. Textul
64CAPITOLUL 4. VALIDATOARE, MANIPULATOARE, CONTROALE

specificat ı̂n proprietatea ErrorMessage este vizibil doar dacă pe pagină se


mai adaugă şi un control de tip ValidationSummary, cu rezultatul din figura
4.2.

1 <asp:ValidationSummary ID="sumarValidare" runat="server" />

Figura 4.1: Validator de valoare cerută

Figura 4.2: Validator de valoare cerută, cu sumarizator de validare

4.1.4 Validatorul RangeValidator


Se foloseşte pentru a verifica dacă o valoare specificată este intr-un inter-
val specificat prin intermediul proprietăţilor MinimumValue şi MaximumValue;
tipul de date pentru care se face comparaţie este specificat ı̂n proprietatea
Type. Se pot specifica tipurile:
• Currency
4.1. CONTROALE DE VALIDARE A INTRĂRII 65

• Date

• Double

• Integer

• String

1 <asp:TextBox runat="server" ID="DayOff" />


2 <asp:RangeValidator runat="server" Display="dynamic"
3 ControlToValidate="DayOff" Type="Date"
4 ErrorMessage="Day Off is not within the valid interval"
5 MinimumValue="08/05/2008" MaximumValue="08/20/2008">*
6 </asp:RangeValidator>

4.1.5 Validatorul CompareValidator


Se foloseţe pentru a verifica dacă valoarea specificată ı̂ntr-un control este
ı̂ntr-o relaţie (mai mare, mai mic etc) cu o valoare specificată, sau chiar cu
valoarea unui alt control.
Prin proprietatea Operator se specifică relaţia pe care trebuie să o aibe
controlul la care este ataşată validarea (văzut aici ca membru stâng al relaţiei
binare) cu o altă valoare. Opţiunile sunt: Equal, NotEqual, GreaterThan,
GreaterThanEqual, LessThan, LessThanEqual şi DataTypeCheck. Ultima
valoare este utilă pentru a verifica dacă valoarea din control se poate repre-
zenta ı̂n tipul de date specificat via Type.

1 <asp:TextBox runat="server" ID="Age" />


2 <asp:CompareValidator runat="server" Display="dynamic"
3 ControlToValidate="Age" ValueToCompare="18"
4 ErrorMessage="You must be at least 18 years old"
5 Type="Integer" Operator="GreaterThanEqual">*
6 </asp:CompareValidator>

O altă situaţie des ı̂ntâlnită este când trebuie specificat ı̂ntr–un tex-
tbox o valoare care să coincidă cu cea scrisă ı̂n alt textbox, de exemplu
la validarea unei parole alese. Pentru aceasta, se va specifica proprietatea
ControlToCompare cu referinţă la celălalt textbox de comparaţie:

1 <asp:TextBox runat="server" TextMode="Password" ID="Password" />


2 <asp:TextBox runat="server" TextMode="Password" ID="Password2" />
3 <asp:CompareValidator runat="server"
66CAPITOLUL 4. VALIDATOARE, MANIPULATOARE, CONTROALE

4 ControlToValidate="Password2" ControlToCompare="Password"
5 ErrorMessage="The passwords don’t match"
6 Type="String" Display="dynamic">
7 <img src="imgError.gif" alt="The passwords don’t match" />
8 </asp:CompareValidator>

4.1.6 Validatorul RegularExpressionValidator


Este utilizat ı̂n cazul ı̂n care se doreşte specificarea unei expresii regulate
faţă de care să se facă validarea. Exemplul cel mai des invocat este cel al
unei adrese de email, cu reprezentarea (acceptabilă, dar şi alte variante mai
inspirate se pot da) .*@.{2,}.̇{2,}1 .

1 <asp:TextBox runat="server" ID="Email" />


2 <asp:RegularExpressionValidator runat="server"
3 ControlToValidate="Email"
4 ValidationExpression=".*@.{2,}\..{2,}"
5 ErrorMessage="E-mail is not in a valid format"
6 Display="dynamic">*
7 </asp:RegularExpressionValidator>

4.1.7 Controlul CustomValidator


Există situaţii care validarea nu poate fi făcută cu ce s–a prezentat mai
sus; fie se invocă resurse de pe server (verificarea unor date din baza de date,
sau logică specifică domeniului), fie este vorba de un şir de paşi care definesc
validarea. Pentru această situaţie se foloseşte CustomValidator. Codul se
poate executa pe parte doar de server sau de server + client.
Numele functiei JavaScript care se apelează pentru validarea ı̂n browser se
specifică ca valoarea a proprietăţii ClientValidationFunction, iar metoda
din code-behind se referă cu atributul asp.net onservervalidate. Pentru
ambele functii, ca prim argument se specifica validatorul care a declansat
evenimentul de verificare, iar al doilea parametru este un argument care
dă acces la valoarea controlului pentru care se face validarea, ı̂mpreună cu
posibilitatea de a seta validitatea stării.
Pentru JavaScript, funţia de validare a faptului că numărul este divizibil
cu 5 este:

1 <script type="text/javascript">
2 function myFunction(ctl, args) {
1
A se vedea Regular Expression Library.
4.1. CONTROALE DE VALIDARE A INTRĂRII 67

3 args.IsValid = (args.Value % 5 == 0);


4 }
5 </script>

iar pe partea de code-behind:

1 protected void CustomValidator1_ServerValidate(object source,


2 ServerValidateEventArgs args)
3 {
4 if (int.Parse(args.Value) % 5 == 0)
5 {
6 args.IsValid = true;
7 }
8 else
9 {
10 args.IsValid = false;
11 }
12 }

referirea lor ı̂n cadrul controlului fiind:

1 <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>


2 <asp:CustomValidator ID="myValidator" runat="server"
3 ErrorMessage="Numarul nu e divizibil cu 5"
4 ClientValidationFunction="myFunction"
5 ControlToValidate="TextBox1" Display="Dynamic"
6 onservervalidate="CustomValidator1_ServerValidate">*
7 </asp:CustomValidator>

4.1.8 Controlul ValidationSummary


Acest control nu este prevăzut pentru validare, ci pentru a strânge un
sumar al erorilor din pagină. De la fiecare validator care semnalează eroare se
preia conţinutul proprietăcţii ErrorMessage şi se arată fie ı̂n pagină, fie ı̂ntr-
o fereastră JavaScript, dacă proprietatea ShowMessageBox are valoarea true.
Modul de afişare a erorrilor este specificat de proprietatea DisplayMode, cu
valorile: SingleParagraph, List, BulletList.

4.1.9 Grupuri de validare


Pentru paginile mai “stufoase”, se poate imagina o grupare a controalelor
(de exemplu, prin obiecte de tip Panel). Proprietatea ValidationGroup se
68CAPITOLUL 4. VALIDATOARE, MANIPULATOARE, CONTROALE

poate specifica la nivel de control de validare, sub forma unui şir de caractere;
de asemenea, fiecare buton care produce postback poate să specifice o valoare
pentru aceeaşi proprietate; la apăsarea lui, se face verificarea validatoarelor
doar pentru cele care au acelaşi grup ca şi butonul acţionat. Pentru toate
celelate validatoare care nu fac parte din grup, se omite partea de verificare
a corectitudinii.

4.2 Manipulatoare HTTP


Manipulatoarele HTTP (ı̂n original: HTTP handlers) sunt modalităţi
de a produce un răspuns de către motorul de ASP.NET pentru o cerere
venită. În secţiunea <httpHandlers> a fişierului web.config se afă o suită de
subelemente <add> care specifică cine anume va răspunde la o cerere pentru
un anumit tip de fişier (aspx, axd etc). De exemplu, cererile către fişiere cu
extensia config sau cs sunt manipulate de către HttpForbiddenHandler care
generează o excepţie.
Pentru crearea unui manipulator HTTP se scrie o clasă care implemen-
tează interfaţa IHttpHandler; interfaţa obligă la implementarea metodei
ProcessRequest() şi a proprietăţii read–only IsReusable. Metoda efec-
tuează ı̂ntreaga muncă (consultare de resurse, implementare de logică a
aplicaţiei, verificări), iar proprietatea comunică motorului de ASP.NET dacă
obiectul de manipulare de HTTP este reutilizabil sau trebuie neapărat distrus
şi refăcut de la 0 pentru o altă cerere.
Exemplu:
1 using System;
2 using System.Web;
3 namespace TestHTTPHandler
4 {
5 public class SimpleHandler : IHttpHandler
6 {
7 public void ProcessRequest(System.Web.HttpContext context)
8 {
9 HttpResponse response = context.Response;
10 response.Write("<html><body><h1>Rendered by the SimpleHandler");
11 response.Write("</h1></body></html>") ;
12 }
13
14 public bool IsReusable
15 {
16 get {return true;}
4.2. MANIPULATOARE HTTP 69

17 }
18 }
19 }
În fisierul web.config, ı̂n secţiunea httpHandlers se adaugă intrarea:
1 <add verb="*" path="fisier.fis" type="TestHTTPHandler.SimpleHandler"/>
Dacă ı̂n adresă se specifică "fisier.fis", atunci se va apela o instanţă a clasei
TestHTTPHandler.SimpleHandler care va produce documentul HTML:
1 <html><body><h1>Rendered by the SimpleHandler</h1></body></html>
Pentru IIS 5.x sau 6, serverul nu va recunoaşte extensia “fis” (mai corect: nu
va invoca handlerul definit de noi) ci va căuta fişierul cu numele “fisier.fis”;
negăsindu-l, va returna o eroare. Pentru a obţine comportamentul descris
mai sus, trebuie făcută o mapare la nivel de extensie de fişier din panoul de
control al serverului IIS (comanda inetmgr.exe).
O altă posibilitate de a lucra cu aceste manipulatoare de HTTP este de-
finirea unor fişiere cu extensia ashx care pot fi apelate de către utilizator sau
de browser. Potenţialul acestei abordări este mare: se poate servi conţinut
provenind din cereri atipice, de exemplu se pot cere imagini sau alte resurse
care sunt stocate ı̂n baza de date. Pe lângă forma de stocare diferită, se
poate să se implementeze nişte paşi – autorizare, decorare a datelor, filtrare
– care să permită accesarea datelor având ı̂n vedere o politică complexă.
Exemplul dat mai jos implementează următorul scenariu: ı̂ntr–o pagină
HTML dorim să avem referinţe către imagini care se află undeva pe harddisk,
dar nu neapărat ı̂n directoare găsite sub directorul aplicaţiei web. Deoarece
referiţa la o imagine se face cu o adresă relativă la rădăcina aplicaţiei web, este
nevoie de a specifica o cale către fişiere prin intermediul unui manipulator
de HTTP. Construim un HTTP handler sub formă de fişier ashx care, la
metoda ProcessRequest va interoga query string-ul “fp” (cheie stabilită de
către noi) care va copia fişierul imagine referit de query string ı̂n fluxul celui
care a făcut cererea. În felul acesta, o imagine poate avea atributul src
cu valoarea “ImageLoader.ashx?fp=d:\images\winter.jpg” (după fp se poate
scrie orice permite determinarea unică a căii către fişier).

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Web;
5 using System.IO;
6 using System.Web.Services;
70CAPITOLUL 4. VALIDATOARE, MANIPULATOARE, CONTROALE

7
8 namespace AlbumFoto
9 {
10 /// <summary>
11 /// loads an image from the disk
12 /// </summary>
13 [WebService(Namespace = "http://cs.unitbv.ro/")]
14 [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
15 public class ImageLoader : IHttpHandler
16 {
17
18 /// <summary>
19 /// Enables processing of HTTP Web requests by a custom
20 /// HttpHandler that implements the
21 /// <see cref="T:System.Web.IHttpHandler"/> interface.
22 /// </summary>
23 /// <param name="context">An
24 /// <see cref="T:System.Web.HttpContext"/>
25 /// object that provides references to the intrinsic
26 /// server objects
27 /// (for example, Request, Response, Session, and Server)
28 /// used to service HTTP requests.</param>
29 public void ProcessRequest(HttpContext context)
30 {
31 string fileFullPath = context.Request.QueryString["fp"];
32 if (String.IsNullOrEmpty(fileFullPath))
33 {
34 return;
35 }
36 int posLasDot = fileFullPath.LastIndexOf(".");
37 string fileType = fileFullPath.Substring(posLasDot + 1)
38 .ToLower().Trim();
39 if (fileType == "jpg")
40 {
41 fileType = "jpeg";
42 }
43 context.Response.ContentType = "image/" + fileType;
44
45 BinaryReader br = new BinaryReader(
46 File.OpenRead(fileFullPath));
47 int bufferLength = 1024;
4.3. CONTROALE UTILIZATOR 71

48 byte[] buffer;
49 while ((buffer = br.ReadBytes(bufferLength)).Length > 0)
50 {
51 context.Response.OutputStream.Write(
52 buffer, 0, buffer.Length);
53 }
54 br.Close();
55 }
56
57 public bool IsReusable
58 {
59 get
60 {
61 return true;
62 }
63 }
64 }
65 }

4.3 Controale utilizator


Va urma...
72CAPITOLUL 4. VALIDATOARE, MANIPULATOARE, CONTROALE
Capitolul 5

Teme, pagini master, navigare

Temele permit specificarea de detalii vizuale pentru paginile web. Se


permite astfel completarea facilităţilor CSS. Pentru un site se pot defini mai
multe teme, care se pot aplica individual, pentru fiecare utilizator in parte.

5.1 Cascading Style Sheets


Folosind Cascading Style Sheets (CSS) reprezintă modalitatea de separare
a aspectului vizual de partea de conţinut; mai mult decât atât, permite
centralizarea acestor reguli de stil şi reutilizarea lor. Regulile CSS se referă
la modul ı̂n care vor fi decorate vizual elementele HTML de către browser.
Regulile sunt aplicate de către browser asupra codului HTML primit de la
server.
O regulă se poate referi ı̂n cazul cel mai simplu la numele de element
căruia i se va aplica:

1 body
2 {
3 font-family: Verdana, Arial, Sans-Serif;
4 font-size: small;
5 }

De exemplu, pentru codul de mai sus se arată că regula se aplică elementului
body, fontul fiind: Verdana, sau dacă acesta nu este disponibil ı̂n browser,
atunci Arial, sau dacă nici acesta nu este disponibil, atunci se va folosi
fontul Sans-Serif implicit al browserului1 . Alternativ, pentru un anumit
(sau orice) element, stilul se poate aplica doar dacă elementul respectiv are
1
Despre fonturi sans serif: aici.

73
74 CAPITOLUL 5. TEME, PAGINI MASTER, NAVIGARE

definită o clasă anume. De exemplu, ı̂n listing–ul de mai jos, stilul se aplică
oricui are clasa heading1, precum ı̂n <p class="heading1">continut</p>:

1 .heading1
2 {
3 font-weight: bold;
4 font-size: large;
5 color: lime;
6 font-family: Verdana, Arial, Sans-Serif;
7 }

O foaie de stiluri – un fişier CSS – poate specifica un set de reguli, care


sunt invocate ı̂n pagina HTML prin intermediul unui link din cadrul ele-
mentului head:

1 <link href="StyleSheet.css" rel="stylesheet"


2 type="text/css" />

Stilurile se aplică cu predilecţie elementelor HTML, dar se pot specifica


şi controalelor Web server, folosind atributul CssClass.

5.2 Teme
Stilurile CSS se pot aplica uşor elementelor de tip HTML sau controalelor,
dar ele vin cu un conţinut fix, specific HTML-ului: dimensiunea fontului,
culoarea fundalului etc. Se pune ı̂ntrebarea: cum anume se poate aplica ideea
de stil şi pentru proprietăţile unui control ASP.NET? având ı̂n vedere că un
asemenea control poate veni cu proprietăţi al căror nume nu se regăseşte ı̂n
ceea ce pune la dispoziţie CSS–ul, avem nevoie de o modalitate de a completa
facilităţile acestuia din urmă. Spre exemplu ne propunem să setăm stilul unui
control de tip Calendar; având ı̂n vedere că acest control este particularizat
prin proprietăţi specifice claselor ASP.NET, vrem să completăm facilităţile
CSS–ului.
Diferenţele ı̂ntre stilurile CSS şi temele ASP.NET sunt:

1. Temele sunt asociate controalelor, nu CSS-uluil; dar temele preiau ideea


de bază a CSS-ului, definirea de stil şi reutilizarea lui pentru obţinerea
unui layout unitar;

2. Temele sunt aplicate de către server; fişierul CSS menţionat pentru o


pagină HTML este descărcat de către browser şi interpretat tot de către
el;
5.2. TEME 75

3. Temele pot fi aplicate prin intermediul fişierelor de configurare; aceasta


permite aplicarea unei teme paginilor dintr-un ı̂ntreg folder fără a fi
nevoie să se modifice paginile aspx

4. Temele nu se aplică ı̂n aceeaşi ordine ca şi stilurile CSS; dacă se specifică
o proprietate ı̂ntr–o temă şi ı̂ntr-un control, atunci setarea din temă este
cea care se aplică

Toate temele se definesc ı̂n câte un director din directorul numit App_Themes
aflat direct ı̂n rădăcina aplicaţiei web. La un moment dat, pentru o anumită
pagină cel mult o temă este activă. Pentru a defini o temă, este nevoie să
se definească cel puţin un fişier text cu extensia skin. Un fişier skin conţine
declaraţii de controale ASP.NET, pentru care se pot specifica proprietăţile
dorite:

1 <asp:ListBox runat="server" ForeColor="White"


2 BackColor="Orange"/>

Spre deosebire de declaraţiile asemănătoare din paginile aspx, atributul


id nu se poate specifica; atributul runat trebuie să fie prezent, iar ı̂n rest orice
alt atribut poate să lipsească. Se pot crea mai multe fişiere skin, de exemplu
fiecare conţinând declaraţii pentru controale ı̂nrudite. Există posibilitatea de
a defini fişiere de skin şi la nivel global, dar acest lucru este nerecomandat,
din cauză că ı̂ngreunează instalarea aplicaţiei pe alte servere.
Exemplu de fişier skin:

1 <asp:ListBox runat="server" ForeColor="White" BackColor="Orange"/>


2 <asp:TextBox runat="server" ForeColor="White" BackColor="Orange"/>
3 <asp:Button runat="server" ForeColor="White" BackColor="Orange"/>

Pentru a aplica o temă anume unei pagini, se poate proceda ı̂n mai multe
feluri: se poate folosi atributul Theme ı̂n cadrul directivei Page, având valoa-
rea numele directorului de temă dorit:

1 <%@ Page Language="C#" AutoEventWireup="true" ... Theme="FunkyTheme" %>

O altă variantă este specificarea proprietăţii Theme a paginii curente ı̂n code
behind:

1 protected void Page_PreInit(object sender, EventArgs e)


2 {
3 Theme = "FunkyTheme";
4 }
76 CAPITOLUL 5. TEME, PAGINI MASTER, NAVIGARE

Menţionăm că tema se poate seta ı̂n code behind doar ı̂n evenimentul de
Page_PreInit, eveniment care se declanşează ı̂nainte de Page_Load. În am-
bele cazuri, efectul este acelaşi.
Dacă se doreşte ca un anumit control să nu fie “ı̂mbrăcat” conform setărilor
de temă, se poate specifica ı̂n cadrul paginii aspx atributul EnableTheming
cu valoarea false.
Pentru un acelaşi control se pot specifica mai multe skin-uri, precum mai
jos:
1 <asp:ListBox runat="server" ForeColor="White"
2 BackColor="Orange" />
3 <asp:TextBox runat="server" ForeColor="White"
4 BackColor="Orange" />
5 <asp:Button runat="server" ForeColor="White"
6 BackColor="Orange" />
7 <asp:TextBox runat="server" ForeColor="White"
8 BackColor="DarkOrange"
9 Font-Bold="True" SkinID="Dramatic" />
10 <asp:Button runat="server" ForeColor="White"
11 BackColor="DarkOrange" Font-Bold="True"
12 SkinID="Dramatic" />
Specificarea skin-ului cu nume, va trebui specificată pentru controlul ı̂n
cauză din pagina aspx proprietatea SkinID având valoarea identificatorul de
skin:
1 <asp:Button ID="Button1" runat="server" ...
2 SkinID="Dramatic" />
În cadrul unui director de tip temă, se poate de asemenea crea un fişier de
tip CSS. Dacă o pagină foloseşte tema curentă, atunci se va aplica automat
şi stilul CSS din directorul temei. Acest lucru este util dacă:
• se doreşte aplicarea de CSS pentru elemente HTML care nu corespund
controalelor server;

• se consideră că CSS-urile, fiind standardizate, ar trebui folosite mai


mult;

• aceste fişiere CSS deja există şi se doreşte utilizarea lor, fără a fi nevoie
ca să fie rescrise sub formă de teme
Există o oarecare problemă: pentru a putea schimba tema la momentul
rulării, deci pentru a aplica CSS-ul din directorul noii teme, este nevoie
5.3. PAGINI MASTER 77

ca să se poată modifica elementul link din interiorul porţiunii de head.


Modificarea se face automat dacă elementul head are precizat atributul runat
cu valoarea server.
Temele se pot aplica şi prin intermediul fişierului de configurare, ı̂n secţiunea
system.web:

1 <configuration>
2 <system.web>
3 <pages theme="FunkyTheme" />
4 </system.web>
5 </configuration>

Dacă o pagină are specificată tema, atunci această din urmă specificare are
prioritate faţă de ceea ce apare ı̂n fişierul web.config.
După cum s–a arătat mai sus, temele pot fi aplicate dinamic, prin intervenţie
din code behind:

1 protected void Page_PreInit(object sender, EventArgs e)


2 {
3 if (Session["Theme"] == null)
4 {
5 // No theme has been chosen. Choose a default
6 // (or set a blank string to make sure no theme
7 // is used).
8 Page.Theme = "";
9 }
10 else
11 {
12 Page.Theme = Session["Theme"] as String;
13 }
14 }

unde depunerea ı̂n sesiune s–ar face prin alegerea dintr-o listă de opţiuni (un
control de tip DropdownList).

5.3 Pagini master


Pentru crearea unei structuri unitare a unui site, se pot folosi variantele:

• controale utilizator; pot fi utilizate pentru a implementa principiul


“don’t repeat yourself”, dar nimeni nu poate să să garanteze că acest
conţinut, chiar dacă e identic, este şi dispus identic;
78 CAPITOLUL 5. TEME, PAGINI MASTER, NAVIGARE

• frame–uri HTML; nu mai sunt suportate de standardul XHTML; moti-


vele pentru care frame-urile sunt considerate o alegere nepotrivită sunt
dezbătute ı̂n peste 7000 de tratări ale subiectului aici.

• pagini master – varianta standard ı̂n ASP.NET.

O pagină master permite crearea unui şablon care va fi aplicat peste nişte
pagini de tip “conţinut”. Utilizarea unei pagini master pentru tot site-ul
determină o apariţie consistentă a acestor pagini. Folosind pagini master,
s–a răspuns următoarelor probleme:

• se defineşte o singură dată o porţiune de pagină care poate fi reutilizată


ori de câte ori se doreşte

• se definesc regiunile editabile, adică acele zone ı̂n care o pagină ce


foloseşte şablonul are voie să adauge conţinut;

• suport din partea unei unelte precum Visual Studio

• se permite particularizarea unor aspecte din pagina master, din pagini


de conţinut

O pagină master este un fişier cu extensia master. Foloseşte un model


de declarare similar cu cel al paginilor aspx, adică pagină pe care se pot
depune controale şi parte de code-behind. Adăugarea unei pagini master se
face asemănător cu adăugarea unei pagini obişnuite de tip aspx. Rezultatul
va fi:

1 <%@ Master Language="C#" AutoEventWireup="true"


2 CodeBehind="MyMasterPage.master.cs"
3 Inherits="WebApplication1.MyMasterPage" %>
4
5 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
6 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
7
8 <html xmlns="http://www.w3.org/1999/xhtml" >
9 <head runat="server">
10 <title></title>
11 <asp:ContentPlaceHolder ID="head" runat="server">
12 </asp:ContentPlaceHolder>
13 </head>
14 <body>
15 <form id="form1" runat="server">
5.3. PAGINI MASTER 79

16 <div>
17 <asp:ContentPlaceHolder ID="ContentPlaceHolder1"
18 runat="server">
19
20 </asp:ContentPlaceHolder>
21 </div>
22 </form>
23 </body>
24 </html>
Controlul de tip ContentPlaceHolder nu are proprietăţi deosebite, doar
că specifică locul ı̂n care o pagină care vrea să aplice acest şablon ı̂şi va depune
conţinutul. Altfel zis, aceasta este partea ı̂n care se va afişa conţinutul vari-
abil dat de diverse pagini. Remarcăm că pagina master defineşte partea de
ı̂nceput a fişierului HTML (elementele html, head, body). Se remarcă faptul
că elementul head apare ı̂n interiorul unui control de tip ContentPlaceHolder,
ceea ce are sens, deoarece astfel se poate specifica ulterior conţinut supli-
mentar: elemente link, script-uri Java, tag-uri meta etc, cu valori specifice
fiecărei pagini conţinut.
O pagină care foloseşte un master page se numeşte pagină conţinut2 ;
legătura dintre ea şi pagina master trebuie specificată explicit, ı̂n directiva
Page:
1 <%@ Page Language="C#" MasterPageFile="~/MyMasterPage.master" ... %>
Specificarea căii cu ~ la ı̂nceput se referă la rădăcina site-ului; dacă se foloseşte
direct numele fişierului de master, atunci se va căuta ı̂ntr–un director al
cărui nume este MasterPages; dacă fişierul nu este găsit, atunci se caută ı̂n
rădăcina site-ului.
O pagină conţinut nu va mai defini partea de html, head, body, deoarece
acestea sunt deja date de către master page; va avea ı̂n schimb un control de
tip asp:Content, care prin intermediul atributului ContentPlaceHolderID
specifică ı̂n ce control de tip ContentPlaceHolder va fi aşezat:
1 <%@ Page Title="" Language="C#"
2 MasterPageFile="~/MyMasterPage.Master" AutoEventWireup="true"
3 CodeBehind="WebForm1.aspx.cs"
4 Inherits="WebApplication1.WebForm1" %>
5 <asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
6 </asp:Content>
7 <asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1"
2
În original: content page.
80 CAPITOLUL 5. TEME, PAGINI MASTER, NAVIGARE

8 runat="server">
9 </asp:Content>
Se remarcă atributul Title din directiva Page, care permite stabilirea
unui titlu pentru pagină, chiar dacă elementul title este prezent ı̂n pagina
master.
Un master page tipic arată precum ı̂n figura 5.1 ([2]). Design-ul se poate
face mai evoluat folosind CSS, ca ı̂n listing-ul următor:
1 .leftPanel
2 {
3 position: absolute;
4 top: 70px;
5 left: 10px;
6 width: 150px;
7 }
8 .rightPanel
9 {
10 position: absolute;
11 top: 70px;
12 right: 10px;
13 width: 150px;
14 }
15 .centerPanel
16 {
17 margin-left: 151px;
18 margin-right: 151px;
19 padding-left: 12px;
20 padding-right: 12px;
21 }
invocat de către div–uri prin:
1 <div class="leftPanel">...</div>
2 <div class="centerPanel">
3 <asp:ContentPlaceHolder id="ContentPlaceHolder1" runat="server">
4 </asp:ContentPlaceHolder>
5 </div>
6 <div class="rightPanel">...</div>
O problemă care poate apărea este calea relativă către diferite resurse
(imagini, fişiere cod JavaScript) invocată ı̂n master page. De exemplu, dacă
se creează un folder numit MasterPages şi ı̂n el se depune o pagină master,
care invocă o imagine precum:
5.3. PAGINI MASTER 81

Figura 5.1: Structura unei pagini de tip master

1 <img src="banner.jpg" />


atunci la design in VS 2008 pagina se vede bine; dacă se foloseşte o pagină
conţinut ı̂ntr–un alt folder, atunci la afişare va lipsi imaginea, deoarece ea
este căutată relativ la pagina conţinut. Codul de imagine de mai sus fiind
de tip HTML, serverul nu ı̂l procesează, iar browserul nu găseşte imaginea
ı̂n directorul unde se află pagina de conţinut.
O rezolvare rapidă este transformarea codului HTML al imaginii ı̂n con-
trol de tip server HTML, iar ı̂n această situaţie calea către imagine este
interpretată de către server (şi nu de browser) ca o cale relativă la pagina
master. O a doua variantă este specificarea căii pentru acest control de tip
server folosind locaţia absolută pornind de la rădăcina serverului cu ~:
1 <img src="~/MasterPages/banner.jpg" runat="server" />
Ca ordine de executare a evenimentelor, se poate vedea, folosind facili-
tatea de Trace din pagini că se execută prima dată partea de Page.PreInit
din master page, apoi Page.PreInit din content page; Page.Load din master
page, apoi Page.Load din content page; astfel se asigură faptul că codul din
pagina conţinut are ultimul cuvânt.
Există situaţii ı̂n care din pagina de conţinut se doreşte accesarea paginii
master, de exemplu penqtru a accede la ceva publicat de către pagina master:
82 CAPITOLUL 5. TEME, PAGINI MASTER, NAVIGARE

1 public string BannerText


2 {
3 get { return lblTitleContent.Text; }
4 set { lblTitleContent.Text = value; }
5 }
Se poate obţine acces la această proprietate prin proprietatea Master:
1 protected void Page_Load(object sender, EventArgs e)
2 {
3 SiteTemplate master =
4 Master as SiteTemplate;
5 master.BannerText = "Content Page #1";
6 }
Conversia cu as este necesară, deoarece proprietatea Master este de tip
MasterPage. Pentru a se evita conversia, se poate folosi o directivă ı̂n cadrul
paginii conţinut:
1 <%@ MasterType VirtualPath="~/SiteTemplate.master" %>
şi atunci partea de ı̂ncărcare a paginii de mai sus devine:
1 protected void Page_Load(object sender, EventArgs e)
2 {
3 Master.BannerText = "Content Page #1";
4 }
Spărgând ı̂ncapsularea, se poate accesa direct conţinutul din masterpage:
1 Label lbl = Master.FindControl("lblTitleContent") as Label;
2 if (lbl != null)
3 {
4 lbl.Text = "hello from content page;
5 }
Dacă se doreşte modificarea de master page dinamic, se poate, prin
acţionarea asupra proprietăţii Page.MasterPageFile ı̂n cadrul evenimen-
tului Page.Init. Mecansimul e util dacă se doreşte adaptarea de master
page la tipul de vizitator, sau se doreşte a se face co-branding.

5.4 Navigare
Va urma...
Capitolul 6

Legarea la date

Legarea la date1 reprezintă capacitatea unui control ASP.NET de a-şi


prelua conţinutul din diverse surse: baze de date clasice, proprietăţi, metode,
servicii web etc. După ce conţinutul este preluat, e afişat conform specificului
controlului.
Un rol la fel de important ı̂l au sursele de date, controale prin intermediul
cărora se determină care este provenienţa datelor; ele acţionează ca o punte
de legătură ı̂ntre datele afişate şi controalele ASP.NET; suplimentar, mai
permit şi specificarea modului de manipulare a lor: datele se sterg, se adaugă,
se modifică. Este remarcabilă flexibilitatea de utilizare a lor, permiţând
specificarea legăturii ı̂ntre controale şi date fie exclusiv declarativ (fără sau
cu foarte puţin cod pe parte de code–behind), fie prin cod, dacă se doreşte
personalizare.
Modul ı̂n care funcţionează acest mecanism este declarativ, folosind cod
scris ı̂n pagina aspx, acolo unde se precizează care e controlul de afişare. Se
separă partea de code–behind de cea de specificare a sursei de date.
Există două feluri de legări de date: pentru date simple (unice) şi pentru
date multiple. În primul caz este vorba de controale de tip etichetă, buton,
LinkButton, Image pentru care se poate specifica o singură valoare care va
fi folosită. Se folosesc aici expresiile de legare.
Alte controale suportă afişarea de mai multe valori – sau legarea cu valori
repetate. De exemplu, putem avea o listă de butoane radio, un tabel, sau
combobox-uri. Toate acestea expun o proprietate DataSource, care acceptă
un obiect de sursă de date. Când se specifică valoare pentru această propri-
etate (fie declarativ, adică ı̂n pagina aspx, fie prin code–behind), se creează
o legătură de la controlul de afişare la datele ce urmează a fi prezentate; mai
trebuie apelată doar metoda DataBind() la nivel de control sau de pagină
1
În original: data binding.

83
84 CAPITOLUL 6. LEGAREA LA DATE

pentru a se face efectiv popularea.

6.1 Legarea la date simple


Pentru controalele care suportă legarea la date simple – adică date care
au o singură valoare şi nu o mulţime de valori – se foloseşte o expresie de
legare. Expresia apare ı̂n partea de cod aspx; este delimitată de porţiunile
<%# şi %>:
1 <%# expresie %>
De exemplu, dacă avem o proprietate de tip string numită NumeUtilizator
ı̂n pagină, atunci se poate ca expresia de legare la date să fie:
1 <%# NumeUtilizator %>
Pentru evaluarea unei expresii de legare trebuie să se apeleze metoda
DataBind pentru pagină sau pentru controlul respectiv, altfel nu se va afişa
nicio valoare. Dacă este vorba de o metodă sau proprietate sau câmp din
clasa de code behind, pentru ca să se poată utiliza ı̂n expresie de legare
de date, e nevoie ca gradul de accesibilitate să fie public, protected sau
internal.
Următoarele exemple de expresii sunt valide:
1 <%# GetUserName() %>
2 <%# 1 + (2 * 20) %>
3 <%# "John " + "Smith" %>
4 <%# Request.Browser.Browser %>
O expresie de legare la date se poate scrie oriunde ı̂n pagină, dar de regulă
se precizează pentru o proprietate a unui control:
1 <asp:Image ID="image1" runat="server"
2 ImageUrl=’<%# FilePath %>’ />
3 <br />
4 <asp:Label ID="label1" runat="server"
5 Text=’<%# FilePath %>’ />
6 <br />
7 <asp:TextBox ID="textBox1" runat="server"
8 Text=’<%# GetFilePath() %>’ />
9 <br />
10 <asp:HyperLink ID="hyperLink1" runat="server"
11 NavigateUrl=’<%# LogoPath.Value %>’
6.1. LEGAREA LA DATE SIMPLE 85

12 Text="Show logo" />


13 <br />
14 <b><%# FilePath %></b>
15 <br />
16 <img src="<%# GetFilePath() %>">

Expresiile pot fi precizate chiar şi ı̂n afara formularului HTML.


Apelarea codului de legare la date – adică evaluarea efectivă a expresiilor
şi introducerea lor ı̂n codul HTML ce va fi trimis către browser se face printr-
un apel precum:

1 protected void Page_Load(object sender, System.EventArgs e)


2 {
3 this.DataBind();
4 }

Se poate apela metoda DataBind() şi ı̂n alte metode, nu doar ı̂n event han-
dlerul de ı̂ncărcare de pagină.
Un alt tip de expresie ce s–a introdus odată cu .NET Framework 2.0 este:

1 <%$ AppSettings:numeSetare %>

referindu–se la un nume de setare care se specifică ı̂n fişierul de configurare


ı̂n secţiunea appSettings:

1 <appSettings>
2 <add key="numeSetare" value="valoare din fisierul de configurare"/>
3 </appSettings>

Un exemplu de utilizare este:

1 <asp:Literal Runat="server" Text="<%$ AppSettings:numeSetare %>" />

Există câteva diferenţe ı̂ntre această expresie de legare de date şi cea
precedentă:

• Nu e nevoie să se apeleze metoda DataBind() pentru evaluarea expre-


siei, aceasta din urmă făcându–se automat;

• Expresiile cu $ nu se pot insera oriunde ı̂n pagină, spre deosebire de


cele cu #; ele se aplică numai controalelor pentru a specifica o valoare
de proprietate; dacă se doreşte doar să se afişeze valoarea expresiei, se
va folosi un control de tip Literal, precum s–a exemplificat mai sus;
86 CAPITOLUL 6. LEGAREA LA DATE

• prima parte a expresiei cu $ indică numele entităţii care e folosită pen-


tru evaluare; avem la dispoziţie AppSettings, ConnectionStrings sau
Resources.

Expresia este des utilizată pentru referirea la un string de conexiune pentru


o sursă de date:

1 <asp:SqlDataSource
2 ConnectionString="<%$ ConnectionStrings:Northwind %>" ... />

6.2 Legarea la date multiple


Sunt cazuri ı̂n care un control este conceput nu pentru afişarea unei date
simple, ci a unei ı̂ntregi colecţii; colecţia poate să fie un vector de elemente,
set de ı̂nregistrări din baza de date etc. Controalele care pot afişa colecţii de
elemente sunt:

• HtmlSelect, ListBox, DropDownList

• CheckBoxList şi RadioButtonList

• BulletedList care creează o listă cu buline sau numerotată

Fiecare va afişa o singură valoare care provine dintr-o proprietate a unui


element ce se afişează. Proprietăţile care se specifică la nivelul controalor de
afişare sunt:

• DataSource se referă la un obiect de date care conţine o colecţie de


date ce se vor afişate;

• DataSourceID reprezintă identificatorul unui control de tip sursă de


date; se specifică fie valoare pentru ea, fie pentru DataSource;

• DataTextField reprezintă numele proprietăţii din fiecare element care


se vrea a fi afişat, ce va apărea vizibil ı̂n pagina HTML;

• DataTextFormatString specifică modul de formatare pentru ceea ce


se va afişa;

• DataValueField - reprezintă un nume de proprietate, a cărui valoare


se evaluează pentru fiecare element care va fi afişat; deosebirea faţă
de DataTextField este că valoarea nu se afşează ı̂n browser, ci este
transmisă prin formular; de regulă, reprezintă o cheie de identificare a
fiecărui element.
6.2. LEGAREA LA DATE MULTIPLE 87

Exemplu: setăm o colecţie de perechi de forma cheie-valoare, stocate ı̂ntr–


un dicţionar; se face popularea pentru diferite controale de tip multivaloare.

1 <form id="form1" runat="server">


2 <div>
3 <select id="Select1" name="Select1" runat="server"
4 datatextfield="Key" datavaluefield="value">
5 <option></option>
6 </select>
7 <select id="Select2" multiple="true" name="Select2"
8 runat="server" datatextfield="Key"
9 datavaluefield="Value">
10 <option></option>
11 </select>
12 <asp:ListBox ID="ListBox1" runat="server"
13 DataTextField="Key" DataValueField="Value">
14 </asp:ListBox>
15 <asp:DropDownList ID="DropDownList1" runat="server"
16 DataTextField="Key" DataValueField="Value">
17 </asp:DropDownList>
18 <asp:CheckBoxList ID="CheckBoxList1" runat="server"
19 DataTextField="Key" DataValueField="Value">
20 </asp:CheckBoxList>
21 <asp:RadioButtonList ID="RadioButtonList1" runat="server"
22 DataTextField="Key" DataValueField="Value">
23 </asp:RadioButtonList>
24 </div>
25 </form>

Pe partea de code behind avem:

1 Dictionary<string, string> myDictionary = new Dictionary<string, string>();


2 myDictionary.Add("key1", "aa");
3 myDictionary.Add("key2", "ab");
4 myDictionary.Add("key3", "ac");
5
6 Select1.DataSource = myDictionary;
7 Select2.DataSource = myDictionary;
8 ListBox1.DataSource = myDictionary;
9 DropDownList1.DataSource = myDictionary;
10 CheckBoxList1.DataSource = myDictionary;
11 RadioButtonList1.DataSource = myDictionary;
88 CAPITOLUL 6. LEGAREA LA DATE

12
13 Page.DataBind();

Efectul este cel din figura 6.2.

Determinarea pe partea de code-behind a elementelor selectate din con-


troale se face folosind proprietatea SelectedIndex pentru fiecare control ı̂n
parte.
Legarea la date multiple poate avea ca sursă orice tip de date care imple-
mentează interfaţa ICollection, exemplele notabile fiind: colecţii ArrayList,
Hashtable, Collection şi clase din ADO.NET DataReader sau DataView.

6.3 Controale de date “bogate”


Pe lângă listele pomenite mai există posibilitatea de a folosi nişte con-
troale de date de tip tabelar, anume introduse pentru afişarea de date. Ele
permit afişarea mai multor proprietăţi din fiecare element, de exemplu lista
tuturor valorilor câmpurilor dintr–o ı̂nregistrare. Controalele sunt:

• GridView: folosite pentru a arăta tabele mari cu date; permit paginare,


selectare, editare, ştergere de date;

• DetailsView: arată un singur element la un moment dat, ı̂ntr-o tabelă


cu o linie pentru fiecare câmp afişat; permite editarea datelor;

• FormView: ca precedentul, dar permite definirea unor şabloane ce oferă


mai multă flexibilitate

Pentru un GridView, o declaraţie suficient de bună este:

1 <asp:GridView ID="grid" runat="server" AutoGenerateColumns="true" />


6.4. CONTROALE DE SURSE DE DATE 89

iar legarea de date se face prin specificarea unei valori adecvate pentru pro-
prietatea DataSource, ce se leagă la un obiect de tip DataReader sau de tip
DataTable.
Legarea la un obiect DataReader duce la un consum mic de memorie, dar
este ineficientă pentru multe scenarii, deoarece nu permite filtrare pe serverul
de aplicaţii, sau sortare. De asemenea, nu poate fi folosit pentru legare la mai
multe controale, simultan. Se poate folosi un set de date deconectat (clasa
DataSet), care ı̂n plus are şi avantajul de a fi agnostică relativ la tipul de
server de date folosit.
Aceste aspecte vor fi prezentate ı̂ntr–un curs următor.

6.4 Controale de surse de date


Reprezintă puntea de legătură ı̂ntre o coleţie de date şi controalele care le
afişează. Ele implementează interfaţa IDataSource. Sunt oferite următoarele:

• SqlDataSource: pentru legături la servere de baze de date sau la surse


de date precum ODBC;

• ObjectDataSource: pentru legături la clase;

• AccessDataSource: pentru baze de date Access (fişiere cu extensia


mdb);

• XmlDataSource: pentru legături către documente XML;

• SiteMapDataSource: pentru legături către fişier Web.sitemap care conţine


structura unui site.

6.5 Ciclul de viaţă al unei pagini Web


Evenimentele dintr-o pagină ASP.NET se succed astfel:

1. PreInit – se pot face următoarele: verificarea proprietăţii IsPostBack,


creare/recreare de controale dinamice, setarea paginii master, setarea
temei, setarea sau citirea valorilor din profil;

2. Init – se poate folosi pentru iniţializarea sau citirea proprietăţilor con-


troalelor;

3. InitComplete – pentru apelarea de cod care asigură efectuarea tuturor


iniţializărilor;
90 CAPITOLUL 6. LEGAREA LA DATE

4. PreLoad – se poate scrie cod care să se apeleze ı̂nainte de apelarea


metodei Load;

5. Load – apelează metoda OnLoad a fiecărui control din pagină, recursiv;


se pot seta proprietăţi şi stabili conexiuni la baza de date;

6. evenimente de control – precum apăsarea de buton, schimbarea inde-


xului activ dintr-un dropdownlist;

7. LoadComplete – cod care trebuie executat după ce s–a făcut iniţializarea


tuturor controalelor din pagină;

8. PreRender – după el se apelează metoda DataBind pentru fiecare con-


trol care are proprietatea DataSourceID setată; la acest eveniment se
specifică cod care trebuie executat pentru setări finale asupra controa-
lor din pagină;

9. SaveStateComplete – ı̂nainte de acest eveniment, ViewState-ul a fost


salvat pentru pagina curentă; orice schimbare de stare de la acest punct
este ignorată;

10. Render – se apelează metoda Render pentru fiecare control conţinut;

11. Unload – se apelează această metodă pentru fiecare control ı̂n parte;
ı̂n fiecare control această metodă poate fi folosită pentru a face dispo-
nibilizări de resurse (altceva decât memorie);

Legarea la date se petrece ı̂n următorii doi paşi:

1. controlul de sursă de date efectuează toate modificările cerute - update,


insert, delete. Fiecare din aceste trei modificări are câte două eveni-
mente asociate, procesate prin metode al căror nume se termină cu
“ing” şi “ed”: Inserting, Inserted; acest moment apare imediat după
rularea handlerelor de eveniment pentru controale;

2. se apelează interogările de date iar datele aduse sunt injectate ı̂n con-
troalele de afişare; aici se apelează metodele Selecting şi Selected; pasul
este efectuat imediat după evenimentul PreRender

Ultimul pas de mai sus poate aduce a redundanţă şi apel ne-necesar ı̂n
cazul ı̂n care nu s–a făcut nicio modificare pe bază de către utilizator; chiar
aşa şi este, iar acest aspect poate fi ı̂mbunătăţit prin folosirea mecanismelor
de caching.
6.6. SURSA DE DATE SQLDATASOURCE 91

6.6 Sursa de date SqlDataSource


Se declară ı̂n pagina aspx sub forma:
1 <asp:SqlDataSource ID="SqlDataSource1" runat="server" ... />
şi reprezintă o sursă de date care foloseşte furnizor de date ADO.NET. Se
pot deci folosi următoarele variante de furnizori:
• System.Data.SqlClient
• System.Data.OracleClient
• System.Data.OleDb
• System.Data.Odbc
Tipul de furnizor se specifică prin proprietatea ProviderName:
1 <asp:SqlDataSource ProviderName="System.Data.SqlClient" ... />

6.6.1 Selectarea de ı̂nregistrări


Pentru un control SqlDataSource trebuie să se specifice care este co-
manda ce aduce elementele selectate. Suplimentar, se pot seta comenzi
de ştergere, modificare, inserare. Proprietăţile se numesc: SelectCommand,
InsertCommand, UpdateCommand, DeleteCommand, toate de tip string. Valo-
rile lor pot fi fraze SQL sau denumiri de proceduri stocate aflate pe server.
Crearea unui control care aduce setul tuturor angajaţilor - a se arăta la
curs;
De aratat:
• crearea de comenzi parametrizate de tip select; alegerea valorii se face
dintr-un dropdownlist
• ca mai sus, cu alegerea din QueryString (2 pagini aspx); varianta: la
Selecting se seteaza valoarea parametrului din clauza where;
• update pe gridview; atenţie la numele de parametri;
• optimistic lock;
• stergere pentru tabela; de precizat rolul proprietatii DataKeyNames
pentru gridview; de procesat exceptie, de aratat pe o tabela nelegata
de altele;
• inserare folosind DetailsView
92 CAPITOLUL 6. LEGAREA LA DATE

6.6.2 Dezavantaje ale SqlDataSource


Deşi uşor de utilizat, există câteva probleme care fac folosirea lui SqlDataSource
neinspirată pentru proiecte de anvergură mare:

1. logica de acces la date este conţinută ı̂n pagina ASP.NET; este greu de
făcut calibrare pentru aceste interogări, profiling-ul e dificil;

2. mentenanţă dificilă: fiecare pagină care foloseşte legarea la date ar


trebui să aibe propriul set de date; reutilizarea este inexistentă; dacă
trebuie modificată o interogare scrisă ca şi cod SQL direct ı̂n pagină,
atunci trebuie schimbat peste tot la fel; problema are o ı̂mbunătăţire
uşoară (dar nu totală) prin folosirea de proceduri stocate, care duc ı̂nsă
la o altă problemă - scrierea logicii de business ı̂n codul SQL;

3. lipsa de flexibilitate - fiecare control care se populează ar avea nevoie


de propriul control de date; dacă pentru o pagină se prevăd mai multe
variante de afişare a datelor, atunci pagina se va umple rapiud de con-
troale de date;

4. dacă se iese din scenariul “interogare/update-delete-insert” pe aceeaşi


bază de date, codul devine incurcat şi greu de menţinut; o bibliotecă
de implementare a logicii aplicaţiei sau chiar şi numai de acces la date
simplifică enorm codul.

6.7 Sursa de date ObjectDataSource


Nu se foloseşte un furnizor de date SQL ca la cazul precedent, ci se face
conexiunea cu o clasă sau un obiect care implementează logica de furnizare a
datelor; poate fi o componentă de tip business logic, sau o bibliotecă de acces
la date care se bazează pe un data mapper care creează obiecte din domeniul
aplicaţiei – flexibilitate maximă.
Condiţii impuse de acest mod de lucru (dar restricţiile sunt suportabile):

• Pentru fiecare interogare, logica trebuie să fie integrată ı̂ntr–o singură


metodă a unei clase; asta favorizează modelul simplu de “transaction
script” sau crearea unei clase de tip “business façade”. lucru de altfe şi
recomandat;

• Trebuie să returneze rezultatul cerut sau să ducă la efectuarea respec-
tivei operaţii;
6.7. SURSA DE DATE OBJECTDATASOURCE 93

• Rezultatul unei interogări de tip select trebuie să fie o colecţie de


elemente, precum un DataSet, DataTable, DataView sau colecţie de
obiecte; fiecare astfel de obiect ar trebuie să ı̂şi expună valorile prin
proprietăţi publice;

• se pot folosi metode instanţe sau statice; ı̂n primul caz trebuie să existe
un constructor implicit pentru clasa care furnizează serviciul, ı̂n celălalt
nu se mai poate folosi polimorfismul;

• obiectul trebuie să fie fără stare.


94 CAPITOLUL 6. LEGAREA LA DATE
Capitolul 7

Controale de lucru cu datele

7.1 Controlul GridView


Paşi:

1. definirea coloanelor; tipuri de coloane:

(a) BoundField
(b) ButtonField
(c) CheckBoxField
(d) CommandField
(e) HyperLinkField
(f) ImageField
(g) TemplateField

2. ascunderea, schimbarea ordinii coloanelor - declarativ si din code-behind

3. proprietati: DataField, DataFormatString, HeaderText, FooterText,


HeaderImageUrl, ReadOnly, InsertVisible, Visible, SortExpression, Ht-
mlEncode, NullDisplayText, ConvertEmptyStringToNull

7.1.1 Formatare
Exemplu: {0:C} - parametrul este formatat sub forma de valoare mo-
netara, in functie de setarile serverului web sau de partea de globalizare;
{0:MM/dd/yy} - Month, day, year.

95
96 CAPITOLUL 7. CONTROALE DE LUCRU CU DATELE

7.1.2 Stiluri
Se seteaza stilurile CSS pentru:

• HeaderStyle

• RowStyle

• AlternatingRowStyle

• SelectedRowStyle

• EditRowStyle

• EmptyDataRowStyle

• FooterStyle

• PagerStyle

Stiluri: ForeColor, BackColor, BorderColor, BorderStyle, BorderWidth,


Height, Width, HorizontalAlign, VerticalAlign, Font, Wrap; alternativ: se
poate folosi proprietatea CssClass; se poate alege dintr-o serie de stiluri pre-
definite; demo: GridStyles.aspx
Formatare la momentul incarcarii, prin tratarea evenimentului RowDa-
taBound - demo: RuntimeFormatting.aspx

7.1.3 Selectarea de linie din GridView


Evenimente: SelectedIndexChanging, SelectedIndexChanged; demo:
SelectRow.aspx

7.1.4 Sortare
Demo pentru GridView cu SqlDataSource; demo pentru ObjectDataSo-
urce - GridViewObjectDataSourceSorting.aspx
Problema: la sortare indexul elementului curent selectat ar trebui pus pe
-1, deoarece prin sortare, indexul selectat este pus pe o inregistrare care se
gaseste pe pozitia selectata anterior; repunerea pe aceeasi inregistrare sortata
se poate face, dar trebuie mentinuta valoarea selectata in viewstate si apoi
parcurs intregul grid;
Important: proprietatea EnableSortingAndPagingCallbacks.
7.2. CONTROALELE FORMVIEW, DETAILSVIEW 97

7.1.5 Paginare
Demo; modul in care se face paginare in SQL Server 2005+, folosind
Common Table Expressions (CTE)

7.1.6 Template-uri
Demo:

• adaugare de campuri template; creare de coloana cu valoare compusa:


TemplateCampCompus.aspx

• template-uri de editare - se pot adauga validatoare; se pot alege campu-


rile care se editeaza; se poate modifica modul in care arata controlul de
editare - e.g. control dropdown; demo: EditTemplate1.aspx; data bin-
ding bidirectional = metoda Bind(); folosire de dropdownlist pentru
TitleOfCourtesy = EditTemplate2.aspx;

7.2 Controalele FormView, DetailsView


98 CAPITOLUL 7. CONTROALE DE LUCRU CU DATELE
Capitolul 8

JavaScript şi Ajax

Factorul esenţial ce facilitează comunicarea dintre client (browser) şi ser-


ver (codul ASP.NET) este trimiterea formularului dispre browser către ser-
ver, de regulă către aceeaşi pagină – postback. Evident, acest postback este
dependent de existenţa unui cod de reacţie pe server, cod care să producă o
modificare a stării paginii.
Există ı̂nsă destule situaţii ı̂n care reacţia poate avea loc doar pe client,
fără a fi nevoie să se facă trimiterea formularului către server. Se scuteşte ast-
fel o deplasare către şi dinspre server, ceea ce creează impresia unei interfeţe
utilizator care răspunde rapid clientului. De exemplu, expandarea elemente-
lor dintr-un meniu sau expandarea nodurilor dintr-un arbore, sau ascunderea
unor elemente de pe ecran (tabele, rânduri, imagini) se poate efectua de re-
gulă doar cu resurse locale – se acţionează asupra porţiuni din pagină. Toate
aceste efecte sunt obţinute prin utilizarea codului JavaScript care este exe-
cutat de către browserul clientului.
Cursul prezintă tehnici de integrare a codului JavaScript cu ASP.NET.

8.1 Elemente de bază JavaScript


JavaScript permite scrierea de cod client, executat de către browser.
JavaScript este văzut actualmente ca un dialect al ECMAScript1 ; este un
limbaj slab tipizat (variabilele nu ı̂şi declară tipurile de date decât arare-
ori), dinamic (permit modificarea codului şi a tipurilor de date, ı̂n timpul
execuţiei), interpretat. Codul JavaScript este inclus direct ı̂n documentul
HTML sau referit de către acesta; el e descărcat de către browser şi rulat.
Pentru a include cod JavaScript ı̂n pagina HTML, se poate proceda astfel:
1
Standard definit de Ecma International, standard aprobat şi de ISO/IEC
Standard ECMA-262, ECMAScript Language Specification.

99
100 CAPITOLUL 8. JAVASCRIPT ŞI AJAX

• se defineşte tratarea unui eveniment pentru un control HTML direct


sub forma codului care va fi executat;
• se adaugă un element <script> care conţine cod JavaScript. Elementul
conţine cod care se execută atunci când browserul ajunge să ı̂l ı̂ncarce
sau codul unei funcţii care poate fi apelată direct sau indirect ca parte
a procesării unui eveniment.
Acţionarea de către codul JavaScript asupra componentelor din pagină
se face prin HTML DOM (Document Object Model), adică DHTML. Eveni-
mentele care pot declanşa apelul unui cod JavaScript sunt:
• onchange - apare atunci când se schimbă starea unui control - selecta-
re/deselectare a unui checkbox, mutarea focusului din textboxul curent
pe alt control etc;
• onclick - apare atunci când se apasă click de mouse pe un control
• onmouseover - cand mouse-ul se miscă deasupra unui control
• onmouseout - cand mouse-ul se mişcă ı̂n afara controlului
• onkeydown, onkeyup - cand utilizatorul apasă/eliberează o tastă
• onfocus - atunci cand un control primeste focusul
• onload - apare atunci cand o pagină se termină de ı̂ncărcat
• onunload - apare atunci când o pagină este pe cale de a fi descărcată
din browser (chiar după ce s-a dat clic pe un link sau s-a introdus o
noua adresă pentru browser)
Aceste evenimente pot fi setate declarativ:
1 <form>
2 <input id="myText" type="text"
3 onmouseover="window.alert(’mouse over’);"/>
4 <input type="button"
5 onclick="window.confirm(’esti sigur(a)?’);"); />
6 </form>
În cazul ı̂n care este vorba de un buton definit ca Web server control,
numele onclick este deja utilizat pentru a face corespondenţa cu metoda din
code-behind care face procesarea evenimentului pe server; ca atare, numele
de eveniment de pe client se defineşte folosesind OnClientClick. Tratarea
evenimentelor poate fi specificată şi din code-behind, folosind proprietatea
Attributes; de exemplu:
8.2. BLOCURI DE SCRIPT 101

1 <form id="form1" runat="server">


2 <div>
3 <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
4 </div>
5
6 <asp:Button ID="Button1" runat="server" onclick="Button1_Click"
7 Text="Ataseaza eveniment JavaScript" />
8 </form>
9
10 protected void Button1_Click(object sender, EventArgs e)
11 {
12 TextBox1.Attributes.Add("onmouseover",
13 "window.alert(’Mouse plutind peste textbox’);");
14 }

8.2 Blocuri de script


Dacă bucata de cod JavaScript care se scrie pentru tratarea de eveniment
este de dimensiuni mai mari sau se reutilizează, atunci este mai practic ca
scrierea să se facă ı̂ntr–o secţiune dedicată, cuprinsă ı̂n interiorul elementului
<script>. Elementul preia un atribut prin care se specifică limbajul ı̂n care
sunt scrise instrucţiunile. Dacă browserul nu acceptă scripturi JavaScript,
atunci această secţiune este ignorată:

1 <script type="text/javascript">
2 <!--
3 var i = 7;
4 window.alert(’Mesaj afisat cand browserul ajunge
5 cu incarcarea in acest loc’);
6 //-->
7 </script>

Comentariile sunt necesare pentru browserele care nu inteleg codul JavaScript,


iar comentariul // era util pentru cazul in care se folosea anumite versiuni de
Netscape. Cele mai multe browsere actuale ı̂nteleg codul JavaScript, chiar
dacă unele sunt setate să nu ı̂l execute.
După cum dă de ı̂nţeles codul de mai sus, se declară o variabilă numită i
căreia i se dă valoarea 7; ea va fi vizibilă ı̂n ı̂ntreaga pagină. Apoi browserul
execută instrucţiunea de afişare a unei ferestre de mesaj. Comportamentul
este util pentru cazul ı̂n care la o anumită porţiune din pagina html este
nevoie să se execute un anumit cod.
102 CAPITOLUL 8. JAVASCRIPT ŞI AJAX

Dacă se doreşte ca un bloc de instrucţiuni să nu fie executat de ı̂ndată ce


browserul ı̂l ı̂ncarcă, se poate folosi o funcţie definită de programator:

1 <script type="text/javascript">
2 function showMessage(message){
3 window.alert(message);
4 }
5 </script>

Un loc des folosit pentru scrierea de funcţii este secţiunea <head> a paginii
web, ceea ce permite o regăsire mai uşoară. Apelarea funcţiei definite anterior
este posibilă prin:

1 <asp:TextBox ID="TextBox1" runat="server"


2 onmouseover="showMessage(’mouse peste textbox’);">
3 </asp:TextBox>

iar efectul este redat ı̂n figura 8.2.

Figura 8.1: Rezultatul apelării funcţiei JavaScript.

Se observă că pentru o funcţie JavaScript nu se declară tipul de retur. O


variantă des folosită este scrierea codului JavaScript ı̂ntr-un fişier cu extensia
consacrată js care este ı̂ncărcat de către browser. Adresa fişierului este dată
sub prin atributul src al elementrului script:

1 <script type="text/javascript" src="scripts/myCode.js">


2 </script>
8.3. MANIPULAREA ELEMENTELOR HTML 103

8.3 Manipularea elementelor HTML


Schimbarea conţinutului paginii se face prin identificarea elementului/e-
lementelor de pagină ce trebuie schimbat/schimbate; ı̂n cazul unui element al
cărui id se cunoaşte, fiind specificat ca valoare a atributului id, identificarea
se face prin intermediul metodei document.getElementById():
1 var myElement = document.getElementById(’textbox1’);
Dacă este vorba de regăsirea unei familii de elemente ı̂nrudite, se poate fo-
losi document.getElementsByTagName sau document.getElementsByName.
Odată recuperate elementele de interes, se schimbă starea lor tot prin instrucţiuni
JavaScript. Proprietăţile uzuale sunt:
• innerHTML - reprezintă conţinutul HTML dintre eticheta de deschidere
şi de ı̂nchidere;
• style - permite referirea elementelor de stil din elementul curent; de
exemplu, myElement.style.fontSize se foloseşte pentru schimbarea
dimensiunii fontului unui element;
• value - reprezintă starea curentă a controlului; de exemplu, starea unui
checkbox sau textul dintr-o căsuţă de text;
• parentElement - dă acces la elementul părinte; util pentru a muta sau
şterge elementul curent;

8.4 Utilizare de JavaScript pentru ı̂ncărcare asin-


cronă a paginilor
Următorul exemplu este preluat din [2]. Să presupunem că se doreşte
ı̂ncărcarea ı̂ntr-un gridview a unor detalii legate de o listă de cărţi (nume,
editură, ISBN) şi a pozei lor. Randarea ı̂ntregii tabele de către browser poate
să ia mult timp dacă sunt multe date sau dacă pozele se ı̂ncarcă greu; se poate
folosi un truc care păcăleşte răbdarea persoanei care aşteaptă ı̂ncărcarea pa-
ginii: ca poză se ı̂ncarcă o imagine neutră, pe post de placeholder; deoarece
este una singură pentru tot gridul, o dată ı̂ncărcată pentru o celulă, devine
instantaneu afişabilă pentru toate celelate celule (facilitate oferită de brow-
ser). Ulterior, pentru fiecare carte se cere de către browser imaginea efectivă,
prin cod JavaScript. Timpul necesar pentru ı̂ncărcarea completă a gridului
cu tot cu imaginile respective nu scade, dar utilizatorul poate să ı̂nceapă să
citească pagina şi să defileze pe ecran.
Gridul poate să fie declarat astfel:
104 CAPITOLUL 8. JAVASCRIPT ŞI AJAX

1 <asp:GridView id="GridView1" runat="server"


2 AutoGenerateColumns="False">
3 <Columns>
4 <asp:BoundField DataField="Title" HeaderText="Title"/>
5 <asp:BoundField DataField="isbn" HeaderText="ISBN"/>
6 <asp:BoundField DataField="Publisher" HeaderText="Publisher"/>
7 <asp:TemplateField>
8 <HeaderTemplate>
9 Book Cover
10 </HeaderTemplate>
11 <ItemTemplate>
12 <img src="UnknownBook.gif"
13 onerror="this.src=’Unknownbook.gif’;"
14 onload=
15 "GetBookImage(this, ’<%# DataBinder.Eval(
16 Container.DataItem, "isbn") %>’);"
17 </ItemTemplate>
18 </asp:TemplateField>
19 </Columns>
20 </asp:GridView>
Accentul cade pe ultimul câmp de pe fiecare linie, care este ı̂n primă fază o
referinţă către o imagine neutră, aflată ı̂n aplicaţia web, iar când se termină
de ı̂ncărcat (lucru de altminteri rapid), se face apel de funcţie JavaScript care
va cere ı̂ncărcarea imaginii aferente fiecărui ISBN. Această a doua ı̂ncărcare
poate fi lentă, motivul pentru care se foloseşte mecanismul prezentat.
Funcţia GetBookImage este:
1 <script language="javascript" type="text/javascript">
2 function GetBookImage(img, url)
3 {
4 // Detach the event handler (the code makes just one attempt
5 // to get the picture).
6 img.onload = null;
7 // Try to get the picture from the GetBookImage.aspx page.
8 img.src = ’GetBookImage.aspx?isbn=’ + url;
9 }
10 </script>
Pagina GetBookImage.aspx (se putea folosi şi un manipulator de HTTP,
adică cod ashx) este cea care face ı̂ncărcarea imaginii respective, fie dintr-un
serviciu extern aplicaţiei web curente, fie din baza de date. Etapele ı̂ncărcării
sunt date ı̂n figurile 8.2 şi 8.3.
8.4. UTILIZARE DE JAVASCRIPT PENTRU ÎNCĂRCARE ASINCRONĂ A PAGINILOR105

Figura 8.2: Apariţia iniţială a paginii [2]

Figura 8.3: Încărcarea imaginilor ı̂n pagină [2]


106 CAPITOLUL 8. JAVASCRIPT ŞI AJAX

8.5 Includerea blocurilor JavaScript din code-


behind
Uneori este mai flexibil ca includerea de cod JavaScript să se facă la ru-
lare, dacă se ı̂ndeplinesc anumite condţii. Pentru aceasta se poate folosi pro-
prietatea Page.ClientScript care expune câteva metode pentru gestiunea
blocurilor de script. Metodele sunt:
1. RegisterClientScriptBlock() - scrie un bloc de script la ı̂nceputul
formularului web, chiar după <form runat="server">;
2. RegisterStartupScript() - scrie un bloc la sfârşitul formularului web,
chiar ı̂nainte de </form>.
Ca un argument al funcţiilor se specifică un nume de script; acesta trebuie
să fie un string unic, rolul lui este de a se asigura că nu se adaugă acelaşi
script de două ori. De exemplu, pentru includerea repetată de controale care
folosesc JavaScript, mecanismul duce la inserarea doar a unui singur script.
De exemplu, codul de mai jos produce includerea unui script care afişează
o fereastra de confirmare la trimiterea formularului - vezi şi figura 8.4:
1 protected void Page_Load(object sender, EventArgs e)
2 {
3 string script = @"
4 <script type=’text/javascript’>
5 function myConfirm()
6 {
7 return confirm(’Vrei trimiterea datelor?’);
8 }
9 </script>
10 ";
11 Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
12 "confirmare", script);
13 Page.Form.Attributes.Add("onsubmit", "return myConfirm();");
14 }
Mecanismul este util ı̂n situaţia ı̂n care există controale utilizator care folosesc
JavaScript.
O altă variantă este includerea unui bloc de script care face referinţă către
un fişier JavaScript:
1 string includeScript = @"scripts/myscript.js";
2 Page.ClientScript.RegisterClientScriptInclude("myInclude",
3 includeScript);
8.6. ATACURI DE INJECTARE DE SCRIPT 107

Figura 8.4: Executarea de cod JavaScript adaugat prin inregistrare de bloc


de script

care duce la includerea ı̂n codul HTML, ı̂n interiorul formularului, a codului:

1 <script src="scripts/myscript.js" type="text/javascript">


2 </script>

8.6 Atacuri de injectare de script


Unul din fenomenele la care este vulnerabilă o pagină web este ataşarea
de cod JavaScript de către utilizator. De exemplu, ı̂n interiorul unui textbox
se poate scrie cod JavaScript, ceea ce duce la executarea lui pe client; sau
folosirea de elemente HTML ı̂n text poate duce la modificarea afişării ı̂n
pagină. Dacă datele sunt preluate pur şi simplu de la utilizator şi introduse
ı̂n baza de date, la reafişarea lor pentru un alt utilizator, executând–se pe
client, pot afecta pe toţi ceilalţi utilizatori.
Aceste script-uri se pot trimite via elementele <script>, <object>, <applet>,
<embed>. O variantă simplă de rezovare este de a face codificarea caracterelor
folosind metoda Server.HtmlEncode.
Implicit, ASP.NET poate să determine dacă conţinutul unei căsuţe de
text are potenţial de injectare de script. Pentru o pagină cu un textbox şi
un buton ce produce postback - figura 8.5, comportamentul dictat de server
este cel dat ı̂n figura 8.6 - adică framework–ul ASP.NET depistează automat
introducerea de elemente HTML şi reacţionează prin respingere:
Se poate inhiba comportamentul de respingere al ASP.NET-ului, prin
specificarea proprietăţii ValidateRequest din cadrul directivei Page, de la
ı̂nceputul paginii aspx:

1 <%@ Page ValidateRequest="false"...


108 CAPITOLUL 8. JAVASCRIPT ŞI AJAX

Figura 8.5: Încercare de trimitere de cod JavaScript prin textbox

Figura 8.6: Respingere de către ASP.NET

În această situaţie, reafişarea naivă a stringului din textbox ı̂ntr-un label
duce la injectarea unui cod JavaScript ı̂n pagină, cod care va fi executat de
către browser - vezi figura 8.7.
1 protected void Page_Load(object sender, EventArgs e)
2 {
3 myLabel.Text = TextBox1.Text;
4 }

Figura 8.7: Inhibarea respingerii de cod JavaScript

Validarea stringurilor trimise de către utilizator poate fi inhibată pentru


ı̂ntregul site, prin specificarea ı̂n fişierul web.config a elementului pages, cu
8.7. JAVASCRIPT PENTRU CONTROALE UTILIZATOR 109

atributul ValidateRequests având valoarea false:

1 <configuration>
2 <system.web>
3 <pages validateRequest="false" />
4 </system.web>
5 </configuration>

Pentru a preveni executarea de cod JavaScript pentru pagini care au speci-


ficată inhibarea validării cererilor, se poate folosi metoda de codificare a strin-
gurilor HTML dată de Server.HtmlEncode sau de HttpUtility.HtmlEncode:

1 protected void Page_Load(object sender, EventArgs e)


2 {
3 myLabel.Text = Server.HtmlEncode(TextBox1.Text);
4 }

modalitate care de fapt se recomandă ori de câte ori se face afişare de text
preluat dintr-o sursă de date oarecare.

8.7 JavaScript pentru controale utilizator


În cazul dezvoltării de controale utilizator, este indicat ca produsul reali-
zat să poată fi folosit ca un modul ı̂n pagină, fără a fi nevoie să se cunoască
detaliile de implementare; implicit, toată partea de cod JavaScript necesară
funcţionării controlului trebuie să fie ascunsă ı̂n control. Exemplificarea ce
urmează este preluată din [2].
Să peresupunem că se vrea crearea de ferestre popup pentru incarcarea
diferitelor mesaje (reclame sau ajutor contextual). Acest lucru se face prin
JavaScript, pe baza codului:

1 <script type="text/javascript">
2 window.open(’http://www.apress.com’, ’myWindow’,
3 ’toolbar=0, height=500, width=800, resizable=1, scrollbars=1’);
4 window.focus();
5 </script>

Metoda JavaScript Open primeşte ca prim parametru adresa paginii care


trebuie deschisă, numele ferestrei care se deschide – util dacă se doreşte re-
utilizarea ferestrei popup, pentru incarcarea de conţinut nou – apoi un şir
de perechi de forma nume=valoare, specificând diferite aspecte vizuale ale
ferestrei de popup: dimensiuni, dacă este sau nu redimensionabilă, dacă să
110 CAPITOLUL 8. JAVASCRIPT ŞI AJAX

arate sau nu barele de derulare, sau dacă se afişează ı̂n prim plan sau ı̂n
fundal.
Putem crea un control ascx care să poată fi inclus ı̂n orice pagină; as-
pectele vizuale pot fi setate prin proprietăţi. Controlul va putea fi utilizat
ı̂ntr–o pagină web precum cele uzuale - etichete, căsuţe de text etc. Avanta-
jul controlului este că permite reutilizarea uşoară a codului, ı̂ntr-o manieră
declarativă.
Codul pentru pagina de popup (controlul ascx) este:

1 using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text;

namespace DemoCurs8
{
public partial class CustomPopup : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
if (Page.Request.Browser.EcmaScriptVersion.Major >= 1)
{
StringBuilder javaScriptString = new StringBuilder();
javaScriptString.Append("<script type=’text/javascript’>");
javaScriptString.Append("\n<!-- ");
javaScriptString.Append("\nvar popupWindow" + ID +
"= window.open(’");
javaScriptString.Append(Url + "’, ’" + ID);
javaScriptString.Append("’,’toolbar=0,");
javaScriptString.Append("height=" + WindowHeight + ",");
javaScriptString.Append("width=" + WindowWidth + ",");
javaScriptString.Append("resizable=" +
Convert.ToInt16(Resizable).ToString() + ",");
javaScriptString.Append("scrollbars=" +
Convert.ToInt16(ScrollBars).ToString());
javaScriptString.Append("’);\n");
if (PopUnder) javaScriptString.Append("popupWindow" + ID +
".blur(); ");
8.7. JAVASCRIPT PENTRU CONTROALE UTILIZATOR 111

javaScriptString.Append("\n-->\n");
javaScriptString.Append("</script>\n");
Literal1.Text = javaScriptString.ToString();
}
}

public bool PopUnder


{
get
{
return (bool)ViewState["PopUnder"];
}
set
{
ViewState["PopUnder"] = value;
}
}

public string Url


{
get
{
return (string)ViewState["Url"];
}
set
{
ViewState["Url"] = value;
}
}

public int WindowHeight


{
get
{
return (int)ViewState["WindowHeight"];
}
set
{
if (value < 1)
{
throw new ArgumentException(@"WindowHeight must be
112 CAPITOLUL 8. JAVASCRIPT ŞI AJAX

greater than 0");


}
ViewState["WindowHeight"] = value;
}
}

public int WindowWidth


{
get
{
return (int)ViewState["WindowWidth"];
}
set
{
if (value < 1)
{
throw new ArgumentException(@"WindowWidth must be
greater than 0");
}
ViewState["WindowWidth"] = value;
}
}

public bool Resizable


{
get
{
return (bool)ViewState["Resizable"];
}
set
{
ViewState["Resizable"] = value;
}
}

public bool ScrollBars


{
get
{
return (bool)ViewState["ScrollBars"];
}
8.7. JAVASCRIPT PENTRU CONTROALE UTILIZATOR 113

set
{
ViewState["ScrollBars"] = value;
}
}

public CustomPopup()
{
PopUnder = true;
Url = "about:blank";
WindowHeight = 300;
WindowWidth = 300;
Resizable = false;
ScrollBars = false;
}
}
}
O pagină aspx care utilizează controlul ar fi:
1 <%@ Page Language="C#" AutoEventWireup="true"
2 CodeBehind="UseCustomPopup.aspx.cs"
3 Inherits="DemoCurs8.UseCustomPopup" %>
4
5 <%@ Register src="CustomPopup.ascx" tagname="CustomPopup" tagprefix="uc1" %>
6
7 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
8 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
9
10 <html xmlns="http://www.w3.org/1999/xhtml" >
11 <head runat="server">
12 <title></title>
13 </head>
14 <body>
15 <form id="form1" runat="server">
16 <div>
17 <uc1:CustomPopup ID="myCustomPopup" runat="server" PopUnder="true"
18 Url="http://localhost/index1.html" Resizable="true"/>
19 <uc1:CustomPopup ID="CustomPopup1" runat="server" PopUnder="false"
20 Url="http://localhost/index2.html" Resizable="true"/>
21 </div>
22 </form>
114 CAPITOLUL 8. JAVASCRIPT ŞI AJAX

23 </body>
24 </html>

O altă variantă de implementare este cea ı̂n care controlul se dezvoltă ca


un control server particularizat2 , aşa cum se arată ı̂n [2], capitolele 27 şi 31.

8.8 Ajax
Până acum, orice cerere de date dinspre server ı̂nseamnă trimiterea for-
mularului către server şi reafişarea paginii. Ca efect, se observă activitatea
sacadată "click şi aşteaptă", precum şi faptul că pagina este reı̂ncărcată, cu
saltul la ı̂nceputul ei. Deşi pentru ASP.NET există posibilitatea de a spe-
cifica atributul MaintainScrollPositionOnPostback cu valoarea true ı̂n
cazul directivei de pagină, comportamentul vizual este acceptabil pe Inter-
net Explorer, dar nesatisfăcător pe Mozilla Firefox.
Ajax = Asynchronous JavaScript and XML este un grup de tehnici web
inter-relaţionate care permit dezvoltarea de aplicaţii web interactive sau cu
un aspect asemănător cu cel al aplicaţiilor ce rulează folosind doar resurse
locale. Prin Ajax se pot aduce date de la server, ı̂n mod asincron, fără a fi
necesară trimiterea propriu-zisă a formularului către server; pagina rămâne
ı̂ncărcată ı̂n browser, dar anumite porţiuni din ea suferă modificări pe baza
conţinutului adus de la server.
JavaScript este de ajutor aici deoarece, presupunând problema preluării
asincrone a datelor rezolvată, permite identificarea fragmentelor de docu-
ment care trebuie să fie modificate şi permite schimbarea lor. Totuşi, doar
JavaScript nu este suficient, deoarece el se descurcă cu resurse locale, ı̂n timp
ce pentru anumite acţiuni utilizator este nevoie de preluare de date pe care
doar un server le poate furniza. Paginile care utilizează Ajax comunică ı̂n
fundal cu serverul; de ı̂ndată ce răspunsul este primit ı̂n ı̂ntregime, se efec-
tuează activităţi adiţionale. Din cauza caracterului asincron al comunicării,
pagina nu este ı̂ntreruptă, iar utilizatorul poate să efectueze şi alte acţiuni
ı̂n ea. Răspunsul poate fi furnizat sub forma unui document XML sau ca
un simplu text. Este responsabilitatea codului Ajax scris de programator să
parcurgă răspunsul şi să acţioneze ı̂n consecinţă.
Un aspect neplăcut al programării cu Ajax este faptul că există diferenţe
mari ı̂ntre browsere, ı̂ncepând chiar de la formularea cererii. Există biblioteci
cross-browser care pot fi folosite ı̂n acest sens.

2
În original: custom server control.
8.8. AJAX 115

8.8.1 Obiectul XMLHttpRequest


Pentru a face comunicarea cu serverul dinspre pagina web, trebuie formu-
lată o cerere către server iar răspunsul trebuie să fie preluat. Ambele acţiuni
sunt efectuate de obiectul XMLHttpRequest. Obţinerea lui se face diferenţiat,
după browser: Internet Explorer 5 sau 6 foloseşte ActiveX, iar IE7 şi Mozilla
Firefox folosesc un obiect JavaScript nativ. Ca atare, codul de instanţiere
a obiectului XMLHttpRequest trebuie să fie scris luând ı̂n considerare toate
variantele, deoarece nu se poate şti ce browser este folosit de către client.
Codul este:

1 var xmlRequest;
2 try
3 {
4 // Asta merge daca XMLHttpRequest face parte din JavaScript.
5 xmlRequest = new XMLHttpRequest();
6 }
7 catch(err)
8 {
9 // altfel, se cere serviciu de la ActiveX
10 xmlRequest = new ActiveXObject("Microsoft.XMLHTTP");
11 }
12 //in acest punct obiectul XMLHttpRequest este instantiat

8.8.2 Trimiterea cererii


Pentru trimiterea unei cereri se folosesc metodele open() şi send(). Prima
metodă setează datele de apel - adresa la care se face apelul şi tipul de co-
mandă HTTP (GET, POST, PUT). Exemplu:

1 xmlRequest.open("GET" , myURL);

URL-ul apelat poate să conţină un query string cu perechi de atribute şi va-
lori; ceea ce se află la adresa referită trebuie să poată prelua aceşti parametri
şi să formuleze răspunsul ı̂n consecinţă.
Se mai poate da un parametru opţional care specifică dacă cererea tre-
buie să fie trimisă asincron (implicit) şi ı̂ncă doi care dau numele şi parola
utilizatorului (nerecomandat).
Metoda send() trimite efectiv comanda către server:

1 xmlRequest.send(null);
116 CAPITOLUL 8. JAVASCRIPT ŞI AJAX

8.8.3 Procesarea răspunsului


Răspunsul se procesează prin intermediul unui manipulator de eveniment
care este apelat atunci când soseşte răspunsul către browser. Manipulatorul
este o funcţie JavaScript care se apelează de către browser:

1 xmlRequest.onreadystatechange = UpdatePage;

Setarea event handlerului se face ı̂nainte de trimiterea cererii către server


(ı̂naintea apelării lui send).
Atunci când răspunsul este primit, el poate fi accesat prin intermediul pro-
prietăcţilor responseText şi responseXML, proprietăţi ale obiectului XMLHttpRequest.
Prima proprietate dă răspunsul sub forma unui şir de caractere; a doua me-
todă preia documentul ca un arbore de noduri. Se poate folosi şi altceva
decât XML pentru crearea unui răspuns structurat, de exemplu JSON3 sau
cod HTML gata preformatat.

8.9 Un exemplu Ajax


Pentru crearea unei pagini ce foloseşte Ajax e nevoie de:

• pagina Web care să folosească Ajax; nu e obligatoriu să fie formă web
(pagină aspx), poate fi o pagină HTML obişnuită;

• O altă pagină (aspx) sau manipulator de HTTP (cod ashx) care să
primească cererea şi să furnizeze un răspuns. Pagina sau handlerul
preia eventualii parametrii transmişi ca parte a adresei şi formulează
un răspuns corespunzător.

În exemplul de mai jos răspunsul este furnizat de către un HTTP handler;
acesta preia din query string două valori, furnizate pentru atributele value1
şi respectiv value2 şi returnează suma numerelor (dacă se poate face suma);
se returnează suma lor ı̂mpreună cu data şi timpul de pe server la care s–a
efectuat calculul (cele două valori - suma şi momentul sunt despărţite prin
virgulă).
Codul pentru handlerul de HTTP care primeşte cererea este:

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Web;
3
JavaScript Object Notation.
8.9. UN EXEMPLU AJAX 117

5 using System.Web.Services;
6
7 namespace DemoCurs8
8 {
9 /// <summary>
10 /// Summary description for $codebehindclassname$
11 /// </summary>
12 [WebService(Namespace = "http://tempuri.org/")]
13 [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
14 public class HandlerCalculator : IHttpHandler
15 {
16
17 public void ProcessRequest(HttpContext context)
18 {
19 HttpResponse response = context.Response;
20 HttpRequest request = context.Request;
21 //tipul de raspuns este text simplu
22 response.ContentType = "text/plain";
23 int value1, value2;
24 if (
25 int.TryParse(request.QueryString["value1"], out value1) &&
26 int.TryParse(request.QueryString["value2"], out value2)
27 )
28 {
29 int sum = value1 + value2;
30 response.Write(sum.ToString());
31 response.Write(",");
32 response.Write(DateTime.Now.ToString());
33 }
34 else
35 {
36 response.Write("-");
37 }
38 }
39
40 public bool IsReusable
41 {
42 get
43 {
44 return true;
45 }
118 CAPITOLUL 8. JAVASCRIPT ŞI AJAX

46 }
47 }
48 }
Pentru pagina web care folosesşte Ajax se scrie codul:
1 <%@ Page Language="C#" AutoEventWireup="true"
2 CodeBehind="AjaxPage.aspx.cs" Inherits="DemoCurs8.AjaxPage" %>
3
4 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
5 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
6 <html xmlns="http://www.w3.org/1999/xhtml">
7 <head runat="server">
8 <title>Pagina care foloseste Ajax</title>
9
10 <script type="text/javascript">
11 var xmlRequest;
12 function CreateXMLHttpRequest() {
13 try {
14 // Asta merge daca XMLHttpRequest face parte din JavaScript.
15 xmlRequest = new XMLHttpRequest();
16 }
17 catch (err) {
18 // altfel, se cere serviciu de la ActiveX
19 xmlRequest = new ActiveXObject("Microsoft.XMLHTTP");
20 }
21 //in acest punct obiectul XMLHttpRequest este instantiat
22 }
23
24 function sendRequest()
25 {
26 xmlRequest.open("GET", "HandlerCalculator.ashx?value1=" +
27 document.getElementById(’TextBoxValue1’).value +
28 "&value2=" + document.getElementById(’TextBoxValue2’).value);
29 xmlRequest.onreadystatechange = UpdatePage;
30 xmlRequest.send(null);
31 }
32
33 function UpdatePage()
34 {
35 if (xmlRequest.readyState == 4)//primire completa de raspuns
36 {
8.9. UN EXEMPLU AJAX 119

37 if (xmlRequest.status == 200)//codul http este de tip "OK"


38 {
39 var response = xmlRequest.responseText;
40 var lblResult = document.getElementById(’lblResult’);
41 if (response == ’-’)
42 {
43 lblResult.innerHTML = ’Nu se poate calcula suma’;
44 }
45 else
46 {
47 var tokens = response.split(’,’);
48 lblResult.innerHTML = ’Suma este: ’ + tokens[0] +
49 ’<br />Calculata la: ’ + tokens[1];
50 }
51 }
52 }
53 }
54 </script>
55
56 </head>
57 <body onload="CreateXMLHttpRequest();">
58 <form id="form1" runat="server">
59 <div>
60 <img src="images/anilion1_e0.gif" alt="leu" /><br />
61 Primul numar:
62 <asp:TextBox ID="TextBoxValue1" runat="server"
63 onkeyup="sendRequest();"></asp:TextBox>
64 <br />
65 Al doilea numar:
66 <asp:TextBox ID="TextBoxValue2" runat="server"
67 onkeyup="sendRequest();"></asp:TextBox>
68 <br />
69 <asp:Label ID="lblResult" runat="server"></asp:Label>
70 </div>
71 </form>
72 </body>
73 </html>
120 CAPITOLUL 8. JAVASCRIPT ŞI AJAX
Capitolul 9

ASP.NET membership

ASP.NET pune la dispoziţie un API pentru gestiunea utilizatorilor care


pot să acceseze o aplicaţie. Se oferă suport pentru următoarele operaţii, care
ı̂n mod normal ar trebuie implementate de fiecare dată ı̂n cadrul unei aplicaţii
web:
• crearea şi ştergerea de utilizatori programatic sau prin interfaţă pusă
la dispoziţie automat;
• abilitatea de a reseta parole, cu posibilitatea de a trimite noua parolă
prin email, dacă a fost specificată o adresă;
• posibilitatea de a se crea parole ı̂n mod automat, dacă se foloseşte
crearea programatică a utilizatorilor;
• determinarea listei de utilizatori, ı̂mpreună cu detaliile asociate;
• controale predefinite pentru loginare, creare de utilizator, resetare de
parolă;
O ierarhizare a acestor funcţionalităţi este dată ı̂n figura 9.1.
Exemplificarea mecanismului se va face cu referire la serverul de baze
de date SQL Server 2005 Express Edition, pentru care există suport nativ;
trebuie zis ı̂nsă că dacă se doreşte personalizarea sursei de date care gestio-
nează utilizatorii, acest lucru este posibil prin implementarea unui furnizor de
“membership”. API-ul furnizat este insenzitiv la modul de stocare al detalii-
lor legate de utilizator - particularităţile de accesare a datelor sunt rezolvate
la nivelul implementării de furnizor de “membership”.
Paşii ce trebuie urmaţi pentru utilizarea unui API-ului de membership
sunt:
1. configurarea modului de autentificare ı̂n fişierul web.config;

121
122 CAPITOLUL 9. ASP.NET MEMBERSHIP

Figura 9.1: Elementele utilizate ı̂n lucrul cu “membership”-ul furnizat de


ASP.NET [2]
9.1. CONFIGURAREA AUTENTIFICĂRII PRIN FORME 123

2. setarea detaliilor relativ la stocarea datelor utilizatorilor;

3. configurarea stringului de conexiune şi a furnizorului de membership;

4. crearea de utilizatori ai aplicaţiei web;

5. crearea unei pagini de login.

9.1 Configurarea autentificării prin forme


Trebuie mai ı̂ntâi structurată aplicaţia ca resurse protejate prin nume şi
parolă, grupate sub formă de directoare. De regulă, ı̂n rădăcina aplicaţiei
Web trebuie să fie lăsate doar paginile care acceptă accesare de utilizator
anononim (neautentificat), cum ar fi pagina de login sau cea ı̂n care se creează
contul. În rest, paginile sunt grupate ı̂n directoare pentru care se specifică tot
prin fişier de configurare că nu se acceptă decât apel de utilizator autentificat.
Primul pas e deci de a specifica forma de autentificare. În fişierul web.config
din rădăcina aplicaţiei se va scrie:
<system . web>
<a u t h e n t i c a t i o n mode="Forms" />
</system . web>

În forma de mai sus se permite accesarea anonimă a paginilor.


Dacă se doreşte ca pentru directorul numit “protejat” să se permită nu-
mai accesarea de către utilizatori autentificaţi, atunci se va crea un fişier
web.config (comanda add new item din solution explorer) care să conţină:
<c o n f i g u r a t i o n >
<system . web>
<a u t h o r i z a t i o n >
<deny u s e r s=" ? " />
</ a u t h o r i z a t i o n >
</system . web>
</ c o n f i g u r a t i o n >

Simbolul “?” referă un utilizator neautentificat. Datorită acestei setări, orice


utilizator care doreşte să acceseze o pagină aspx din directorul “protejat” va
fi verificat: dacă are tichetul furnizat de un proces de autentificare, atunci i
se permite accesul; altfel, este redirectat automat la pagina de login.
124 CAPITOLUL 9. ASP.NET MEMBERSHIP

9.2 Crearea sursei de date


Dacă se foloseşte SQL Server Express Edition 2005 ı̂n conjuncţie cu
ASP.NET, furnizorul de “membership” va crea automat un fişier de date,
cu o structură bine definită, care va putea fi folosit pentru memorarea date-
lor utilizatorilor. Se va crea un fişier numit ASPNETDB.MDF ı̂n directorul
App_Data al aplicaţiei web. Deşi funcţionează satisfăcător pentru etapa de
dezvoltare, este total neadecvat pentru un sistem aflat ı̂n producţie. Dacă
este o versiune de server diferită de ce s-a spus (de exemplu: SQL Server
2000 sau 2005 non-express), atunci se poate interveni pentru a crea această
sursă de date ı̂n interiorul unui server, sau chiar ı̂n interiorul unei baze de
date preexistente. Această variantă (evitarea fişierului ASPNETDB.MDF) e
sugerată pentru sisteme de producţie.
Pentru crearea structurii de tabele necesare pentru memorarea datelor de
autentificare se poate folosi utilizatrul din linie de comandă aspnet_regsql.exe,
pornit din varianta de command prompt realizată de instalarea lui .NET Fra-
mework. Pornirea uneltei ı̂n sistem “wizard” se face prin tastarea numelui
aplicaţiei pomenite, după care va apărea fereastra din figura 9.2. Wizard-ul
permite fie adăugarea structurilor de date necesare la un server, fie ştergerea
lor (figura 9.3). Ulterior, se specifică: serverul SQL ı̂n care se va face sal-
varea datelor şi contul de acces pentru server (cont necesar utilitarului) -
figura 9.4. În baza de date specificată se vor crea tabele – precum se vede
ı̂n figura 9.5 – care vor stoca datele legate de utilizatori, profiluri, roluri etc.
Structura acestor tabele nu trebuie să fie ı̂nţeleasă de către programator, ea
va fi folosită de către API-ul de “membership”.
Menţionăm că unealta de integrare poate fi folosită şi exlusiv din linia de
comandă cu parametri, fără a fi nevoie să se utilizeze wizard–ul grafic din
figurile 9.2 - 9.5.

9.3 Configurarea stringului de conexiune şi a


furnizorului de “membership”
Dacă s–a folosit utilitarul aspnet_regsql ca mai sus, atunci este nevoie
să se specifice ı̂n fişierul de configurare web.config stringul de conexiune care
va fi folosit pentru furnizorul de membership SQL. Acest lucru se face ca la
orice string de conexiune, prin adăgarea ı̂n secţiunea connectionStrings a
unei intrări:
<c o n n e c t i o n S t r i n g s >
<add name="myConStrMembership"
9.3. CONFIGURAREA STRINGULUI DE CONEXIUNE ŞI A FURNIZORULUI DE “MEMBERSHI

Figura 9.2: Utilitarul de configurare a sursei de date pentru autentificare

Figura 9.3: Specificarea modului de lucru pentru utilitarul aspnet_regsql


126 CAPITOLUL 9. ASP.NET MEMBERSHIP

Figura 9.4: Specificarea serverului, contului şi bazei de date

Figura 9.5: Tabelele rezultate după utilizarea uneltei de integrare a serverului


de baze de date cu partea de SQL Membership din ASP.NET
9.3. CONFIGURAREA STRINGULUI DE CONEXIUNE ŞI A FURNIZORULUI DE “MEMBERSHI

c o n n e c t i o n S t r i n g=" data s o u r c e =( l o c a l ) \ d e v e l o p ;
u s e r i d=sa ; password=1q2w3e
i n i t i a l c a t a l o g=t e s t " />
</ c o n n e c t i o n S t r i n g s >
Pentru a specifica apoi metoda de autentificare, se va adăuga ı̂n fişierul
web.config un element ce precizează modul de autentificare:
<system . web>
<a u t h e n t i c a t i o n mode="Forms" />

<membership d e f a u l t P r o v i d e r=" MyMembershipProvider ">


<p r o v i d e r s >
<add name=" MyMembershipProvider "
connectionStringName="myConStrMembership"
applic at ionN am e="/"
e n a b l e P a s s w o r d R e t r i e v a l=" f a l s e "
e n a b l e P a s s w o r d R e s e t=" t r u e "
requiresQuestionAndAnswer=" t r u e "
r e q u i r e s U n i q u e E m a i l=" t r u e "
passwordFormat=" Hashed "
type=" System . Web . S e c u r i t y . SqlMembershipProvider " />
</ p r o v i d e r s >
</membership>
</system . web>
În interiorul elementului membership pot exista mai multe elemente providers,
dar cel specificat prin intermediul atributului defaultProvider este cel aflat
ı̂n uz. La lansarea interfeţei de administrare a aplicaţiei – Project → ASP.NET
Configuration – se va putea alege dintre furnizorul implicit şi cel definit an-
terior – MyMembershipProvider – aşa cum se vede ı̂n figura 9.6.
Atributele elementului add din cadrul furnizorului definit sunt:
• name - defineşte un nume unic asociat furnizorului curent;
• applicationName - numele aplicaţiei web pentru care funcţionează fur-
nizorul de securitate ales
• description - o descriere opţională
• passwordFormat - formatul ı̂n care vor fi salvate parolele ı̂n baza de
date; valori posibile: Clear, Encrypted, Hashed
• minRequiredNonalphanumericCharacters - care este numărul minim de
caractere non-alfanumerice care sunt cerute ı̂n parolă
128 CAPITOLUL 9. ASP.NET MEMBERSHIP

Figura 9.6: Configurare din aplicaţia de administrare a aplicaţiei Web

• minRequiredPasswordLength - lungimea minimă a parolei

• passwordStrengthRegularExpression - o expresie regulată care descrie


formatul general pe care trebuie să ı̂l respecte o parolă;

• enablePasswordReset - dacă se permite suprascrierea unei parole uitate;


valori posibile: true, false

• enablePasswordRetrieval - se precizează dacă parola poate fi recuperată


prin apeluri de metode din API-ul de membership; posibil d.n.d nu s–a
folosit varianta de “Hashed”

• maxInvalidPasswordAttempts - numărul maxim de ı̂ncercări consecu-


tive greşite suportate de sistem pentru un utilizator; dacă se ajunge
la acest prag, contul utilizatorului este dezactivat pentru o anumită
perioadă

• passwordAttemptWindow - cât timp este blocat un utilizator care face


prea multe ı̂ncercări de introducere a unei parole;

• requiresQuestionAndAnswer - dacă pentru recuperarea parolei utiliza-


torul trebuie să dea un răspuns la o ı̂ntrebare;
9.4. CREAREA DE UTILIZATORI 129

• requiresUniqueEmail - specifică dacă adresa de email trebuie să fie


unică pentru utilizatorii care se ı̂nscriu ı̂n sistem

9.4 Crearea de utilizatori


Se poate crea un utilizator din interfaţa de management al aplicaţiei Web:
tab-ul Security, apoi Create User, precum ı̂n figura 9.7.

Figura 9.7: Adăugarea unui utilizator

Din acest moment se poate folosi ı̂n partea de code–behind:


protected void LoginAction_Click ( object s e nde r , EventArgs e )
{
i f ( Membership . V a l i d a t e U s e r ( UsernameText . Text , PasswordText . Text ) )
{
F or m s A ut he nt ic at ion . RedirectFromLoginPage ( UsernameText . Text , f a l s e ) ;
}
else
{
LegendStatus . Text = "Nume i n v a l i d ! " ;
130 CAPITOLUL 9. ASP.NET MEMBERSHIP

}
}
cu sanse de autentificare reusită pentru numele de utilizator şi parola salvate
ı̂n baza de date.

9.5 Crearea unei pagini de login


Pagina de login se poate face cu controale puse la dispoziţie de către
ASP.NET sau prin pagini create de programator. Controalele ASP.NET
sunt:
• Login - control compus care rezolvă cele mai multe probleme legate de
autentificare - afişarea unor căsuţe de text ı̂n care se pot scrie numele
de utilizator şi parola;

• LoginStatus - exprimă starea de autentificare a unui utilizator; dacă nu


e autentificat, utilizatorul va vedea un buton a cărui apăsare va duce
la redirectarea către pagina de login; altfel va afişa un buton de logout;

• LoginView - permite afişarea condiţională a diferitelor controale; dacă


utilizatorul este autentificat, atunci va vedeaun anumit conţinut, altfel
un altul;

• PasswordRecovery - permite recuperarea parolei de către utilizator;

• ChangePassword - cere parola veche şi permite specificarea unei noi


parole;

• CreateUserWizard - include un wizard complex care permite crearea


de conturi utilizator.
Avantajul acestor controale este minimul de cod care trebuie scris pentru
a putea face uz de partea de membership.

9.5.1 Controlul Login


-demo live
Faţă de setările implicite ale controlului, se mai poate interveni asupra
proprietăţilor:
• CreateUserText - arată textul de pe un link care va fi folosit pentru
redirectare către o pagină care dă posibilitatea de a crea un nou cont
de utilizator;
9.5. CREAREA UNEI PAGINI DE LOGIN 131

• CreateUserUrl - dă adresa paginii de creare de cont de utilizator;


• HelpPageText - textul link-ului către o pagină de help
• HelpPageUrl - adresa paginii care conţine partea de help
• UsernameRequiredErrorMessage, PasswordRequiredErrorMessage -
mesajele de eroare care apar dacă utilizatorul nu scrie numele de cont
sau parola
• DestinationPageUrl - adresa paginii către care se face redirectare dacă
autentificarea este reuşită; este valabilă ı̂n cazul ı̂n care utilizatorul nu
a fost redirectat către pagina de login dintr-o pagină protejată;
• PasswordRecoveryUrl - adresa paginii de unde se poate face recupe-
rarea parolei
Se pot trata următoarele evenimente:
• LoggingIn - apare ı̂nainte ca utilizatorul să se logineze;
• LoggedIn - apare după ce utilizatorului i s–au recunoscut numele şi
parola introduse;
• LoginError - dacă numele şi parola introduse nu sunt recunoscute
• Authenticate - gestioneză procesul de autentificare; dacă se implemen-
tează un manipulator pentru eveniment, atunci ı̂ntreaga responsabili-
tate a verificării identităţii sistemului este ı̂n sarcina programatorului.

9.5.2 Controlul LoginStatus


-demo live

9.5.3 Controlul LoginView


Permite afişarea unor seturi diferite de controale, ı̂n funcţie de autentifica-
rea utilizatorului: autentificat sau nu; mai departe, se pot face particularizări
ı̂n funcţie de rolul pe care ı̂l are utilizatorul. Controlul constă din template-
uri, fiecare având un anumit conţinut.
-demo live

9.5.4 Controlul PasswordRecovery


-demo live
132 CAPITOLUL 9. ASP.NET MEMBERSHIP

9.5.5 Controlul ChangePassword


-demo live

9.6 Gestiunea utilizatorilor prin API-ul de “mem-


bership”
Este posibil ca la un momemnt dat să se dorescă scrierea de cod care să
permită gestionarea prin cod a utilizatorilor. Plecând de la metodele oferite
de clasa Membership, se pot face următoarele operaţii:
• creare de noi utilizatori

• ştergerea de utilizatori

• modificarea datelor utilizatorilor existenţi

• aducerea listei de utilizatori

• aducerea detaliilor despre un utilizator

• validarea credenţialelor faţă de sursa de date.


Multe din metodele clasei Membership pot lucra cu un parametru de tip
MembershipUser sau returnează coleţie de obiecte d eacest tip.

9.6.1 Aducerea listei de utilizatori


Să presupunem că vom aduce lista de utilizatori ı̂ntr-un grid, având co-
loanele: username, email şi ı̂ncă o coloană ce conţine link de selectare. Codul
pentru crearea de grid ı̂n pagina aspx este:
<asp : GridView ID=" GridView1 " runat=" s e r v e r "
AutoGenerateColumns=" F a l s e " DataKeyNames="UserName">
<Columns>
<asp : CommandField ShowSelectButton=" t r u e " />
<asp : BoundField D a t a F i e l d="UserName"
HeaderText=" username " />
<asp : BoundField D a t a F i e l d=" Email "
HeaderText=" e m a i l a d d r e s s " />
</Columns>
</asp : GridView>
Legarea la date se poate face din partea de code-behind astfel:
9.6. GESTIUNEA UTILIZATORILOR PRIN API-UL DE “MEMBERSHIP”133

public p a r t i a l c l a s s A l l U s e r s : System . Web . UI . Page


{
MembershipUserCollection a l l U s e r s ;

protected void Page_Load ( object s e nde r , EventArgs e )


{
a l l U s e r s = Membership . G e t A l l U s e r s ( ) ;
GridView1 . DataSource = a l l U s e r s ;
GridView1 . DataBind ( ) ;
}
}

9.6.2 Obţinerea detaliilor despre un utilizator


Odată ce se obţine un obiect MembershipUser pentru care se cer detaliile,
pe baza proprietăţilor expuse de obiect se pot afla detalii de interes. Pentru
codul de mai sus, ı̂n partea de code-behind se adaugă metoda:
protected void GridView1_SelectedIndexChanged ( object s e nde r ,
EventArgs e )
{
M e m b e r s h i p U s e r C o l l e c t i o n allUsersWithName =
Membership . FindUsersByName ( GridView1 . S e l e c t e d V a l u e as string ) ;
i f ( allUsersWithName . Count == 1 )
{
Label1 . Text = " Last l o g i n dat e " +
allUsersWithName [ GridView1 . S e l e c t e d V a l u e as string ] .
LastLoginDate . ToString ( ) ;
}
}

9.6.3 Modificarea datelor unui utilizator


O dată ce este regăsit obiectul de tip MembershipUser pentru un utilizator
ale cărui date trebuie schimbate, se pot accesa diferite proprietăţi ale sale
(EmailText, CommentTextBox, IsApprovedCheck etc) iar via metoda statică
Membership.UpdateUser(obiect) se face salvarea datelor modificate.

9.6.4 Crearea şi ştergerea unui utilizator


Se fac prin metodele statice Membership.CreateUser, respectiv Membership.DeleteUser.
134 CAPITOLUL 9. ASP.NET MEMBERSHIP

9.6.5 Validarea unui utilizator


Validarea utilizatorului vizavi de sursa de date folosite se face prin in ter-
mediul metodei statice Membership.ValidateUser(string username, string
password).
Bibliografie

[1] D. Gourley, B. Totty, M. Sayer, S. Reddy, and A. Aggarwal. HTTP: The


Definitive Guide. O’Reilly, 2002.

[2] M. MacDonald and M. Szpuszta. Pro ASP.NET 3.5 in C# 2008. Apress,


2008.

[3] L. Sasu. Tehnologii XML. Note de curs, Facultatea de Matematică şi


Informatică, Universitatea Transilvania din Braşov, 2009.

[4] C. Wong. HTTP Pocket Reference. O’Reilly, 2000.

135