De sourcecode van dit artikel is hier beschikbaar: KeywordRedirects.zip
Deze keer niet iets wat ik voor een klant heb uitgezocht of wat in een training werd gevraagd, maar iets wat ik geresearched heb voor onze eigen website. Ik wilde een simpele manier om een pagina op te kunnen vragen op basis van een keyword. Zo wilde ik dat http://www.oosterkamp.nl/microsoftcsharp.aspx ook benaderbaar is op http://www.oosterkamp.nl/2124. Het Microsoft cursusnummer van de C# training is 2124. Deze nummers gebruiken wij intern en is dus handig om aan cursisten te laten zien welke vervolgtrainingen we hebben en wat de onderwerpen zijn die in die training aan bod komen. Ook is het handig om in geschreven communicatie simpele url's te kunnen gebruiken. Afijn, een lange inleiding om te komen tot de volgende probleemstelling:
hoe redirect ik op een onderhoudbare manier een URL dat eindigt op een keyword naar een bestaande aspx-pagina?
Mijn eerste idee was om UrlMappings in de web.config te gebruiken. Maar dat gaat niet werken. Een request voor een keyword heeft immers geen extensie en komt dus helemaal niet bij de ASP.NET Pipeline uit. Die wordt dus al door IIS afgehandeld met een prachtige 404. Dus dit werkt niet:
<configuration>
<system.web>
<urlMappings>
<add url="~/2124" mappedUrl="~/microsoftcsharp.aspx"/>
</urlMappings>
</
configuration> Dus er zit niets anders op dan tegen IIS te vertellen dat een 404 doorgestuurd moet worden naar een bepaalde pagina van de website. In IIS Manager (ik draai IIS 7 onder Vista), kun je voor elke Http error code instellen wat daar mee moet gebeuren.

