Scarica il codice di esempio dell'articolo (.zip 16kb)
Già dalla prima beta di asp.net è diventato chiaro che finalmente i programmatori di scuola Microsoft avrebbero avuto a disposizione uno strumento per processare una richiesta ricevuta, interpretare l'Url, e restituire una pagina diversa da quella richiesta.
L'applicazione pratica di questa tecnica è quella di eliminare gli Url con richieste GET parametriche tipo http://www.miosito.it/pagina.aspx?id=11 e trasformarla in un più comprensibile http://www.miosito.it/pagina11.aspx, o ancora meglio, viste le ormai inderogabili necessità di indicizzazione da parte dei motori di ricerca, in una bella pagina del tipo http://www.miosito.it/pagina11_il_titolo_della_pagina.aspx
L'interfaccia IHttpHandlerFactory
Prima di partire con il codice, è bene dare un'occhiata a System.Web.IHttpHandlerFactory, l'interfaccia messa a disposizione dal framework per interpretare la richiesta di un URL.
L'interfaccia viene descritta dalla documentazione del Framework .Net in maniera molto criptica:
" ... L'unica funzione svolta da una classe che implementa l'interfaccia IHttpHandlerFactory è la creazione dinamica di nuovi oggetti di gestione, vale a dire di nuove istanze delle classi che implementano l'interfaccia IHttpHandler ..."
L'interfaccia espone 2 metodi pubblici:
Public Function GetHandler(ByVal context As HttpContext, ByVal requestType As String, ByVal url As String, ByVal pathTranslated As String) As IHttpHandler Public Sub ReleaseHandler(ByVal handler As IHttpHandler)
La Sub ReleaseHandler consente di riutilizzare gli oggetti dell'Http Handler, ma non approfondiamo la trattazione perché non ci è utile per quello che andiamo a realizzare.
La funzione GetHandler invece restituisce un nuovo oggetto IHttpHandler che elabora la richiesta e che implementa l'interfaccia IHttpHandler. Questi sono i 4 parametri:
- context - Istanza della classe HttpContext della richiesta corrente, dalla quale potete ottenere tutti gli oggetti della richiesta (Request, Response ecc.)
- requestType - Metodo di trasferimento dei dati HTTP, GET o POST, utilizzato dal client.
- url - L'Url della richiesta (es.: /cartella/pagina.aspx)
- pathTranslated - Proprietà PhysicalApplicationPath della risorsa richiesta, ossia il path fisico dell'Url della richiesta (es.: c:\inetpub\wwwroot\cartella\pagina.aspx)
A questo punto possiamo partire, perché la quantità di codice da scrivere è veramente limitata. Oltre alla classe che implementa l'interfaccia, dovremo andare a modificare il web.config dell'applicazione.
In questo articolo prendiamo come punto di arrivo quello di una pagina "virtuale" del tipo http://www.miosito.it/content11_il_titolo_della_pagina.aspx; le richieste per questo tipo di pagina saranno indirizzate alla pagina ~/Content.aspx
Imports System
Imports System.IO
Imports System.Web
Imports System.Web.UI
Public Class ContentUrlRewriting
Implements IHttpHandlerFactory
Public Function GetHandler(ByVal context As HttpContext, ByVal requestType As String, ByVal url As String, ByVal path As String) As IHttpHandler Implements IHttpHandlerFactory.GetHandler
Dim pagina As String = context.Request.MapPath("content.aspx")
Return PageParser.GetCompiledPageInstance(url, pagina, context)
End Function
Public Sub ReleaseHandler(ByVal handler As IHttpHandler) Implements IHttpHandlerFactory.ReleaseHandler
End Sub
End Class
Come vedete le operazioni vere e proprie da effettuare si riducono a 2 righe: nella prima recuperiamo il percorso fisico della pagina "reale", nella seconda invece andiamo ad utilizzare il metodo statico GetCompiledPageInstance della classe System.Web.UI.PageParser, per ottenere un IHttpHandler, e in particolare un'istanza della nostra pagina.
E' molto importante capire che NON stiamo trasferendo l'esecuzione della pagina, ma che invece stiamo generando un'istanza della pagina (è un oggetto come altri) e la stiamo "passando" ad ASP.Net per effettuare il rendering e trasferirla al client.
Public Shared Function GetCompiledPageInstance(ByVal virtualPath As String, ByVal inputFile As String, ByVal context As System.Web.HttpContext) As System.Web.IHttpHandler
La classe è Sealed, per cui non possiamo ereditare da essa, e la funzione richiede 3 parametri:
- virtualPath - il path della richiesta; gli passiamo direttamente l'url esattamente come è stato fornito dal metodo.
- inputFile - qui gli passiamo il path fisico della pagina che abbiamo ottenuto.
- context - il context della richiesta; anche qui gli passiamo il parametro esattamente come è stato fornito dal metodo.
A questo punto possiamo passare alla compilazione, ad esempio della DLL UrlRewriting.dll.
Modifiche al web.config
Adesso non dobbiamo fare altro che effettuare il mapping della richiesta sul nostro nuovo handler factory. Dobbiamo semplicemente aggiungere una linea di codice nella sezione httpHandlers del web.config:
<configuration>
<system.web>
<httpHandlers>
<add verb="*" path="~/content*.aspx" type="ContentUrlRewriting, UrlRewriting" />
</httpHandlers>
</system.web>
</configuration>
Con questa linea di codice non facciamo altro che dire ad asp.net di indirizzare le pagine nella root dell'applicazione che iniziano per content e finiscono per .aspx, alla nostra classe ContentUrlRewriting contenuta nella Dll UrlRewriting.
La pagina Content.aspx
Già adesso tutte le richieste verranno reindirizzate alla pagina Content.aspx, ma se abbiamo passato un identificativo è evidente che ci servirà per fare qualche operazione, magari in un database.
Per cui dentro il Page_Load della pagina (o in una funzione) possiamo mettere questo semplice codice per recuperare l'identificativo:
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
dim id as integer
'es.: content11_il_titolo_della_pagina.aspx
'recuperiamo il nome completo della pagina senza estensione - es.: content11_il_titolo_della_pagina
Dim pagina As String = System.IO.Path.GetFileNameWithoutExtension(Request.Path)
'eliminiamo il termine content - es.: 11_il_titolo_della_pagina
Dim codice As String = pagina.Remove(0, 7)
If codice <> String.Empty Then
Try
If codice.IndexOf("_") > 0 Then
'eliminiamo il testo "superfluo"
codice = codice.Substring(0, (codice.IndexOf("_")))
End If
id = Int32.Parse(codice)
Catch
id = -1
End Try
End If
'
'codice ulteriore che utilizza l'ID
'
End Sub
Ecco come con poche righe di codice siamo riusciti ad ottenere un titolo della pagina più leggibile e soprattutto più efficace per il posizionamento sui motori di ricerca.