URL Rewriting implementing a HttpHandler
Visits: 9411
Friday, May 19, 2006


Page not translated!
I'm sorry but the text of this page doesn't have translation at this time.

Framework .NET 2.0

Basandomi su alcuni articoli trovati in rete, ho sviluppato un personalissimo handler HTTP per controllare le richieste dei client in modo da gestire i nomi delle pagine in maniera dinamica. Sto parlando ovviamente del cosidetto URL Rewriting. Si tratta, semplicemente, di utilizzare nomi di file inesistenti (ma utili) per la nostra applicazione web.
Prendiamo come esempio il mio blog: il link http://blog.devexperience.net/it/9/Caricare_un_file_EXE_ed_eseguirlo_in_memoria.aspx punta ad un file inesistente sul mio server. L'handler che ho sviluppato in pratica analizza il nome della pagina ricercata (nel nostro caso /it/9/Caricare_un_file_EXE_ed_eseguirlo_in_memoria.aspx) e visualizza al visitatore, se trovata, la corrispettiva vera pagina (che può essere, ad esempio, /Articolo.aspx?id=9). In questo modo Google (ma non è il solo) apprezzerà molto di più le pagine del nostro sito che avrà anche maggior pagerank.

Per il mio blog ho pensato ad una struttura URL del genere:

/ [lingua] / [ID] / [titolo].aspx

dove, mediante l'uso delle espressioni regolari, andrò a carpire quale articolo visualizzare al visitatore ed in quale lingua (richiamando la pagina Articolo.aspx?id=n). Il titolo dell'articolo viene utilizzato solo per ricreare il nome della pagina.

I passi da seguire

Per prima cosa occorre modificare il file web.config al nodo system.web:

<httpHandlers>
  <add verb="*" path="/it/*/*.aspx" type="devHttpHandler.HandleIt" />
  <add verb="*" path="/en/*/*.aspx" type="devHttpHandler.HandleIt" />
</httpHandlers>

In questo modo tutte le richieste effettuate dai client a qualsiasi file sotto le due cartelle it e en del mio blog, verranno redirette prima al mio handler devHttpHandler.HandleIt che potrà quindi controllarle ed effettuare le operazioni del caso.

Il secondo passo sarà quello di creare la classe per la verifica della richiesta HTTP. Creiamo quindi un nuovo file HttpHandler.cs nella nostra cartella App_Code:


using System;
using System.Web;
using System.Web.UI;
using System.Globalization;

namespace devHttpHandler
{
    public class HandleIt : IHttpHandlerFactory
    {
        public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
        {
            // ritorno l'IHttpHanlder
            // I parametri da passare sono:
            // virtualPath (il path della richiesta),
            // inputFile (il percorso fisico del file da richiamare), 
            // context (ritorno quello ricevuto al momento della richiesta)
            return PageParser.GetCompiledPageInstance("/Article.aspx", context.Server.MapPath("/Article.aspx"), context);
        }

        public void ReleaseHandler(IHttpHandler handler)
        {
        
        }
    }
}


Quando un client effettua una richiesta di pagina ASPX, il framework passa tale richiesta alla classe PageHandlerFactory. Quest'ultima implementa l'interfaccia IHttpHandlerFactory (quella che ho usato nell'esempio precedente) che possiede 2 metodi GetHandler e ReleaseHandler.
Il primo metodo ci permette di gestire la chiamata HTTP utilizzando questi 4 parametri:

  • context
    Istanza della classe HttpContext della richiesta corrente
  • requestType
    Metodo di trasferimento dati HTTP (GET o POST)
  • url
    L'URL della richiesta (ad es. il nostro famoso /it/9/Caricare_un_file_EXE_ed_eseguirlo_in_memoria.aspx)
  • pathTranslated
    Restituisce  il path fisico sul server della pagina richiamata

Il metodo GetCompiledPageInstance della classe PageParser ci permette di restituire l'IHttpHandler della richiesta indicando però a quale reale pagina farlo puntare (in pratica diciamo "non prendere la pagina Caricare_un_file_EXE_ed_eseguirlo_in_memoria.aspx ma Articolo.aspx").
Fatto questo sappiamo che, digitando ad esempio /it/0/prova.aspx, il framework reindirizzerà la richiesta del client alla classe appena implementata. Da questa poi verrà richiamata la pagina Articolo.aspx che analizzerà l'URL della richiesta che si comporterà di conseguenza. 

La pagina Articolo.aspx

Utilizzando l'evento PreInit della pagina, analizzo l'URL richiesto e, nel caso di uno complesso (es. /it/9/Caricare_un_file_EXE_ed_eseguirlo_in_memoria.aspx) visualizzo l'articolo di riferimento utilizzando l'ID trovato nel path (quel 9 infatti è l'ID dell'articolo nel database):


protected void Page_PreInit(object sender, EventArgs e)
{
    if (Request.Url.AbsoluteUri.IndexOf("/Articolo.aspx") > -1)
    {
        // pagina normale (es. /Articolo.aspx?id=1)
        
        // da fare...
        // controllare nel database se esiste l'articolo con questo ID
    }
    else
    {
        // pagina complessa, cerco l'ID dell'articolo nell'url
        FindIdArticolo();
    }
}
        

Il metodo FindIdArticolo mi permette di capire quale ID è presente nell'URL:


private void FindIdArticolo()
{
    string url = Request.Url.AbsoluteUri;
    Regex r = new Regex(@"/(en(glish)*|it(alian)*)/(?<id>\d+)/(?<nome>.*)\.aspx");
    if (r.IsMatch(url))
    {   
        int id = r.Match(url).Result("${id}");
        
        // da fare...
        // controllare nel database se esiste l'articolo con questo ID
    }
}
    


A questo punto abbiamo concluso (con poche righe di codice) la generazione dinamica di URL per il nostro sito. Google ne sarà contentissimo ;-)

