Sie sind auf Seite 1von 26

1

Le Dveloppement dune Application Web

Le Dveloppement dune Application Web


Sommaire
Le Dveloppement dune Application Web ...................................................................................... 1 1 2 Introduction ..................................................................................................................................... 2 Le dveloppement de site Web....................................................................................................... 2 2.1 La leve dune exception dune page et dune application..................................................... 2 Lvnement Page_Error ................................................................................................. 3 Lvnement Application_Error....................................................................................... 3

2.1.1 2.1.2 2.2 2.3

Intervenir dynamiquement sur le fichier de configuration Web.config ................................. 4 Crer des pages Web Asynchrone........................................................................................... 9 Pourquoi utiliser le mode Asynchrone ............................................................................ 9 Activation et utilisation du mode Asynchrone .............................................................. 10

2.3.1 2.3.2 2.4 3

Gnrer dynamiquement des images ................................................................................... 16

Rcupration dinformation .......................................................................................................... 18 3.1 En gnral .............................................................................................................................. 18 Response ....................................................................................................................... 19 Request .......................................................................................................................... 20 Server............................................................................................................................. 21 Context .......................................................................................................................... 23

3.1.1 3.1.2 3.1.3 3.1.4 3.2 3.3 4

Sur le navigateur.................................................................................................................... 24 Les Headers ........................................................................................................................... 25

Conclusion ..................................................................................................................................... 26

23 septembre 2008

Le Dveloppement dune Application Web

1 Introduction
Lun des aspects unique que nous procure ASP.NET dans le dveloppement dApplication Web est la faon de comprendre les interactions entre le Navigateur Web et le Serveur Web. Beaucoup doutils nous sont donns afin de traiter les informations durant les communications client-serveur. Afin de coder lapplication Web la plus optimise qui soit, il est ncessaire de connaitre ces diffrents outils et de savoir les configurer. Voici quelques exemples de comptence que vous devrez maitriser la fin de ce chapitre : Le dveloppement dune application Web : - La dtection du navigateur Web dans nos Web Forms - Dterminer la cause dune exception non gre sur notre page Web - Accder aux enttes dune page Web - Accder aux pages encapsules et grer leurs contextes dapplication - Crer une page Asynchrones Ajouter et configurer des Web Server Controls : - Modifier le fichier de configuration du site Web via le code - Ecrire un vnement gnrant des images dynamiquement affichable sur notre page Web Afin dapprhender ces nouvelles connaissances nous diviserons ce chapitre en deux parties : Le dveloppement de Site Web Les requtes dinformations

2 Le dveloppement de site Web


Bien que nous sachions comment crer diffrents types de tache en ASP.NET, il est trs souvent ncessaire de comprendre comment interagir directement avec les Services dInformation Internet (IIS). Ainsi, grce cette interaction, nous allons voir comment crer les diffrentes taches suivantes : Rattraper les exceptions non gres Consulter ou modifier le fichier de configuration de notre application Web Lancer des taches en mode Asynchrone Gnrer dynamiquement diffrents types de fichier

2.1 La leve dune exception dune page et dune application


La leve dexception est le moyen le plus efficace pour rattraper des erreurs dans de petites portions de code. Par exemple, il est trs courant (voire recommand) de mettre un bloc try/catch entre une connexion une base de donnes pour prvoir une surcharge de connections ou bien tout simplement pour palier une impossibilit de joindre notre base de donnes.

23 septembre 2008

Le Dveloppement dune Application Web

Ce cours nayant pas pour but de vous former lutilisation de block try/catch nous considrerons cette leon comme acquise. Dans le cas contraire veuillez vous rfrer aux cours de C# et VB.NET. Bien que vous prvoyiez un maximum derreurs dans vos blocks try/catch il peut arriver que cela ne suffise pas et que certaines erreurs que vous naviez pas prvues, se prsentent. Il est possible de rcuprer ces erreurs qui ne rsoudront pas le problme mais pourront informer lutilisateur du problme. 2.1.1 Lvnement Page_Error Pour rcuprer les erreurs sur la page, il faut crer un gestionnaire dvnement Page_Error dans chaque page afin de pouvoir grer lvnement Error. Le gestionnaire dvnement prend en paramtre un Object et un EventArgs, mais en rgle gnrale il est inutile de se proccuper de ces deux paramtres. Une fois lintrieur de notre gestionnaire dvnement on peut utiliser les mthodes suivantes : Server.GetLastError() qui permet de retrouver la dernire erreurs en date ; Server.ClearError() qui permet deffacer les erreurs en queue ; Trace.Write() est une fonction qui permet dafficher le contenue de nos erreurs.

Voici un exemple type dun gestionnaire dvnement Page_Error :


C#
private void Page_Error(object sender, EventArgs e) { Trace.Write("ERREUR : " + Server.GetLastError().Message); Server.ClearError(); }

