Accueil
Rechercher:
sur developpez.com sur les forums
Forums | Tutoriels | F.A.Q's | Participez | Hébergement | Contacts
Club Emploi Blogs   TV   Dév. Web PHP XML Python Autres 2D-3D-Jeux Sécurité Windows Linux PC Mac
Accueil Conception Java DotNET Visual Basic  C  C++ Delphi MS-Office SQL & SGBD Oracle  4D  Business Intelligence
FORUMS .NET FAQs .NET TUTORIELS .NET SOURCES .NET LIVRES .NET OUTILS .NET BLOG .NET DOTNET TV

Tutoriel : Ecrire un module d'Url Rewriting pour asp.net en C#

Date de publication : 10/01/2008 , Date de mise à jour : 10/01/2008

Par Nico-pyright(c) (Page d'accueil)
 

Cet article a pour but de présenter les bases d'écriture d'un module d'url-rewriting.

1.Introduction
2.L'Url rewriting, qu'est-ce que c'est ?
3.Un exemple d'url rewriting simple
3.1.Rappels sur le global.asax
3.2.Réécriture
3.3.Exemple
4.Ecrire un module d'url rewriting
4.1.Rappels sur un module Http
4.2.Une implémentation de module d'url rewriting
4.3.Exemple d'un module url rewriting
5.Amélioration, utiliser le web.config pour paramétrer le module
5.1.Créer sa section personnalisée
5.2.Gestion de l'url rewriting simple dans le module
5.3.Amélioration, utilisation d'expressions régulières
5.4.Un peu plus loin avec les expressions régulières
5.5.Finalisation du module
6.Url rewriting et postback
6.1.Surcharge du contrôle HtmlForm
6.2.Utiliser un contrôle adapter
7.Une méthode de pseudo-réécriture d'url, utilisation de la propriété PathInfo
8.Configurer IIS pour l'url mapping
9.Conclusion
Remerciements
Contact


1.Introduction

Vous êtes un développeur Asp.Net utilisant le C# et vous souhaitez avoir des urls agréables à lire et facile à utiliser ? Ou bien améliorer votre référencement ?
Alors ce tutoriel est pour vous.

Vous apprendrez dans ce cours comment utiliser l'Url-Rewriting en C# pour un site web en asp.net. Vous apprendrez également à écrire un module paramétrable de réécriture d'url dans le web.config. Vous saurez également comment gérer les postbacks avec l'url rewriting.


2.L'Url rewriting, qu'est-ce que c'est ?

L'url rewriting (réécriture d'url) est la capacité d'intercepter une url qui n'existe pas physiquement sur le serveur pour la transformer en une url existante pour notre site afin que celle-ci soit plus facile à lire ou à écrire pour les utilisateurs.
Exemple : Imaginons que nous ayons un site web d'e-commerce et que nous pouvions accéder à la fiche technique d'un disque dur de 200 Go qui a pour identifiant : XB45BTO de cette façon :
http://monsite/ficheProduit.aspx?id=XB45BTO
Cette url n'est pas très explicite en soi, il aurait surement été préférable de pouvoir y accéder ainsi :
http://monsite/Produits/Disque-dur/Marque-X-200GO.html
Cela a en effet plusieurs avantages. Dans un premier temps, c'est un confort pour l'utilisateur qui est mieux averti de la fiche produit qu'il va visiter. C'est ensuite plus facile à repérer sur les moteurs de recherche.
Techniquement, une url réécrite permet de masquer le fonctionnement interne du site web. Dans notre exemple, on apprend que le site est fait en asp.net et qu'on passe des identifiants de produit dans l'url.
Avoir une url plus explicite et plus descriptive permet également d'améliorer son référencement.
Cette technique permet aussi d'éviter les liens morts. Un utilisateur peut par exemple avoir bookmarké le lien. En cas de changement d'url (par exemple changement de la structure interne du site), le lien bookmarké renverra vers une page inexistante. L'url rewriting aurait pu nous éviter ce souci.

L'url rewriting c'est donc la capacité d'un site à réécrire l'url
http://monsite/Produits/Disque-dur/Marque-X-200GO.html
en
http://monsite/ficheProduit.aspx?id=XB45BTO

3.Un exemple d'url rewriting simple

