Curiosamente stamane Simone mi ha preceduto di un soffio nel parlarvi di
Url Rewriting. In questi giorni infatti mi sono trovato nella necessità di
soddisfare una particolare esigenza di rewriting che mi ha fatto approfondire
l'argomento e avevo in mente di scrivere questo post da un po'. Preso atto che
la scelta di Simone è ricaduta sull'implementazine di un IHttpModule, è evidente
che il mio post non è una ripetizione perchè in realtà il metodo che ho usato è
basato su una tecnica che nell'articolo di MSDN che Simone ha citato viene
appena sfiorata. Eccomi quindi a proporvi il metodo che ho recentemente
utilizzato per il rewriting dell'url in una applicazione che vedeva le proprie
pagine riportate parzialmente nel database e parzialmente sul filesystem.
La mia implementazione di rewrite è basata sulla creazione di
un HttpHandlerFactory. Se consideriamo un HttpModule come un
filtro che intercetta tutte le chiamate alle pagine di una applicazione, e un
HttpHandler un gestore per particolari tipi di chiamate che si
sostituisce a quello delle normali pagine, la HttpHandlerFactory invece
è una classe che viene usata dal runtime per istanziare un HttpHandler
specifico in base alla chiamata che esso riceve. Implementando l'interfaccia
IHttpHandlerFactory, in sostanza ci si pone nella HttpPipeline
al livello più alto possibile e si è in grado di decidere quale handler
istanziare in base all'url che si riceve ed in seguito passare ad esso la
chiamata.
Questa condizione nel mio caso era davvero esemplificativa. Si
trattava infatti di effettuare una semplice query nel database - query che con
l'implementazione di un meccanismo di caching è diventata unica in tutta la vita
dell'applicazione - e in base al risultato decidere se istanziare l'handler
delle pagine aspx normalmente oppure variando leggermente l'input per
ottenere oltre al rewrite, il caricamento di una specifica pagina in base a
quanto specificato nel database. Vedo di spiegarmi meglio; tanto per cominciare
vediamo come fa il runtime ad istanziare l'handler per le pagine aspx.
IhttpHandler
page =
PageParser.GetCompiledPageInstance(url, pathTranslated, context);
Per
mezzo di questa semplice riga, è possibile indicare al runtime di produrre una
istanza di IHttpHandler del tutto uguale a quella che produce esso
stesso in condizioni normali. I parametri indicano il path virtuale, il path
fisico della pagina e l'HttpContext della chiamata. Ecco quindi che in
realtà utilizzando tale metodo si può "imbrogliare" il runtime facendo caricare
ad esso un file che si trovi in un percorso diverso da quello reale che esso ha
calcolato sulla base della chiamata. Il trucco per fare il rewriting in questo
caso è di avere una singola pagina "dbpage.aspx", nella root
dell'applicazione cui redirigere tutte le chiamate che fanno riferimento al
database e - nel mio caso - rifilare al runtime una pagina
"fisica" scelta fra una rosa di possibili layout per mezzo di
un campo nel database stesso. Ecco il codice:
public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
{
PortalPage page = this.Indexer.FindPage(url);
if (page != null)
{
context.Items["CurrentPage"] = page;
return PageParser.GetCompiledPageInstance("~/dbpage.aspx", this.MapLayout(page.Layout), context);
}
return PageParser.GetCompiledPageInstance(url, pathTranslated, context);
}
public string MapLayout(string layoutName)
{
return string.Format("~/layouts/{0}.aspx", layoutName);
}
Il codice parla da solo, ma vediamo lo stesso di spendere due
parole. La prima riga reperisce l'istanza della PortalPage da un oggetto tenuto
in cache, che rappresenta la struttura caricata dal database una volta per
tutte. A questo punto, se la pagina viene trovata essa viene inserita nel
contesto della chiamata e poi viene effettuato il rewrite modificando l'url in
~/dbpage.aspx e passando come path fisico una diversa pagina aspx - che
per inciso contiene una serie di WebPartZones in un
ContentPlaceHolder. Nel caso in cui la pagina non esista nel database,
i parametri vengono passati così come sono ricevuti al PageParser
che quindi costruirà l'IHttpHandler consueto.
Risultato di questo giochetto è che parte delle pagine
dell'applicazione sono definite in una tabella del database e l'utente potrà
navigare tale struttura come se fosse fusa con quella sul filesystem stesso.
Non mi rimane di segnalarvi che questa tecnica è utilizzabile
anche con il framework 1.1, praticamente senza alcuna modifica.
powered by IMHO 1.3