VB.NET
Protected Sub Page_Error(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Trace.Write("ERREUR : " + Server.GetLastError().Message) Server.ClearError() End Sub

Il est important de remarquer quil est impossible daccder nos controles partir de notre gestionnaire dvnement. 2.1.2 Lvnement Application_Error Il est galement possible de lever une erreur au niveau de lapplication entire. Cette autre mthode nous permet de ne plus tre oblig de crer un gestionnaire dvnement Page_Error sur chaque page de notre application, mais de nous renvoyer vers une mme page qui traitera toutes les erreurs. Pour procder ainsi, il faut ajouter notre projet llment Global Application Class qui est unique pour chaque application. Tous les vnements dont nous pouvons avoir besoin sont automatiquement ajouts par Visual Studio. Maintenant que lon peut accder au fichier Global.asax il ne reste plus qu rediriger toutes nos erreurs vers une page de traitement. Exemple : 23 septembre 2008

Le Dveloppement dune Application Web

C# de Global.asax
protected void Application_Error(object sender, EventArgs e) { Server.Transfer("ErrorPage.aspx"); // ErrorPage.aspx est bien entendu une page que vous // aurez cre dans laquel nous rcuprerons // les messages d'erreurs pour les afficher // grace aux mthode vu prcdement }

C# de Global.asax
Protected Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs) Server.Transfer("ErrorPage.aspx") ' ErrorPage.aspx est bien entendu une page que vous ' aurez cre dans laquel nous rcuprerons ' les messages d'erreurs pour les afficher ' grace aux mthode vu prcdement End Sub

2.2 Intervenir dynamiquement sur le fichier de configuration Web.config


Vous avez surement du frquemment toucher au fichier de configuration Web.config de faon manuelle pour activer ou dsactiver les ViewStates, ou bien activer le mode Debug dans Visual Studio (demand automatiquement la premire compilation de chaque projet). Il existe cela dit une manire dynamique (en passant par le CodeBehind) pour modifier ce fichier de configuration. Ce genre de manipulation peut savrer trs utile dans le cas de dploiement assist de votre application Web, ou bien dans une interface dadministrateur etc. Au cours de cette partie nous allons voir comment effectuer de telles modifications. En premier lieu, il nous faut utiliser lobjet System.Configuration.Configuration afin de lire et crire dans le Web.config. Dans le but de crer un objet Configuration de notre application courante, il vous faudra faire appel WebConfigurationManager. A partir de cet objet Configuration, il nous est possible daccder aux mthodes GetSection et GetSectionGroup qui vont nous permettre de lire dans nos sections de notre fichier de configuration. Il est ncessaire que lutilisateur en cours ait les autorisations de lecture sur tous les fichiers de configuration. Une fois vos actions finies, deux choix soffrent vous : Grace la mthode Save vous pourrez sauvegarder vos changements effectus, il faut toutefois possder les droits de modifications pour que cette action soit valide. Grace la mthode SaveAs vous pourrez sauvegarder vos changements dans un autre fichier de configuration, dans ce cas l, il faut en plus les droits de cration de fichier.

Il est prfrable dutiliser la mthode SaveAs lorsque lon veut spcifier une configuration bien prcise un sous-dossier par exemple. Voici une portion de code montrant comment modifier un fichier de configuration. Pour que ce code fonctionne, il ne faut pas oublier dajouter le namespace System.Web.Configuration.

23 septembre 2008

Le Dveloppement dune Application Web

C#
protected void Page_Load(object sender, EventArgs e) { //Tout d'abord il nous faut rcuprer la configuration Web de notre application System.Configuration.Configuration config = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~/Test"); //Ensuite rcuprons la section dsire AuthenticationSection section = (AuthenticationSection)config.GetSection("system.web/authentication"); //On cr un Label pour stocker les informations contenue dans notre section // et que l'on souhaitera afficher. Label Label1 = new Label(); Label1.Text = section.Mode.ToString(); form1.Controls.Add(Label1); }

VB.NET
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load ' Tout d'abord il nous faut rcuprer la configuration Web de notre application Dim config As System.Configuration.Configuration = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~/Test") ' Ensuite rcuprons la section dsire Dim section As Web.Configuration.AuthenticationSection = config.GetSection("system.web/authentication") ' On cr un Label pour stocker les informations contenue dans notre section ' et que l'on souhaitera afficher. Dim Label1 As Label = New Label() Label1.Text = section.Mode.ToString() form1.Controls.Add(Label1) End Sub

Chaque lment du Web.config dispose de sa propre classe. Voici un tableau dtaillant les plus utiliss.
Class AuthenticationSection AnonymousIdentificationSection AuthorizationSection CacheSection CompilationSection Section de Configuration dans <system.web> <authentication> <anonymousIdentification> <authorization> <cache> <compilation> Description Configure lauthentification pour une page Web Configure lidentification anonyme pour les utilisateurs non authentifis Configure une autorisation dapplication Web Configure les paramtres de cache globaux pour une application ASP.NET Dfinit des paramtres de configuration qui sont utiliss pour prendre en charge l'infrastructure de compilation d'applications Web Configure les erreurs personnalises ASP.NET Dfinit des paramtres de configuration qui

CustomErrorsSection DeploymentSection

<customErrors> <deployement>

23 septembre 2008

Le Dveloppement dune Application Web

sont utiliss pour prendre en charge le dploiement d'une application Web GlobalizationSection <globalization> Dfinit des paramtres de configuration qui sont utiliss pour prendre en charge l'infrastructure de globalisation d'applications Web Configure les profiles ASP.NET afin de dterminer comment les vnements du health-monitoring sont envoy vers le gestionnaire dvnement Dfinit des paramtres de configuration qui contrlent le comportement de l'environnement d'hbergement de l'application Configure des proprits pour les cookies utiliss par une application Web Configure un gestionnaire HTTP pour une application Web Configure le runtime HTTP ASP.NET Configure l'identit d'une application Web Dfinit les paramtres de configuration qui contrlent la gnration de cls et les algorithmes utiliss dans le chiffrement, le dchiffrement et les oprations MAC (Media Access Control) dans l'authentification Windows Forms, la validation d'tat d'affichage et l'isolation d'application des tats de session Dfinit les paramtres de configuration pour la prise en charge de l'infrastructure permettant de configurer et de grer les dtails d'appartenance (membership) Configure le cache de sortie pour une application Web Fournit l'accs par programme la section de l'lment pages (Schma des paramtres ASP.NET) du fichier de configuration La classe ProfileSection permet d'accder au contenu de la section profile du fichier de configuration et de le modifier par programme. Dfinit des paramtres de configuration qui sont utiliss pour prendre en charge l'infrastructure de scurit d'une application Web Configure la section sessionPageState Configure l'tat de session pour une application Web

HealthMonitoringSection

<heathMonitoring>

HostingEnvironmentSection

<hostingEnvironment>

HttpCookiesSection HttpHandlersSection HttpRuntimeSection IdentitySection MachineKeySection

<httpCookies> <handlersSection> <httpRuntime> <identity> <manchineKey>

MembershipSection

<membership>

OutputCacheSection PagesSection

<outputCache> <pages>

ProfileSection

<profile>

SecurityPolicySection

<securityPolicy>

SessionPageStateSection SessionStateSection

<sessionPageState> <sessionState>

23 septembre 2008

Le Dveloppement dune Application Web

SiteMapSection

<siteMap>

Dfinit des paramtres de configuration utiliss pour prendre en charge l'infrastructure pour la configuration, le stockage et le rendu de la navigation de site Configure les dpendances de cache SQL pour une application ASP.NET Configuration du service de trace ASP.NET Configure le niveau de scurit d'accs du code appliqu une application Configure la section webControls Fournit l'accs par programme la section du fichier de configuration webParts Configure la section xhtmlConformance

SqlCacheDependencySection TraceSection TrustSection WebControlsSection WebPartsSection XhtmlConformanceSection

<sqlCacheDependency> <trace> <trust> <webControls> <webParts> <xhtmlConformance>

Une fois que vous aurez crez une instance dune de ces classes, vous pouvez utiliser leurs mthodes ainsi que leurs proprits pour lire et crire des informations dans le fichier de configuration. Il est possible dafficher nos modifications personnalises dans un Label comme peut le montrer la ligne de code suivante :
C#
Label1.Text = WebConfigurationManager.AppSettings["Test"];

VB.NET
Label1.Text = Web.Configuration.WebConfigurationManager.AppSettings("Test")

Ici Test reprsente le nom de notre application/solution. Il est aussi possible daccder dynamiquement au Connection Strings en utilisant la collection WebConfigurationManager.ConnectionStrings. Exemple :
C#
Label1.Text = WebConfigurationManager.ConnectionStrings["MaBase"].ConnectionString;

VB.NET
Label1.Text = Web.Configuration.WebConfigurationManager.ConnectionStrings("MaBase").ConnectionString

La mthode statique WebConfigurationManager est le moyen le plus efficace pour consulter la configuration du site car elle prend en compte la hierarchie entire. En revanche si vous souhaitez effectuer des changements dans cette configuration il vous faudra choisir un emplacement spcifique pour votre configuration. Dans ce but, vous allez devoir crer une instance de lobjet Configuration dont la syntaxe est un peu particulire.

23 septembre 2008

Le Dveloppement dune Application Web

Pour crer une instance qui touchera la racine de votre Web.Config (qui sappliquera toute votre application Web) il faut appeler la mthode statique WebConfigurationManager.OpenWebConfiguration et lui passer ~ en paramtre. Ensuite, il faut utiliser lobjet Configuration pour crer des objets destins des sections individuelles. Une fois vos oprations termines, il suffit dappeler la mthode Configuration.Save() pour sauvegarder vos modifications. Exemple :
C#
protected void Page_Load(object sender, EventArgs e) { //Ce code rajoute <trace enabled="true" /> notre fichier de Web.config Configuration rootConfig = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~"); //Il est possible de passer en paramtre le chemin de notre application //Exemple : WebConfiguration.OpenWebConfiguration("/Test") System.Web.Configuration.TraceSection trace = (System.Web.Configuration.TraceSection)rootConfig.GetSection("system.web/tra ce"); trace.Enabled = true; rootConfig.Save(); }

VB.NET
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load ' Ce code rajoute <trace enabled="true" /> notre fichier de Web. Dim rootConfig As System.Configuration.Configuration = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~") ' Il est possible de passer en paramtre le chemin de notre application ' Exemple : WebConfiguration.OpenWebConfiguration("/Test") Dim trace As System.Web.Configuration.TraceSection = CType(rootConfig.GetSection("system.web/trace"), System.Web.Configuration.TraceSection) Trace.Enabled = True rootConfig.Save() End Sub

Il existe trois faons diffrentes de sauvegarder notre configuration que lon utilise grace lnumration ConfigurationSaveMode : Full : Toutes les proprits sont crites dans le fichier de configuration. Trs utilis pour le deplacement de lapplication dune machine une autre. Minimal : Seules les proprits qui diffrent des valeurs hrites sont crites dans le fichier de configuration. Modified : Seules les proprits modifies seront crites dans le fichier de configuration, et ce, mme lorsque la valeur est la mme que celle dont elle hrite.

23 septembre 2008

Le Dveloppement dune Application Web

Pour crer un nouveau fichier de configuration il faut appeler la mthode Configuration.SaveAs() et dfinir son emplacement. Exemple :
C#
Configuration config = WebConfigurationManager.OpenWebConfiguration("~"); config.SaveAs(Server.MapPath("Test.Web.config"), ConfigurationSaveMode.Full, true); //Rappelons que Server.MapPath() nous permet de donner un chemin relatif.

VB.NET
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load Dim rootConfig As System.Configuration.Configuration = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~") rootConfig.SaveAs(Server.MapPath("Test.Web.config"), ConfigurationSaveMode.Full, True) ' Rappelons que Server.MapPath() nous permet de donner un chemin relatif. End Sub

2.3 Crer des pages Web Asynchrone


Jusqu prsent nous ne nous sommes attachs qu la conception de notre application Web, dsormais nous allons aborder la notion de performance (rapidit) de notre application. Lintroduction ce concept est trs particulire car il sera difficile de constater cette amlioration lors du dveloppement. Rappelons que nous sommes les seuls effectuer des requtes sur nos pages Web durant cette tape. La diffrence ne sera notable que lorsque lapplication sera en ligne et quune multitude dutilisateurs effectuerons des requtes en simultan. Faire de la programmation en mode asynchrone permet entre autre de crer une application plusieurs threads, c'est--dire une application disposant de plusieurs processus lgers sexcutant en mme temps (en parallle). Lavantage de ce type de programmation est que faire tourner plusieurs petits processus augmente de faon considrable la performance de notre application. La programmation asynchrone peut savrer relativement complique au premier abord, mais elle est parfois indispensable dans le cas daccs une base de donnes par exemple, afin de diminuer le temps dattente lors de son accs. 2.3.1 Pourquoi utiliser le mode Asynchrone Si vous avez dj fait de la programmation en WinForm Application et que vous avez dj abord les threads, il faudra bien comprendre que les objectifs ne sont absolument pas les mmes. En effet, tandis que lun des objectifs du thread est de lancer un petit processus en parallle en attendant une rponse de lutilisateur, par exemple dans le cas dun script de Tchat, lobjectif principal de lasynchrone en Web est dallger au maximum la charge serveur afin davoir un accs aux donnes et par consquent un affichage de notre page Web le plus rapide possible. Car, contrairement au WinForm (dans le cas dune application avec dialogue rseau) o chacun dispose de sa propre partie cliente dj install sur sa propre machine, plusieurs requtes sont frquemment envoyes par plusieurs utilisateurs en mme temps. 23 septembre 2008

10

Le Dveloppement dune Application Web

De base, ASP.NET et IIS ne peuvent effectuer le rendu que dun nombre limit de page simultanment. Par consquent, plus le pool de thread est consomm plus les performances diminuent tel un entonnoir. Une fois ce pool compltement consomm, les autres pages sont mises en attente tant que le rendu des pages en cours nest pas termin. Lactivation et la bonne gestion du mode asynchrone permet daugmenter le nombre de rendu simultan et donc damliorer les performances de notre application Web. 2.3.2 Activation et utilisation du mode Asynchrone Nous allons expliquer comment utiliser le mode Asynchrone en trois tapes. On estimera que vous saurez implmenter les espaces de nommage adquat. Etape 1 : Pour commencer, il faut ajouter Async="true" la directive page dans laquelle on inclura des vnements en asynchrone.
ASP.NET
<%@ Page Language="c#" Async="true" CodeBehind="Default.aspx.cs" Inherits="Test._Default" %>

Cette directive indique au compilateur quil faut que la page implmente IHttpAsyncHandler au lieu de IHttpHandler. Cette interface permet dimplmenter des fonctionnalits de traitements asynchrones la page. Etape 2 : Dans le CodeBehind de notre page, nous allons crer nos taches qui vont se lancer en mode asynchrone dans notre Page_Load par exemple.
C#
protected void Page_Load(object sender, EventArgs e) { //Nous allons commencer par crer nos taches PageAsyncTask myTask = new PageAsyncTask(new BeginEventHandler(BeginFirstTask), new EndEventHandler(EndFirstTask), new EndEventHandler(TimeoutFirstTask), null); //On peut remarquer que le constructeur PageAsyncTask prend en paramtre : // - un BeginEventHandler : gre l'vnement de dbut de notre tache ; // - un EndEventHandler : gre l'vnement de fin de notre tache ; // - un TimoutException : gre l'vnement en cas de requte trop longue ; // - un Objet state : mis null par dfaut //Enregistrons notre tache maintenant : Page.RegisterAsyncTask(myTask) ; }

23 septembre 2008

11

Le Dveloppement dune Application Web

VB.NET
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load ' Nous allons commencer par crer nos taches Dim myTask As PageAsyncTask = New PageAsyncTask(New BeginEventHandler(AddressOf BeginFirstTask), New EndEventHandler(AddressOf EndFirstTask), New EndEventHandler(AddressOf TimeoutFirstTask), DBNull.Value) ' On peut remarquer que le constructeur PageAsyncTask prend en paramtre : ' - un BeginEventHandler : gre l'vnement de dbut de notre tache ; ' - un EndEventHandler : gre l'vnement de fin de notre tache ; ' - un TimoutException : gre l'vnement en cas de requte trop longue ; ' - un Objet state : mis null par dfaut ' Enregistrons notre tache maintenant : Page.RegisterAsyncTask(myTask) End Sub

Notons quaucune action na t dfinie pour nos taches asynchrones pour le moment. Nous allons cependant expliquer comment va tre trait ce bout de code. 1. Un premier thread est donn la requte. La page est traite en entier jusqu'au Page_PreRenderComplete ; 2. La page liste toutes les tches et les laisse, les une aprs les autres, envoyer leur requte asynchrone, puis elle rend le thread au pool ASP.NET ; 3. Lorsque les requtes sont en train dtre traits aucun thread du pool ASP.NET nest consomm ; 4. Quand ces requtes reviennent , ASP.NET redonne un thread la page, qui excute les fonctions de retour ou de timeout, puis fini l'excution normale de la page (le Page_PreRenderComplete et la suite). On peut forcer ASP.NET effectuer toute cette mcanique avant le Page_PreRenderComplete en appelant explicitement la mthode Page.ExecuteRegisteredAsyncTasks. Etape 3 : Nous allons maintenant grer nos vnements correspondant chacune de nos taches. BeginFirstTask :

23 septembre 2008

12

Le Dveloppement dune Application Web

C#
public partial class _Default : System.Web.UI.Page { //Il nous faut crer des attirbuts par lesquels on va passer pour // communiquer entre nos vnements private string dataBase = @"Data Source=\SQLEXPRESS; Initial Catalog=dbTest; Integrated Security=True; Pooling=False; Asynchronous Processing=true"; //Changer la valeur de votre attribut en fonction de votre base de donnes //Il ne faut surtout pas oublier d'ajouter "Asynchronous Processing=true" // pour accder notre base de donnes en mode asynchrone private SqlCommand Commande; protected void Page_Load(object sender, EventArgs e) { Voir plus haut } //Evnement du dbut de notre tache IAsyncResult BeginFirstTask(object sender, EventArgs e, AsyncCallback cb, object state) { //Nous allons nous connecter une base de donnes //On peut admettre que cette opration est longue si une grand nombre // d'utilisateur est connect en mme temps SqlConnection MaConnection = new SqlConnection(dataBase); //Il faut bien sur avoir cre une base de donnes contenant la table sur // laquelle nous effectuons notre requte plus bas. MaConnection.Open(); SqlCommand MaCommande = new SqlCommand( "SELECT * FROM Users", MaConnection); //Nous allons stocker notre sql Commande dans nos // attributs pour les rcuprer dans nos autres taches Commande = MaCommande; //On termine par renvoy l'execetion de notre vnement // qui va tre excuter en mode asynchrone. return Commande.BeginExecuteReader(cb, state); } }

23 septembre 2008

13

Le Dveloppement dune Application Web

VB.NET
Partial Public Class _Default Inherits System.Web.UI.Page ' Il nous faut crer des attirbuts par lesquels on va passer pour ' communiquer entre nos vnements Private dataBase As String = "Data Source=\SQLEXPRESS;Initial Catalog=dbTest;Integrated Security=True;Asynchronous Processing=true" ' Changer la valeur de votre attribut en fonction de votre base de donnes ' Il ne faut surtout pas oublier d'ajouter "Asynchronous Processing=true" ' pour accder notre base de donnes en mode asynchrone Private Commande As SqlCommand Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load Voir plus haut End Sub ' Evnement du dbut de notre tache Function BeginFirstTask(ByVal sender As Object, ByVal e As EventArgs, ByVal cb As AsyncCallback, ByVal state As Object) As IAsyncResult ' Nous allons nous connecter une base de donnes ' On peut admettre que cette opration est longue si une grand nombre ' d'utilisateur est connect en mme temps Dim MaConnection As SqlConnection = New SqlConnection(dataBase) ' Il faut bien sur avoir cre une base de donnes contenant la table sur ' laquelle nous effectuons notre requte plus bas. MaConnection.Open() Dim MaCommande As SqlCommand = New SqlCommand("SELECT * FROM Users", MaConnection) ' Nous allons stocker notre sql Commande dans nos ' attributs pour les rcuprer dans nos autres taches Commande = MaCommande ' On termine par renvoy l'execetion de notre vnement ' qui va tre excuter en mode asynchrone. Return Commande.BeginExecuteReader(cb, state) End Function End Class

EndFirstTask : Afin de lier nos vnements notre base de donnes et les afficher nous allons crer une GridView sur notre page ASPX.
ASPX
<form id="form1" runat="server"> <asp:GridView ID="MonRepeateur" runat="server"> </asp:GridView> </form>

Grons maintenant lvnement aprs BeginFirstTask. 23 septembre 2008

14

Le Dveloppement dune Application Web

C#
void EndFirstTask(IAsyncResult ar) { //Lions notre base de donnes notre GridView MonRepeateur.DataSource = Commande.EndExecuteReader(ar); MonRepeateur.DataBind(); }

VB.NET
Protected Sub EndFirstTask(ByVal ar As IAsyncResult) 'Lions notre base de donnes notre GridView MonRepeateur.DataSource = Commande.EndExecuteReader(ar) MonRepeateur.DataBind() End Sub

TimeoutFirstTask : Pour finir, nous allons grer lvnement timeout. Par exemple on peut crer un Label sur notre page ASPX dont la proprit texte sera gre dans le CodeBehind.
ASPX
<form id="form1" runat="server"> <asp:GridView ID="MonRepeateur" runat="server"> </asp:GridView> <asp:Label ID="MonLabel" runat="server"></asp:Label> </form>

C#
//Evnement de Timeout void TimeoutFirstTask(IAsyncResult ar) { MonLabel.Text = "Erreur de connection"; }

VB.NET
' Evnement de Timeout Protected Sub TimeoutFirstTask(ByVal ar As IAsyncResult) MonLabel.Text = "Erreur de connection" End Sub

Code Source final :


ASPX
<body> <form id="form1" runat="server"> <asp:GridView ID="MonRepeateur" runat="server"> </asp:GridView> <asp:Label ID="MonLabel" runat="server"></asp:Label> </form> </body>

23 septembre 2008

15

Le Dveloppement dune Application Web

C#
public partial class _Default : System.Web.UI.Page { private string dataBase = @"Data Source=\SQLEXPRESS; Initial Catalog=dbTest; Integrated Security=True; Pooling=False; Asynchronous Processing=true"; private SqlCommand Commande; protected void Page_Load(object sender, EventArgs e) { PageAsyncTask myTask = new PageAsyncTask( new BeginEventHandler(this.BeginFirstTask), new EndEventHandler(this.EndFirstTask), new EndEventHandler(this.TimeoutFirstTask), null); Page.RegisterAsyncTask(myTask); } IAsyncResult BeginFirstTask(object sender, EventArgs e, AsyncCallback cb, object state) { SqlConnection MaConnection = new SqlConnection(dataBase); MaConnection.Open(); SqlCommand MaCommande = new SqlCommand("SELECT * FROM Users", MaConnection); Commande = MaCommande; return Commande.BeginExecuteReader(cb, state); } void EndFirstTask(IAsyncResult ar) { MonRepeateur.DataSource = Commande.EndExecuteReader(ar); MonRepeateur.DataBind(); } void TimeoutFirstTask(IAsyncResult ar) { MonLabel.Text = "Erreur de connection"; } }

23 septembre 2008

16

Le Dveloppement dune Application Web

VB.NET
Partial Public Class _Default Inherits System.Web.UI.Page Private dataBase As String = "Data Source=\SQLEXPRESS;Initial Catalog=dbTest;Integrated Security=True;Pooling=False;Asynchronous Processing=true" Private Commande As SqlCommand Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load Dim myTask As PageAsyncTask = New PageAsyncTask(New BeginEventHandler(AddressOf BeginFirstTask), New EndEventHandler(AddressOf EndFirstTask), New EndEventHandler(AddressOf TimeoutFirstTask), DBNull.Value) Page.RegisterAsyncTask(myTask) End Sub Function BeginFirstTask(ByVal sender As Object, ByVal e As EventArgs, ByVal cb As AsyncCallback, ByVal state As Object) As IAsyncResult Dim MaConnection As SqlConnection = New SqlConnection(dataBase) MaConnection.Open() Dim MaCommande As SqlCommand = New SqlCommand("SELECT * FROM Users", MaConnection) Commande = MaCommande Return Commande.BeginExecuteReader(cb, state) End Function Protected Sub EndFirstTask(ByVal ar As IAsyncResult) MonRepeateur.DataSource = Commande.EndExecuteReader(ar) MonRepeateur.DataBind() End Sub Protected Sub TimeoutFirstTask(ByVal ar As IAsyncResult) MonLabel.Text = "Erreur de connection" End Sub End Class

2.4 Gnrer dynamiquement des images


Lors de laffichage dune image dans notre application Web, IIS lit simplement le fichier image du systme de fichier, et les renvoie directement au navigateur Web. Cependant il peut arriver que dans certains cas on ait besoin quune image soit gnre automatiquement, par exemple pour des miniatures de photos ou pour afficher un graphique rsultant de statistique ou sondage. Il est bien entendu possible de gnrer des miniatures ou des graphiques lavance et de les afficher, et bien que cette solution soit la plus lgre pour le serveur, nous sommes frquemment obligs de gnrer nous mme ces images. ASP.NET nous donne justement des outils qui vont nous permettre de crer ces miniatures et ces graphiques. Dans cette partie nous allons voir comment utiliser quelques mthodes comprises dans les espaces de nommage System.Drawing et System.Drawing.Imaging. Afin de les illustrer nous allons vous montrer comment crer la miniature dune image. 23 septembre 2008

17

Le Dveloppement dune Application Web

Il existe une mthode appartenant aux objets Image qui va nous permettre, de faon trs simple, de gnrer des miniatures dynamiquement : la mthode GetThumbnailImage() prenant en paramtre une largeur, une hauteur, un callback, et un IntPtr quil faut obligatoirement mettre zro. Exemple :
C#
public bool ThumbnailCallback() // Necessaire pour la gnration de miniatures { return false; } public void CreerMiniature() { Image.GetThumbnailImageAbort myCallback = new Image.GetThumbnailImageAbort(ThumbnailCallback); // -> Dlgu Image.GetThumbnailImageAbort. Dans GDI+ version 1.0, le dlgu n'est pas utilis. // Vous devez toutefois crer un dlgu et passer une rfrence ce dlgu dans ce paramtre. Bitmap bmp = new Bitmap(@"C:\monImage.jpg"); // -> On cre un nouvel objet Bitmap pointant sur notre fichier charg Image maMiniature = bmp.GetThumbnailImage(100, 100, myCallback, IntPtr.Zero); // -> On cre notre miniature

}
VB.NET
Public Function ThumbnailCallback() As Boolean ' Necessaire pour la gnration de miniatures Return False End Function Public Sub CreerMiniature() Dim myCallback As Drawing.Image.GetThumbnailImageAbort = New Drawing.Image.GetThumbnailImageAbort(AddressOf ThumbnailCallback) ' -> Dlgu Image.GetThumbnailImageAbort. Dans GDI+ version 1.0, le dlgu n'est pas utilis. ' Vous devez toutefois crer un dlgu et passer une rfrence ce dlgu dans ce paramtre. Dim bmp As Drawing.Bitmap = New Drawing.Bitmap("C:\monImage.jpg") ' -> On cre un nouvel objet Bitmap pointant sur notre fichier charg Dim maMiniature As Drawing.Image = bmp.GetThumbnailImage(100, 100, myCallback, IntPtr.Zero) ' -> On cre notre miniature End Sub

Afin de bien observer lutilit dune telle fonction nous vous proposons un TP raliser sur la cration dune galerie dimage. Ce TP se trouve en annexe de cette leon dans le dossier Chapitre 8 TP . Voici un apperu de la page que vous serez amen crer au cours de ce TP :

23 septembre 2008

18

Le Dveloppement dune Application Web

3 Rcupration dinformation
Nous allons voir comment il est possible dans une application Web ASP.NET de rcuprer et modifier les informations relatives au serveur, au client et autres.

3.1 En gnral
En ASP.NET nous avons plusieurs objet qui nous permettent de rcuprer des informations et pour certaine de les dfinir et/ou modifier. Ces objets contiennent des informations sur tout ce qui touche la communication entre le client (navigateur) et le serveur. Voyons ces objets un peu plus en dtails : Objets Application Dfinition Cet objet permet laccs un cache de lapplication qui peut tre lu et crit par nimporte quel utilisateur (indiffremment des sessions quils possdent). On sen sert pour stocker des informations qui ne sont pas spcifiques lutilisateur mais qui serviront pour tous. Ce qui est stock dans ce dictionnaire est accessible de nimporte quel endroit de lapplication. Cet objet permet daccder tout ce qui correspond au contexte actuel de lapplication. Pour faire simple cest un autre moyen daccder aux objets Session, Request, Response, Server, etc. . Ainsi quaux erreurs qui ont pu se produire dans lapplication. Il nous permet daccder tout ce qui touche la requte de lutilisateur. Il nous donne donc laccs tout ce qui a t envoy par le navigateur : header, cookies, query string, etc. Celui-ci vient la suite du Request puisquil nous permet daccder tout ce qui va tre envoy au navigateur du client la suite de sa requte. On pourra donc 23 septembre 2008

Context

Request

Response

19

Le Dveloppement dune Application Web

Server

Session

Trace

modifier les headers, ajouter du texte la page, insrer des cookies, etc. Fourni laccs la dernire erreur du serveur ainsi qua des mthodes du serveur. Entre autre il permet deffectuer une redirection sur le serveur (c'est--dire que le client aura le nom de la page quil a demand quil en verra une autre : le serveur lui a envoy la page sur laquelle on aura redirig). Il permet aussi de rcuprer la racine du serveur avec sa mthode MapPath. Il nous permet de travailler sur la session de lutilisateur. Un utilisateur possdant une session possde aussi un identifiant de session. Celui-ci est stock sur le client. Cet objet va nous permettre de dfinir, entre autres, de quel manire il sera stock (cookies, url, etc.). Pour exemple on peut ajouter, diter ou supprimer des cookies dans la session dun utilisateur. Cet objet permet de rcuprer toutes les donnes disponibles et de les afficher. Par exemple on peut afficher toutes les variables session ou application avec leurs valeurs. Ou encore toutes les variables du serveur et autres.

Nous avons dj vu dans le chapitre prcdent (Gestion dtat) les objets Session et Application. Nous allons donc voir plus en dtails les objets Response, Request, Server et Context. 3.1.1 Response

Mthodes BinaryWrite AppendHeader Clear ClearContent End Flush

Redirect TransmitFile Write WriteFile WriteSubstitution

Dfinition Ecrit des caractres binaire dans le flux de rponse HTTP. Ajoute des headers dans le flux de rponse HTTP. Vider le flux de rponse HTTP. Supprimer le contenu du flux HTTP. Cela n'inclu pas les headers. Ferme le flux et envoie la page l'utilisateur. Envoie directement la page l'utilisateur sans fermer le flux. Ce systme est utilis par exemple pour envoyer une page en partie. par exemple pour le traitement d'un formulaire on peut crire dans le flux avec la mthode Write pour crire "En cours de traitement". Ensuite on appel cette mthode pour envoyer le flux alors que le traitement n'est pas fini. Permet de rediriger l'utilisateur vers une autre page. Il existe aussi la mthode Server.Transfer. Ecrit un fichier dans le flux HTTP sans le mettre en buffer. Permet d'crire dans le flux HTTP avec buffer. Permet de rediriger l'utilisateur. Remplace un string dans le flux de rponse.

23 septembre 2008

20

Le Dveloppement dune Application Web

Proprits Cookies Buffer

Cache

Expires ExpiresAbsolute

Status / StatusCode

Dfinition Permet de lire / crire un cookie. Si cette proprit est True, la rponse sera mise en buffer avant d'tre envoy l'utilisateur. Si elle est false, elle ne sera pas buffris et sera envoy en morceau l'utilisateur. Utile si vous devez envoyer une grande quantit de donne (il vaut mieux l'envoyer en morceau). Permet d'obtenir les options de mise en page (stratgie) de la page Web. Entre autre il y a le dlai d'expiration. Dlai en minutes aprs lequel le navigateur doit arrter la mise en cache de la page. Il correspond au dlai durant lequel, si l'utilisateur accde nouveau cette page, la page mise en cache sera raffich. Ce dlai dpass, la page n'est plus valide. Permet de lire ou crire le code HTTP du statut de la requte. Pour cela ce reporter au chapitre 1.

3.1.2

Request

Mthodes MapPath

SaveAs ValidateInput

Dfinition Dtermine un chemin, une adresse, vers un fichier en absolu (adresse complte) partir de la racine du serveur. Par exemple avec Default.aspx, il va retourner le chemin absolu sur le systme de ce fichier. Permet de sauvegarder la requte dans le fichier spcifi. Gnre une exception si les donnes envoyes par l'utilisateur prsentent un risque de danger. Par dfaut elle est active.

Proprits ApplicationPath Browser

ClientCertificate

Dfinition Rcupre le chemin sur le systme de la racine de l'application. Rcupre les informations relative au navigateur de l'utilisateur comme son nom, sa version, ses fonctionnalits etc. Si le client fourni un certificat de scurit, il le

23 septembre 2008

21

Le Dveloppement dune Application Web

Cookies FilePath Files Headers HttpMethod IsAuthenticated isLocal IsSecureConnection

LogonUserIdentity Params Path PhysicalApplicationPath PhysicalPath QueryString RawUrl / Url TotalBytes UrlReferrer UserAgent

UserHostAddress c UserHostName UserLanguages

rcupre. Permet de lire / crire un cookie. Rcupre le chemin virtuel de la requte courante. Rcupre une collection de fichier envoy par l'utilisateur. Rcupre une collection contenant les headers de la requte. Rcupre la mthode de transfert de donnes utilis par le client comme Get, Post ou Head. Rcupre un boolen qui dfini si l'utilisateur est authentifi ou non. Rcupre un boolen qui dfinit si l'utilisateur utilise un ordinateur sur le rseau local ou non. Rcupre un boolen qui permet de dire si la connexion utilis est scuris ou non (elle est scuris quand on passe par HTTPS). Rcupre l'objet Windowsidentity qui reprsente l'utilisateur courant. Rcupre une collection qui inclut les items QueryString, Form, ServerValidation et Cookies. Donne le chemin virtuel de la requte courante. Fourni le chemin physique vers le dossier racine de l'application. Rcupre le chemin physique de la requte courante. Rcupre une collection contenant les variables se trouvant dans l'url. Donne l'url de la requte courante. Rcupre la taille de la requte. Rcupre l'url de la page prcdente. Rcupre une chaine de caractres qui caractrise le navigateur (comprend aussi le nom du navigateur). Rcupre l'adresse IP du lient. Rcupre le nom du DNS du client. Rcupre la langue de l'utilisateur. Correspond un string stock dans le navigateur du client.

3.1.3

Server

23 septembre 2008

22

Le Dveloppement dune Application Web

Mthodes ClearError GetLasterror HtmlDecode HtmlEncode

MapPath

Transfer

UrlDecode UrlEncode UrlPathEncode

Dfinition Efface la dernire erreur Rcupre la dernire exception. Transforme les caractres comme &lt; en caractre (pour l'exemple : &lt; conne < ). Remplace les caractres <, > et & par une chaine de caractre qui sera traduite par le serveur. C'est utile pour la scurit. Par exemple si une personne essaye de rentrer du code avec du html qui sera plus tard afficher dans un label (comme dans son pseudo par exemple), et bien au lieu que le html soit excut il ne sera qu'affich. Retourne le chemin physique du fichier spcifi en chemin (le fichier est spcifi depuis la racine de l'application). Arrte l'excution de la page en cours et dmarre celui de la page spcifi. L'url ne sera donc pas chang pour l'utilisateur qui vera l'url de la page d'origine mme si le contenu est celui d'une autre page. Comme HtmlDecode sauf que c'est utilis sur l'url. Comme HtmlEncode sauf que c'est utilis sur l'url. Code la partie de l'url correspondant au chemin d'accs.

Voici un exemple utilisant la mthode HtmlEncode :


C#

protected void Page_Load(object sender, EventArgs e) { Label Label1 = new Label(); form1.Controls.Add(Label1); Label Label2 = new Label(); form1.Controls.Add(Label2); string str = @"<h1> test </h1>"; String myEncodedString; // On encode la chaine de caractres myEncodedString = Server.HtmlEncode(str); Label1.Text = "On affiche le string une fois qu'il a t encod " + myEncodedString + "<br />"; StringWriter myWriter = new StringWriter(); // On dcode la chaine de caractres Server.HtmlDecode(myEncodedString, myWriter); Label2.Text = "Voici le string une fois dcod :" + myWriter.ToString(); }

23 septembre 2008

23

Le Dveloppement dune Application Web

VB.NET

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Dim Label1 As Label = New Label() form1.Controls.Add(Label1) Dim Label2 As Label = New Label() form1.Controls.Add(Label2) Dim str As String = "<h1> test </h1>" Dim myEncodedString As String ' On encode la chaine de caractres myEncodedString = Server.HtmlEncode(str) Label1.Text = "On affiche le string une fois qu'il a t encod " + myEncodedString + "<br />" Dim myWriter As System.IO.StringWriter = New System.IO.StringWriter() ' On dcode la chaine de caractres Server.HtmlDecode(myEncodedString, myWriter) Label2.Text = "Voici le string une fois dcod :" + myWriter.ToString() End Sub

On obtient une page avec ceci dessus :

On remarque que le <h1> a t affich en caractres et non pas interprt comme si ctait du code avec lencodage. On remarquera dailleurs dans le code source de la page quil y a un bien un code qui reprsente les caractres < et > :
Code source de la page dans le navigateur

<span> On affiche le string une fois qu'il a t encod &lt;h1&gt; test &lt;/h1&gt;<br /> </span> <span>Voici le string une fois dcod :<h1> test </h1></span>

3.1.4

Context

Mthodes AddError ClearError

Dfinition Ajoute une exception la page. Efface la dernire erreur.

Proprits AllErrors IsCustomErrorEnabled

Dfinition Rcupre une collection d'exception non gr qui ont eu lieu sur la page. Obtient un boolen reprsentant le statut des erreurs personnaliss ( true elles sont activ)

23 septembre 2008

24

Le Dveloppement dune Application Web

IsDebuggingEnabled Timestamp

Obtient un boolen qui est true si le dbogage est activ. Rcupre le timestamp auquel la requte HTTP t cr.

3.2 Sur le navigateur


Il peut tre utile de savoir quel navigateur est utilis par vos visiteurs pour pouvoir leur proposer un site adapt. En effet tous les navigateurs nont pas le mme affichage voir nont pas les mmes fonctionnalits. Certain, par exemple, ne peuvent pas stocker de cookies, dautres ne possdent pas lActiveX. Si notre site en contient et quil nest pas possible pour le navigateur de les prendre en compte, lutilisation de votre site pourrait sen voir rduite. Savoir quel navigateur est utilis est donc primordial pour pouvoir proposer aux utilisateurs, soit une alternative qui leur permettra davoir tout de mme accs la fonctionnalit, soit de faire apparaitre un message derreur pour linformer quil ne peut pas y avoir accs et/ou lui recommandant dutiliser un autre navigateur. Aussi, afin duniformiser votre site sur tous les navigateurs, faudra t-il le tester sur plusieurs navigateurs (au moins les plus rpandu).

Pour rcuprer des informations sur le navigateur de lutilisateur, on va utiliser Request.Browser. Il contient plusieurs proprits utiles pour connaitre le nom du navigateur, ainsi que ses fonctionnalits. Ci-dessous un tableau recensant les proprits les plus utiles : Proprits ActiveXControls BackgroundSounds Browser Dfinition Renvoi un boolen indiquant si le navigateur peut utiliser ActiveX. Renvoi un boolen indiquant si le navigateur prend en charge le balise <bgsounds> qui permet de jouer des sons. Obtient la chaine de caractres qui caractrise le nom du navigateur (exemple : Firefox, IE ). Il est possible que certain navigateur nen ai pas ou que ce ne soit pas le bon. Nous le verrons un peu mieux plus tard. Obtient la valeur du Framework .Net install sur le client. Renvoi un boolen indiquant si le navigateur prend en charge les cookies. Renvoi un boolen indiquant si le navigateur est un moteur de recherche. Renvoi un boolen indiquant si le navigateur prend en charge les frames. Renvoi un boolen indiquant si le navigateur possde un affichage des couleurs. Renvoi un boolen indiquant si le navigateur est celui dun tlphone reconnu. Indique si le navigateur peut prendre en charge du Java. Indique si le navigateur peut prendre en charge du Javascript. 23 septembre 2008

ClrVersion Cookies Crawler Frames IsColor IsMobileDevice JavaApplets Javascript

25

Le Dveloppement dune Application Web

Version

Obtient le numro complet de la version du navigateur.

On peut par exemple utiliser lvnement Session_Start qui comme nous lavions vu, partir dun fichier asax, va nous permettre de dfinir avec du code behind ce que lon veut faire au dclenchement dune session. On va pouvoir ce moment vrifier si le navigateur accepte les cookies, et si tel nest pas le cas, utiliser un autre des moyens que nous avions vu (comme lurl o les champs cach) pour passer lidentifiant de session.

Voyons maintenant un exemple dans lequel nous allons rcuprer le nom et la version du navigateur pour lafficher :
C#

protected void Page_Load(object sender, EventArgs e) { Label Label1 = new Label(); Label1.Text = Request.Browser.Browser; Label1.Text += " "; Label1.Text += Request.Browser.Version; form1.Controls.Add(Label1); }
VB.NET

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Dim Label1 As Label = New Label() Label1.Text = Request.Browser.Browser Label1.Text += " " Label1.Text += Request.Browser.Version form1.Controls.Add(Label1) End Sub

Voici, ci-contre, ce que vous pouvez obtenir avec Internet Explorer et Mozilla Firefox. On aurait aussi bien pu changer

3.3 Les Headers


Les headers sont des informations qui sont stockes dans len-tte de la requte HTTP. ASP.NET nous permet de les lire et de les modifier. Il y a deux objets qui permettent daccder aux headers : lobjet Page et lobjet Response. Lobjet Response permet de modifier les headers dans le flux de rponse qui va tre envoy lutilisateur.

Voici le tableau des proprits de Page.Header. Il permet de rcuprer et de modifier les headers dj existant. Il ne permet pas den ajoutant ou den supprimer.

23 septembre 2008

26

Le Dveloppement dune Application Web

Proprits StyleSheet Title

Dfinition Permet de modifier le style qui sera utilis. Permet de modifier le titre de la page.

Plus exactement les headers sont les informations qui sont contenues dans la balise head. Aussi pour que lon puisse modifier ou lire ces informations depuis le code behind, il faut que cette balise possde la proprit runat="server".

Pour exemple voici comment changer le titre dune page depuis le code behind :
C#

protected void Page_Load(object sender, EventArgs e) { Page.Header.Title = "Test"; }


VB.NET

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Page.Header.Title = "Test" End Sub

4 Conclusion
A la suite de ce chapitre, nous sommes capable de rcuprer et/ou de modifier des informations relatives lapplication ou au client comme le navigateur utilis, ses possibilits, les headers quon envoi au client ou ceux quil a envoy . En fonction de lendroit o lon se trouve dans le code on na pas accs aux mmes objets, mais il est possible daccder de diffrentes faons la mme information. Ainsi on peut retrouver la mme mthode sur plusieurs objets. Le prochain chapitre va porter sur laffichage et les thmes, on y verra comment grer ce qui concerne laffichage et le systme des thmes (avec skin) fourni par ASP.NET.

23 septembre 2008

Das könnte Ihnen auch gefallen