URL Rewriting implementing a HttpHandler
Visite: 12618
venerdì 19 maggio 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.

Commenti


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
Scritto da miche - mercoledì 12 luglio 2006 alle ore 17.47

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"/>
... ...
Scritto da DanKan - martedì 14 novembre 2006 alle ore 15.31

Thanks...
Scritto da Kelly Miller - mercoledì 6 dicembre 2006 alle ore 3.21

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
Scritto da Roberto - giovedì 3 maggio 2007 alle ore 11.05

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.. ;)
Scritto da ZofM - giovedì 3 maggio 2007 alle ore 11.47

Ciao, funziona anche per normali pagine .asp o solo per .aspx?
Scritto da Marco - venerdì 11 luglio 2008 alle ore 22.02

Non è possibile fare altrettanto con risorse fittizie html? Ad esempio /it/9/Caricare_un_file_EXE_ed_eseguirlo_in_memoria.html Ho provato a sostituire l'estensione nell'Handler del web.config in questo modo ma non funziona. Grazie in anticipo
Scritto da Nicola - martedì 27 gennaio 2009 alle ore 18.26

@Nicola
Non è possibile fare altrettanto con risorse fittizie html? Ad esempio /it/9/Caricare_un_file_EXE_ed_eseguirlo_in_memoria.html Ho provato a sostituire l'estensione nell'Handler del web.config in questo modo ma non funziona.

Si, certo.. ma è necessario mappare l'estensione .HTML su IIS altrimenti questi file verranno considerati sempre come statici. Attenzione però: una volta mappati, verranno sempre eseguiti dall'isapi .net, quindi se si tenterà di aprire un vero file .html, esso andrà in errore, proprio come se fosse una pagina aspx mal scritta.
Scritto da ZofM - mercoledì 28 gennaio 2009 alle ore 8.42

Ciao, sto cercando di convertire il tuo UrlRewriting in VB.NET, purtroppo ricevo errore alla riga Implements IHttpHandlerFactory e l'errore è: "BC30149: Class 'HandleIt' deve implementare 'Function GetHandler(context As HttpContext, requestType As String, url As String, pathTranslated As String) As IHttpHandler' per l'interfaccia 'System.Web.IHttpHandlerFactory'."
Scritto da Fabio - sabato 21 marzo 2009 alle ore 0.11

Ciao, purtroppo non conosco benissimo VB ma hai già provato una cosa del genere?

Public Overridable Function GetHandler (context As HttpContext, requestType As String, url As String, pathTranslated As String) As IHttpHandler Implements IHttpHandlerFactory.GetHandle

?
Scritto da ZofM - sabato 21 marzo 2009 alle ore 11.30

Ciao, ho provato il tuo script in locale ottengo pagina non trovata in remoto: Security Exception The application attempted to perform an operation not allowed by the security policy. qualche idea?
Scritto da Daniele - venerdì 28 agosto 2009 alle ore 14.02



Scrivi nuovo commento

Autore:  
E-mail:
Sito:
Ricorda le mie informazioni:
Messaggio:
Verifica codice: Password verification

 

-- Anteprima commento --


Versione italiana Versione italiana

CATEGORIE

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

ARCHIVIO

aprile 2009 (1)
maggio 2008 (1)
aprile 2008 (1)
gennaio 2008 (4)
dicembre 2007 (1)
maggio 2007 (1)
febbraio 2007 (1)
dicembre 2006 (3)
ottobre 2006 (1)
settembre 2006 (3)
agosto 2006 (1)
giugno 2006 (1)

CHI SONO [curriculum]

Curriculum ZofM

RINGRAZIAMENTI