Ik heb de 404 geredirect naar de URL 404Redirect.aspx. Let op dat je het pad van de virtual directory ook meeneemt in de url.
Dat is de eeste stap. Elke niet-bestaande pagina komt nu bij mijn 404Redirect..aspx uit. De volgende stap is om uit te vissen welk keyword werd meegegeven in het request. Het blijkt dat IIS dit meegeeft in de QueryString van 404Redirect.aspx. Als ik naar http://www.oosterkamp.nl/keyword browse, dan blijkt dit de volledige Url te zijn in 404Redirect.aspx:
http://localhost/TestApp/404Redirect.aspx?404;http://localhost:80/TestApp/keyword
Dus het keyword kunnen we er makkelijk af slopen: We zouden alles na de laatste slash kunnen nemen, maar ik heb er voor gekozen om het iets ingewikkelder te doen. Ik wilde ook support open houden voor keyword met slashes er in.
string[] parms = Request.Url.Query.Split( ';' );
if ( parms.Length == 2 && parms[ 0 ] == "?404" )
{
Uri uri = new Uri( parms[ 1 ] );
string keyword = uri.PathAndQuery;
keyword = keyword.Substring( Request.ApplicationPath.Length + 1 );
Response.Write( keyword );
}
Op deze manier weet ik dus het keyword dat in de url opgegeven werd. Volgende stap was om een onderhoudbare manier te verzinnen om een mapping te maken tussen een keyword en een url. Ik besloot om dat in de web.config op te nemen. Dat is de meest aangewezen plek er voor. Het zou wel zorgen voor een application restart als we een keyword zouden toevoegen, maar voor onze site is dat geen wezenlijk probleem. In .NET 2.0 is het veel eenvoudiger geworden om custom configuration sections te maken. Er zijn verschillende classes waar je van kan afleiden: ConfigurationElement, ConfigurationSection, ConfigurationElementCollection etc. Dit is wat ik in m'n web.config wilde hebben:
<configSections>
<section name="oosterkamp.web" type="Oosterkamp.Web.OosterkampWebSection" />
</configSections>
<oosterkamp.web>
<keywordRedirects>
<add keyword="bla" redirect="~/default.aspx" />
<add keyword="2124" redirect="~/microsoftcsharp.aspx" />
</keywordRedirects>
</oosterkamp.web>
Om dit te realiseren heb ik een aantal classes gemaakt. Ten eerste een class die de <add keyword="" redirect="" /> representeert:
public class KeywordRedirectionElement: ConfigurationElement
{
[ConfigurationProperty( "keyword" )]
public string Keyword
{
get { return (string) base["keyword"]; }
}
[ConfigurationProperty( "redirect" )]
public string Redirect
{
get { return (string) base["redirect"]; }
}
Je hoeft alleen maar af te leiden van ConfigurationElement en properties te maken met het ConfigurationPropertyAttribute. Dat mapt de property en het xml-attribuut op elkaar.
public class KeywordRedirectionCollection : ConfigurationElementCollection
{
public override ConfigurationElementCollectionType CollectionType
{
get
{
return ConfigurationElementCollectionType.AddRemoveClearMap;
}
}
protected override ConfigurationElement CreateNewElement()
{
return new KeywordRedirectionElement();
}
protected override object GetElementKey( ConfigurationElement element )
{
return (element as KeywordRedirectionElement).Keyword.ToLower();
}
public KeywordRedirectionElement this[ int index ]
{
get
{
return (KeywordRedirectionElement) BaseGet( index );
}
set
{
if ( BaseGet( index ) != null )
BaseRemoveAt( index );
BaseAdd( index, value );
}
}
public KeywordRedirectionElement this[ string keyword ]
{
get
{
return (KeywordRedirectionElement) this.BaseGet( keyword.ToLower() );
}
}
}
De collection is wat lastiger. Je leidt af van ColfigurationElementCollection en implementeert een aantal methods/properties:
-
CreateNewElement
Tsja, wat zou ie doen? :-)
-
GetElementKey
Bepaalt de key per item.
-
CollectionType
Bepaalt wat voor soort collectie het is. In dit geval een add/remove/clear collectie.
-
Een indexer om op index de collectie te kunnen bevragen.
-
Een indexer om een item op te kunnen zoeken op keyword.
En als laatste een class die de section representeert:
public class OosterkampWebSection: ConfigurationSection
{
[ConfigurationProperty( "keywordRedirects" )]
[ConfigurationCollection( typeof( KeywordRedirectionCollection ),
AddItemName="add", RemoveItemName="remove" )]
public KeywordRedirectionCollection KeywordRedirects
{
get
{
return (KeywordRedirectionCollection) base[ "keywordRedirects" ];
}
}
}
Dat is alle benodigde code om de keywords in de web.config op te nemen. Als laatste dan de code waarmee we in de 404Redirect.aspx uit de web.config halen naar welke pagina we moeten redirecten:
protected void Page_Load( object sender, EventArgs e )
{
OosterkampWebSection section =
(OosterkampWebSection) WebConfigurationManager.GetSection( "oosterkamp.web" );
string[] parms = Request.Url.Query.Split( ';' );
if ( parms.Length == 2 && parms[ 0 ] == "?404" )
{
Uri uri = new Uri( parms[ 1 ] );
string path = uri.PathAndQuery;
path = path.Substring( Request.ApplicationPath.Length + 1 );
KeywordRedirectionElement element = section.KeywordRedirects[ path ];
if ( element != null )
Response.Redirect( element.Redirect );
else
Response.StatusCode = 404;
}
That's all. Hiermee kunnen we nu eenvoudig keyword redirects toevoegen aan onze web.config om zo gebruikers van onze site makkelijk te onthouden urls te kunnen geven in onze advertenties bijvoorbeeld. Of om zelf op een eenvoudige manier bepaalde pagina's op te kunnen vragen.
Ennuh...voor iemand er wat over zegt: ik weet het, het werkt op onze site. Dit was maar een research-projectje. Maar dat duurt niet lang meer voor het ook echt op onze site werkt.