Ps. Per velocizzare la scrittura di questo articolo non ho scritto molto codice di controllo (starà a voi implementarlo), spero comunque sia utile per capirne le basi.

Comments


Grazie Stavo proprio cercando di capire come fare per implementare una cosa del genere e il tuo esempio mi calza a pennello. Molto utile. Ciao
Written by miche - Wednesday, July 12, 2006 at 5:47 PM

Io avevo fatto una cosa simile... Ma usavo il web.config nel <urlMapping> degli add:
<urlMappings>
 <add url="~/Login.aspx" mappedUrl="~/Gestione/Login.aspx"/>
 <add url="~/Residenziale/Immobile.aspx" mappedUrl="~/Pagine/Residenziale.aspx"/>
 <add url="~/Appartamento/Immobile.aspx" mappedUrl="~/Pagine/Appartamento.aspx"/>
... ...
Written by DanKan - Tuesday, November 14, 2006 at 3:31 PM

Thanks...
Written by Kelly Miller - Wednesday, December 06, 2006 at 3:21 AM

Ciao, ho ritenuto questo metodo semplice e veloce, ma preciso che in VB nella classe non richiama la pagina Article ma articolo e poi una domanda come mai per farla funzionare ho dovuto mettere questo simbolo ~/articolo. CIAO GRAXIE
Written by Roberto - Thursday, May 03, 2007 at 11:05 AM

Il carattere ~ (Tilde) identifica la root della tua applicazione. In pratica "~/Articolo.aspx" indica la pagina "/Articolo.aspx". Utilizzare questo carattere risulta utile quando il sito si trova in sottocartelle (come ad esempio durante il debug delle applicazioni su VS2005 o Visual Web Developer). In questi casi il web-server si avvia in una sottodirectory del tipo: "http://localhost:0000/cartella/Default.aspx" e richiamare la pagina "~/Default.aspx" da qualsiasi posizione del sito ti permette di evitare l'uso del path assoluto "/" che, in questo caso, ti restituirebbe un 404 pagina non trovato (perché si trova in /cartella/Default.aspx). Ho scritto un pò in fretta.. spero sia abbastanza leggibile.. ;)
Written by ZofM - Thursday, May 03, 2007 at 11:47 AM

Ciao, funziona anche per normali pagine .asp o solo per .aspx?
Written by Marco - Friday, July 11, 2008 at 10:02 PM



Post new comment

Author:  
E-mail:
Site:
Remember my information:
Message:
Code verification: Password verification

 

-- Comment preview --


Versione italiana Versione italiana

CATEGORIES

Ajax (2)
ASP.NET (11)
C# Code (4)
IIS (1)
Silverlight (1)
Sql Server 2000 (1)
Varie (4)
Visual Studio 2005 (3)

ARCHIVE

May 2008 (1)
April 2008 (1)
January 2008 (4)
December 2007 (1)
May 2007 (1)
February 2007 (1)
December 2006 (3)
October 2006 (1)
September 2006 (3)
August 2006 (1)
June 2006 (1)
May 2006 (1)

ABOUT ME [curriculum]

Curriculum ZofM

CREDITS


Rimini Blog - Storie di vita Riminese

Powered by ASP.NET 2.0

Valid XHTML 1.0 Transitional

Feed RSS 2.0