Le but est d'intercepter chaque url pour savoir s'il y a besoin de la réécrire pour qu'elle soit comprise par notre application. Nous devons donc être en mesure d'intercepter chaque demande de requête de notre application. C'est un des domaines que peut couvrir le global.asax.


3.1.Rappels sur le global.asax

Comme précisé dans la faq asp.net, la classe Global est la classe capable de gérer des évènements du niveau application. Elle se trouve dans un fichier appelé Global.asax (plus précisément Global.asax.cs pour C#).
Msdn nous dit que l'événement BeginRequest signale la création de toute nouvelle demande donnée. Cet événement est toujours déclenché et constitue systématiquement le premier événement à se produire au cours du traitement d'une demande.
C'est donc dans cet événement que devra intervenir notre réécriture d'url.

Ajoutons un fichier global.asax à notre application et surchargeons cet événement : click droit sur le projet -> add -> new item -> global application class

Il génère par défaut les événements Application_Start et Application_End. Nous pouvons les supprimer et ajouter l'événement Application_BeginRequest :
global.asax.cs
protected void Application_BeginRequest(object sender, EventArgs e)
{
}

3.2.Réécriture

Toute la mécanique de réécriture d'url d'asp.net repose sur la méthode : Context.RewritePath


3.3.Exemple

Dans cet exemple, nous allons simplement chercher à transformer une url de ce type :
http://monsite/nico.html
en
http://monsite/Default.aspx?name=nico
La page Default.aspx récupérera le paramètre dans l'url et l'affichera dans un label.
Nous allons donc ajouter un label dans le fichier Default.aspx :
Default.aspx
<asp:Label ID="leLabel" runat="server" />
Et on aura dans le code-behind le code pour récupérer le paramètre name et l'afficher dans le label :
Default.aspx.cs
protected override void OnInit(EventArgs e)
{
	string aValue = Request.QueryString["name"];
	if (!string.IsNullOrEmpty(aValue))
		leLabel.Text = string.Format("Le nom passé est : {0}", aValue);
	base.OnInit(e);
}
Notre réécriture, qui se passe dans l'événement Application_BeginRequest, va consister en :

  • vérifier que l'url se termine bien par .html
  • récupérer ce qu'il y a avant le .html
  • et réécrire l'url en Default.aspx et lui passer la chaine récupérée en paramètre name
soit :
global.asax.cs
protected void Application_BeginRequest(object sender, EventArgs e)
{
	string urlPath = Request.Url.ToString();
	if (urlPath.EndsWith(".html"))
	{
		int index = urlPath.LastIndexOf('/');
		string name = urlPath.Substring(index + 1, urlPath.LastIndexOf(".html") - index - 1);
		Context.RewritePath(string.Format("/Default.aspx?name={0}", name));
	}
}
Maintenant, quand vous taperez par exemple les urls suivantes :
url saisie dans le navigateur
http://monsite/nico.html
url saisie dans le navigateur
http://monsite/nico-pyright.html
Le site affichera :
affichée par la page
Le nom passé est : nico
affichée par la page
Le nom passé est : nico-pyright


4.Ecrire un module d'url rewriting

Pour l'instant, ce qu'on a fait, c'est utiliser l'événement d'application BeginRequest pour faire notre réécriture d'url.
Nous allons désormais écrire une classe module qui va se substituer à la classe d'application en la complétant pour gérer notamment les requêtes entrantes. Cette classe va implémenter l'interface IHttpModule et s'abonner à l'événement BeginRequest


4.1.Rappels sur un module Http

Comme décrit dans MSDN, un module HTTP est un assembly appelé sur chaque demande effectuée sur votre application. Les modules HTTP sont appelés dans le cadre du pipeline de demande ASP.NET et ont accès aux événements du cycle de vie de toute la demande. Par conséquent, les modules HTTP vous donnent la possibilité d'examiner les demandes entrantes et d'agir en fonction de la demande.
On a besoin de les déclarer dans le web.config de cette façon, à la section <system.web> :
web.config
<httpModules>
	<add name="le nom de mon module" type="namespace.classe, assembly"/>
</httpModules>
Comme d'habitude, il s'agit de déclarer notre module en précisant son namespace, sa classe et son assembly.


4.2.Une implémentation de module d'url rewriting

Pour écrire un module, il faut implémenter IHttpModule. Dans notre cas, il faudra également s'abonner à BeginRequest. Nous devrons ensuite récupérer le context pour procéder à la réécriture d'url.
Le minimum à écrire pour une telle classe est :
MonUrlRewriter.cs
public class MonUrlRewriter : IHttpModule
{
	public void Init(HttpApplication context)
	{
		context.BeginRequest += context_BeginRequest;
	}

	private void context_BeginRequest(object sender, EventArgs e)
	{
		HttpApplication application = sender as HttpApplication;
		HttpContext context = (application == null) ? null : application.Context;
		if (context != null)
		{
			HttpRequest request = context.Request;
			// ici faire l'url rewriting
		}
	}

	public void Dispose()
	{
	}
}
Pour que cette classe soit prise en compte par notre application, on va la définir dans le web.config à la section <system.web> :
web.config
<httpModules>
	<add name="MonUrlRewriter" type="demoModuleUrlRewriting.MonUrlRewriter, demoModuleUrlRewriting"/>
</httpModules>

4.3.Exemple d'un module url rewriting

pour faire l'équivalent de notre premier exemple, dans la méthode context_BeginRequest, on fera :
MonUrlRewriter.cs

private void context_BeginRequest(object sender, EventArgs e)
{
	HttpApplication application = sender as HttpApplication;
	HttpContext context = (application == null) ? null : application.Context;
	if (context != null)
	{
		HttpRequest request = context.Request;
		// ici faire l'url rewriting
		string urlPath = request.Url.ToString();
		if (urlPath.EndsWith(".html"))
		{
			int index = urlPath.LastIndexOf('/');
			string name = urlPath.Substring(index + 1, urlPath.LastIndexOf(".html") - index - 1);
			context.RewritePath(string.Format("/Default.aspx?name={0}", name));
		}
	}
}
				
Une fois le contexte récupéré, il s'agit du même code qui va récupérer la chaine représentant la page html et la passer en paramètre à Default.aspx.
Ainsi, notre application saura à nouveau rediriger une url de ce type :
http://monsite/nico.html
en
http://monsite/Default.aspx?name=nico
D'où, le site affichera :
Le nom passé est : nico


5.Amélioration, utiliser le web.config pour paramétrer le module

C'est bien beau tout ca, mais on se rend compte qu'on va vite se retrouver avec une usine à gaz si on a plusieurs réécriture à faire, plus ou moins complexe. On va vite se retrouver avec des if dans tous les sens, démultiplier les index of ...
Ca va vite devenir ingérable, difficile à maintenir et augmenter grandement les risques d'erreurs !

Une idée intéressante serait de généraliser le traitement et de déplacer le paramétrage dans le web.config histoire d'avoir juste à écrire un truc du genre dans le web.config pour paramétrer l'url rewriting :
web.config
<UrlRewriterConfigurationSection>
	<rewriting map="/home.html" to="~/Default.aspx" />
</UrlRewriterConfigurationSection>
qui permettrait la transformation de l'url home.html en Default.aspx.
Ce qui permettrait de rajouter facilement une autre transformation si besoin, sans rajouter de ligne de code. Par exemple, si j'ai besoin de mapper une url contact.html, j'aurai juste à rajouter (par exemple) :
web.config
<rewriting map="/contact.html" to="~/Default.aspx?action=contact" />

5.1.Créer sa section personnalisée

Pour avoir une telle section dans son web.config, il faut créer une section personnalisée. Implémenter une section de configuration personnalisée n'est pas si complexe que ca pourrait en avoir l'air.
Je vous renvoi en préambule à mon tutoriel sur les sections personnalisées pour savoir comment implémenter une telle section.

Dans notre cas, cela pourrait être :
fichiers ListeElement.cs, ListesElementCollection.cs et UrlRewriterConfigurationSection.cs
using System;
using System.Configuration;

namespace demoModuleSectionUrlRewriting
{
    public class UrlRewriterConfigurationSection : ConfigurationSection
    {
        private static readonly ConfigurationPropertyCollection _proprietes;
        private static readonly ConfigurationProperty _listes;

        static UrlRewriterConfigurationSection()
        {
            _listes = new ConfigurationProperty("", typeof(ListesElementCollection), 
				null, ConfigurationPropertyOptions.IsRequired | ConfigurationPropertyOptions.IsDefaultCollection);
            _proprietes = new ConfigurationPropertyCollection();
            _proprietes.Add(_listes);
        }

        public ListesElementCollection Listes
        {
            get { return (ListesElementCollection)base[_listes]; }
        }

        public new ListeElement this[string nom]
        {
            get { return Listes[nom]; }
        }

        protected override ConfigurationPropertyCollection Properties
        {
            get { return _proprietes; }
        }    
    }

    public class ListeElement : ConfigurationElement
    {
        private static readonly ConfigurationPropertyCollection _proprietes;
        private static readonly ConfigurationProperty _map;
        private static readonly ConfigurationProperty _to;

        static ListeElement()
        {
            _map = new ConfigurationProperty("map", typeof(string), null, ConfigurationPropertyOptions.IsKey);
            _to = new ConfigurationProperty("to", typeof(string), null, ConfigurationPropertyOptions.IsRequired);
            _proprietes = new ConfigurationPropertyCollection();
            _proprietes.Add(_map);
            _proprietes.Add(_to);
        }

        public string Map
        {
            get { return (string)base[_map]; }
            set { base[_map] = value; }
        }

        public string To
        {
            get { return (string)base[_to]; }
            set { base[_to] = value; }
        }

        protected override ConfigurationPropertyCollection Properties
        {
            get { return _proprietes; }
        }
    }

    public class ListesElementCollection : ConfigurationElementCollection
    {
        public override ConfigurationElementCollectionType CollectionType
        {
            get { return ConfigurationElementCollectionType.BasicMap; }
        }
        protected override string ElementName
        {
            get { return "rewriting"; }
        }

        protected override ConfigurationPropertyCollection Properties
        {
            get { return new ConfigurationPropertyCollection(); }
        }

        public ListeElement this[int index]
        {
            get { return (ListeElement)BaseGet(index); }
            set
            {
                if (BaseGet(index) != null)
                {
                    BaseRemoveAt(index);
                }
                base.BaseAdd(index, value);
            }
        }

        public new ListeElement this[string map]
        {
            get { return (ListeElement)BaseGet(map); }
        }

        public void Add(ListeElement item)
        {
            base.BaseAdd(item);
        }

        public void Remove(ListeElement item)
        {
            BaseRemove(item);
        }

        public void RemoveAt(int index)
        {
            BaseRemoveAt(index);
        }

        public void Clear()
        {
            BaseClear();
        }

        protected override ConfigurationElement CreateNewElement()
        {
            return new ListeElement();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            if (element != null)
                return ((ListeElement)element).Map;
            else
                return null;
        }
    }
}

5.2.Gestion de l'url rewriting simple dans le module

Maintenant que la section personnalisée est créée, il faut être capable de faire la réécriture d'url en allant lire notre section personnalisée dans l'événement BeginRequest, une fois le contexte récupéré comme d'habitude :
MonUrlRewriter.cs
string urlAbsolutePath = request.Url.AbsolutePath;
if (urlAbsolutePath.EndsWith(".html"))
{
	UrlRewriterConfigurationSection section = (UrlRewriterConfigurationSection)ConfigurationManager.GetSection("UrlRewriterConfigurationSection");
	foreach (ListeElement list in section.Listes)
	{
		if (urlAbsolutePath == list.Map)
		{
			context.RewritePath(list.To);
			break;
		}
	}
}
On récupère dans un premier temps l'url depuis le contexte. Et on boucle sur toutes sections. Si l'url courante correspond au mapping défini dans une section, alors on réécrit l'url avec la valeur correspondante de la section.

N'oubliez pas de déclarer la section dans le web.config, à savoir :
web.config
<configSections>
	<section name="UrlRewriterConfigurationSection" 
		type="demoModuleSectionUrlRewriting.UrlRewriterConfigurationSection, demoModuleSectionUrlRewriting" />
</configSections>
<UrlRewriterConfigurationSection>
	<rewriting map="/home.html" to="~/Default.aspx" />
</UrlRewriterConfigurationSection>
Ainsi, quand on utilise l'url suivante dans le site web :
http://monsite/home.html
il la transforme en :
http://monsite/Default.aspx
Rajoutons désormais dans le web.config la ligne suivante :
web.config
<rewriting map="/contact.html" to="~/Default.aspx?action=contact" />
et traitons dans Default.aspx l'action contact en affichant dans un label que nous sommes dans la page contact :
Default.aspx.cs
protected override void OnInit(EventArgs e)
{
	string aValue = Request.QueryString["action"];
	if (!string.IsNullOrEmpty(aValue) && aValue == "contact")
		leLabel.Text = "Nous sommes dans la page de contact";
	base.OnInit(e);
}
Ainsi, quand on utilise l'url suivante dans le site web :
http://monsite/contact.html
le site nous affiche :
Nous sommes dans la page de contact


5.3.Amélioration, utilisation d'expressions régulières

Dans le cas précédent, on fait un simple mapping. Pour améliorer et faire quelque chose d'un peu plus complexe, on peut utiliser la capacité du framework.net à gérer les expressions régulières.
Exemple :
Imaginons que j'ai une base de tutoriel qui soit accessible par l'url
~/Default.aspx?tutoriel=urlRewriting
Il serait plus pratique et meilleur pour le référencement d'avoir un lien :
/tutoriel/urlRewriting.html
On pourrait imaginer vouloir paramétrer notre webconfig ainsi :
web.config
<rewriting map="/tutoriel/.+.html" to="~/Default.aspx?tutoriel={0}"/>
Pour rappel, l'expression régulière .+ permet d'avoir tout ce qui contient plus d'une lettre.
Voici à quoi pourrait ressembler la partie url rewriting :
MonUrlRewriter.cs
string urlAbsolutePath = request.Url.AbsolutePath;
if (urlAbsolutePath.EndsWith(".html"))
{
	UrlRewriterConfigurationSection section = (UrlRewriterConfigurationSection)ConfigurationManager.GetSection("UrlRewriterConfigurationSection");
	foreach (ListeElement list in section.Listes)
	{
		Regex reg = new Regex(list.Map);
		if (reg.IsMatch(urlAbsolutePath))
		{
			string urlRewrited = string.Format(list.To, 
				System.IO.Path.GetFileNameWithoutExtension(VirtualPathUtility.GetFileName(request.Url.LocalPath)));
			context.RewritePath(urlRewrited);
			break;
		}
	}
}
La méthode isMatch nous permet de traiter uniquement les urls qui répondent aux critères définis.
On extrait ensuite la chaine saisie juste avant le .html (ceci est fait grâce aux méthodes de la classe path, en tant que fichier sans extension).
Et on la remplace dans l'url à mapper, grâce à {0} et à string.format.

Pour simuler l'affichage du tutoriel, dans le OnInit, on fera :
Default.aspx.cs
string aValue = Request.QueryString["tutoriel"];
if (!string.IsNullOrEmpty(aValue))
	leLabel.Text = string.Format("Voici le tutoriel {0}", aValue);
base.OnInit(e);
Ce qui fait que les urls suivantes :
http://monsite/tutoriel/urlRewriting.html
http://monsite/tutoriel/asp.net.html
				
Afficherons :
Voici le tutoriel urlRewriting
Voici le tutoriel asp.net


5.4.Un peu plus loin avec les expressions régulières

Une fonctionnalité bien pratique des expressions régulières est la méthode Replace. Pour rester simple, cette méthode nous permet de remplacer une valeur d'une expression régulière par une autre valeur.
Imaginons que je dispose d'une fonctionnalité de recherche implémentée sur mon site, accessible par :
~/Default.aspx?search=maRecherche
On voudrait avoir une url plus digeste du genre :
/search/maRecherche.html
on va paramétrer notre web.config de la sorte :
web.config
<rewriting map="/search/(.+).html" to="~/Default.aspx?search=$1"/>
et traiter le cas de cette manière :
MonUrlRewriter.cs
UrlRewriterConfigurationSection section = (UrlRewriterConfigurationSection)ConfigurationManager.GetSection("UrlRewriterConfigurationSection");
foreach (ListeElement list in section.Listes)
{
	Regex reg = new Regex(list.Map);
	if (reg.IsMatch(urlAbsolutePath))
	{
		context.RewritePath(reg.Replace(urlAbsolutePath, list.To));
		break;
	}
}
On boucle sur tous les éléments de notre section et si l'url matche, alors on remplace la valeur trouvée dans la chaine de destination. Ici, $1 sera remplacé par le contenu de (.+).

Ainsi, pour les urls suivantes :
http://monsite/search/nico.html
http://monsite/search/un%20truc%20avec%20des%20espaces.html
				
Le site affichera, pour un traitement dans le OnInit du genre :
Default.aspx.cs
string aValue = Request.QueryString["search"];
if (!string.IsNullOrEmpty(aValue))
	leLabel.Text = string.Format("Résultat de la recherche pour {0} : ....", aValue);
Affichage de la page
Résultat de la recherche pour nico : .... 
Résultat de la recherche pour un truc avec des espaces : .... 
On peut bien sur cumuler les paramètres de remplacement...

Les expressions régulières sont tellement puissantes qu'on pourrait imaginer rapidement une petite utilisation :
web.config
<rewriting map="/add/(.+)\+(.+).html" to="~/Default.aspx?value1=$1&amp;value2=$2" />
combiné à un traitement dans Default.aspx de ce genre :
Default.aspx.cs
string value1 = Request.QueryString["value1"];
string value2 = Request.QueryString["value2"];
if (!string.IsNullOrEmpty(value1) && !string.IsNullOrEmpty(value2))
{
	int intValue1;
	if (int.TryParse(value1, out intValue1))
	{
		int intValue2;
		if (int.TryParse(value2, out intValue2))
			leLabel.Text = string.Format("Résultat de {0} + {1} = {2}", intValue1, intValue2, intValue1 + intValue2);
		else
			leLabel.Text = string.Format("Paramètre incorrect, {0} doit être numérique", intValue2);
	}
	else
		leLabel.Text = string.Format("Paramètre incorrect, {0} doit être numérique", intValue1);
}
Nous aurons donc pour les urls suivantes :
http://monsite/add/15+18.html
http://monsite/add/15+0.html
				
les affichages suivants :
Résultat de 15 + 18 = 33 
Résultat de 15 + 0 = 15 


5.5.Finalisation du module

Les méthodes présentées ci-dessus pourraient avantageusement être combinées pour couvrir un paramétrage complexe.
Il pourrait d'ailleurs être intéressant d'utiliser des groupes de sections et des sous groupes pour faciliter le paramétrage.

Exemple pour un web.config de ce genre :
web.config
<configSections>
	<sectionGroup name="UrlRewriterConfigurationSection">
		<section name="Mapping" type="urlRewritingAll.UrlRewriterConfigurationSection, urlRewritingAll" />
		<section name="RegEx" type="urlRewritingAll.UrlRewriterConfigurationSection, urlRewritingAll" />
	</sectionGroup>
</configSections>
<UrlRewriterConfigurationSection>
	<Mapping>
		<rewriting map="/home.html" to="~/Default.aspx" />
		<rewriting map="/contact.html" to="~/Default.aspx?action=contact" />
	</Mapping>
	<RegEx>
		<rewriting map="/search/(.+).html" to="~/Default.aspx?search=$1"/>
		<rewriting map="/add/(.+)\+(.+).html" to="~/Default.aspx?value1=$1&amp;value2=$2" />
	</RegEx>
</UrlRewriterConfigurationSection>
On pourrait avoir notre module d'url rewriting ainsi :
MonUrlRewriter.cs
private void context_BeginRequest(object sender, EventArgs e)
{
	HttpApplication application = sender as HttpApplication;
	HttpContext context = (application == null) ? null : application.Context;
	if (context != null)
	{
		HttpRequest request = context.Request;
		string urlAbsolutePath = request.Url.AbsolutePath;
		if (urlAbsolutePath.EndsWith(".html"))
		{
			TraiteSectionMapping(urlAbsolutePath, context);
			TraiteSectionExpressionReguliere(urlAbsolutePath, context);
		}
	}
}

private void TraiteSectionMapping(string urlAbsolutePath, HttpContext context)
{
	UrlRewriterConfigurationSection section = 
		(UrlRewriterConfigurationSection)ConfigurationManager.GetSection("UrlRewriterConfigurationSection/Mapping");
	foreach (ListeElement list in section.Listes)
	{
		if (urlAbsolutePath == list.Map)
		{
			context.RewritePath(list.To);
			break;
		}
	}            
}
private void TraiteSectionExpressionReguliere(string urlAbsolutePath, HttpContext context)
{
	UrlRewriterConfigurationSection section = 
		(UrlRewriterConfigurationSection)ConfigurationManager.GetSection("UrlRewriterConfigurationSection/RegEx");
	foreach (ListeElement list in section.Listes)
	{
		Regex reg = new Regex(list.Map);
		if (reg.IsMatch(urlAbsolutePath))
		{
			context.RewritePath(reg.Replace(urlAbsolutePath, list.To));
			break;
		}
	}
}
Notez que l'on récupère les sous-sections avec GetSection et l'url Groupe/SousGroupe. Ensuite, pour chaque section, on boucle sur toutes les valeurs et on applique la règle de transformation pour réécrire l'url, s'il y a concordance.



6.Url rewriting et postback

Le problème d'un postback combiné à l'url rewriting c'est qu'il utilise l'attribut action de la balise form d'une page pour afficher la page après l'envoi du formulaire.
Ainsi, une page dont l'url aura été réécrite comme home.html (à la place de Default.aspx) qui enverrait un postback au serveur serait affichée après ce postback avec l'url Default.aspx puisque celle-ci est celle qui est enregistrée dans l'attribut action.
C'est tout à fait fonctionnel dans les cas simples, mais disgracieux pour l'utilisateur qui ne comprend pas d'où vient cette url qu'il ne connait pas, alors qu'il en utilisait une autre au départ.
Nous allons donc tacher de résoudre ce problème, en étudiant deux possibilités qui sont basées sur le même principe, à savoir : la modification du comportement de la balise Form pour ne pas réécrire l'attribut action, comme ca, elle conserverait l'url courante et pourrait afficher l'url correcte lors d'un postback


6.1.Surcharge du contrôle HtmlForm

Une première solution serait d'écrire un contrôle personnalisé qui va surcharger le contrôle HtmlForm et qui filtrera l'attribut action.
Une technique pour le faire est de créer une classe qui dérive de HtmlTextWriter et qui surcharge la méthode WriteAttribute pour filtrer l'attribut action. Nous utiliserons cette classe comme writer de notre HtmlForm.
Form.cs
public class Form : HtmlForm
{
	protected override void RenderAttributes(HtmlTextWriter writer)
	{
		NoActionWriter customWriter = new NoActionWriter(writer);
		base.RenderAttributes(customWriter);
	}
        
	private class NoActionWriter : HtmlTextWriter
	{
		public NoActionWriter(HtmlTextWriter writer) : base(writer.InnerWriter)
		{
		}

		public override void WriteAttribute(string name, string value, bool fEncode)
		{
			if (name != "action")
				base.WriteAttribute(name, value, fEncode);
		}
	}
}
Pour utiliser ce contrôle personnalisé, dans notre page aspx il faudra enregistrer le contrôle de cette façon :
Default.aspx
<%@ Register TagPrefix="MaForm" Namespace="demoUrlRewritingPostBack" Assembly="demoUrlRewritingPostBack" %>
Et entourer nos contrôles dans la page par la nouvelle balise Form (tout en ayant supprimé l'ancienne balise <form>):
Default.aspx
<MaForm:Form runat="server">
	<a href="/home.html">Aller sur /home.html</a>
	<div>
		<asp:TextBox ID="TextBox1" runat="server"/>
		<asp:Button ID="Button1" runat="server" Text="Envoyer" />
		<asp:Label ID="leLabel" runat="server"/>
	</div>
</MaForm:Form>
Cette solution montre dès à présent un inconvénient. Il faut remplacer toutes les balises form de notre application par notre contrôle personnalisé. Ce qui peut être très laborieux si la solution comporte beaucoup de pages, sauf si l'on dispose d'une master page, dans ce cas, il ne faudra le faire qu'une unique fois.



6.2.Utiliser un contrôle adapter

Une autre solution qui ne fonctionne qu'avec asp.net 2.0 est d'utiliser un fichier de définition de navigateur pour personnaliser le rendu de la balise form. (Plus de précisions sur les fichiers de définition de navigateur dans MSDN)

Ajouter le dossier spécial App_Browsers : click droit sur le projet -> add -> add asp.net folder -> App_Browsers

Et ajouter un nouveau fichier BrowserFile que nous allons appeler FormUrlRewriting.browser

Dedans, nous allons y mettre le type de l'adapter que nous allons utiliser pour le contrôle System.Web.UI.HtmlControls.HtmlForm.
FormUrlRewriting.browser
<browsers>
	<browser refID="Default">
		<controlAdapters>
			<adapter controlType=