Asp.net : Passer le viewstate en session

Le viewstate pose souvent des problèmes, parmi les plus connus :

  • Taille de page importante
  • Corruption du viewstate
  • Blocage au niveau de google qui n’aime pas trop ce champ
  • Champs non crypté par défaut, présentant une exposition et une altération possible des valeurs contenues dedans par un utilisateur mal intentionné.

Même si le meilleur conseil serait d’essayer de ne pas l’utiliser, il fait quand même partie du Framework et il faut composer avec.

Principe de base

L’idée est de surcharger le comportement du framework pour la gestion du viewstate afin de la passer en session. On pourrait imaginer tout autre mécanisme de stockage (bdd, fichier,…) mais la mémoire reste le moyen le plus rapide de travailler sur ces données.

Il faut quand même gérer certains cas liés aux sessions :

  • Un même utilisateur peut ouvrir plusieurs fois la même page, les viewstate doivent être différents
  • Les sessions peuvent être désactivées

Inconvénients :

La technique utilisée n’est pas parfaite, elle présente trois inconvénients :

– Il ne doit y a voir aucune balise <% %> contenue au sein même de la balise <form runat= « server »> dans les masters page, sans quoi vous aurez ce type de message :

La collection Controls ne peut pas être modifiée, car le contrôle contient des blocs de code (c’est-à-dire <% … %>)

– Si vous mettez en cache une page entière (au niveau aspx  avec un <%@ouputcache %>), il peut y avoir collision de viewstate si l’internaute ouvre plusieurs onglets sur la même page, toutes les pages auront le même viewstate. Si vous déportez le contenue de la page dans un usercontrol afin de gérer le cache dans ce dernier, plus de problème.

– Les pages doivent dériver d’une base page pour en profiter automatiquement

Mise en place :

Tous les codes sources sont en fin du billet

Tout d’abord on va avoir besoin d’un control dont le ClientID et le UniqueID ne varie jamais (possible dans asp.net 4.0 mais pas pour les framework précédent)

Dans notre base page on surcharge les méthodes qui chargent et sauvegardent le viewstate :

On surcharge ensuite le OnInit afin d’injecter notre contrôle personnalisé qui va maintenir pour nous l’identifiant unique de la page pour un internaute et une page vue

On crée un accesseur pour utiliser plus facilement la valeur de notre champ identifiant

C’est terminé !

Codes sources :
/// <summary>
/// hidden field that don"t respect cliendid and uniqueid  http://www.west-wind.com/WebLog/posts/4605.aspx
/// </summary>
public sealed class UniqueIDHiddenField : HiddenField
{
/// <summary>
/// Override to force simple IDs all around
/// </summary>
public override string UniqueID
{
get
{
return this.ID;
}
}

/// <summary>
/// Override to force simple IDs all around
/// </summary>
public override string ClientID
{
get
{
return this.ID;
}
}
}

/// Loads any saved view-state information to the Page object.
/// </summary>
/// <returns></returns>
protected override object LoadPageStateFromPersistenceMedium()
{
try
{
object viewStateBag;
//variation sur la page
string m_viewState = (string)Session["ViewState" + PageID];
LosFormatter m_formatter = new LosFormatter();
try
{
viewStateBag = m_formatter.Deserialize(m_viewState);
}
catch
{
throw new HttpException("The View State is invalid.");
}
return viewStateBag;
}
catch
{
return base.LoadPageStateFromPersistenceMedium();
}
}

/// <summary>
/// Saves any view-state and control-state information for the page.
/// </summary>
/// <param name="state"></param>
protected override void SavePageStateToPersistenceMedium(object state)
{
try
{
using (MemoryStream ms = new MemoryStream())
{
LosFormatter m_formatter = new LosFormatter();
m_formatter.Serialize(ms, state);
ms.Position = 0;
StreamReader sr = new StreamReader(ms);
string viewStateString = sr.ReadToEnd();
//variation sur la page
Session["ViewState" + PageID] = viewStateString;
ms.Close();
return;
}
}
catch
{
base.SavePageStateToPersistenceMedium(state);
}
}

private UniqueIDHiddenField hiddenPageIdentifier = new UniqueIDHiddenField();

/// <summary>
/// init
/// </summary>
/// <param name="e"></param>
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
if (this.Form != null)
{
//mise en place de l'indentifiant de page
hiddenPageIdentifier.ID = "____VSVAL";
if (!IsPostBack)
{
//on créer un nouvel identifiant aleatoire pour la page
System.Security.Cryptography.RNGCryptoServiceProvider rng = new System.Security.Cryptography.RNGCryptoServiceProvider();
byte[] buff = new byte[8];
rng.GetBytes(buff);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < buff.Length; i++)
{
sb.Append(buff[i].ToString("X2"));
}

hiddenPageIdentifier.Value = sb.ToString();
}
else
{
//on recupere l'identifiant recu en postback
hiddenPageIdentifier.Value = Request.Form[hiddenPageIdentifier.ID];
}

//on ajoute le control au form
this.Form.Controls.Add(hiddenPageIdentifier);

}

}

/// <summary>
/// get the unique page life id, initialized a load, persisted at postback
/// </summary>
public string PageID
{
get
{
return hiddenPageIdentifier.Value;
}

Une réflexion sur “Asp.net : Passer le viewstate en session

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s