Utilizzare le nuove funzionalità di URL Routing di ASP.NET 3.5 SP1

di Cristian Civera, in ASP.NET 3.5 SP 1, System.Web.Routing,

E' ormai di uso comune dare la possibilità di raggiungere le pagine di un'applicazione web attraverso URI più comprensibili per l'utente, per far riconoscere una pagina più facilmente, o ai motori di ricerca, per essere indicizzati più correttamente.
E' possibile implementare questa tecnica, conosciuta con il nome di URL rewriting, fin dalle prime versioni di ASP.NET attraverso un HTTP Module oppure, da ASP.NET 2.0, attraverso la sezione di configurazione system.web/urlMapping. Questi due mezzi non sono però mai stati ottimali per via di alcune problematiche, risolte solo con ASP.NET 2.0, riguardanti il PostBack, o per la scarsa elasticità della seconda soluzione.

Nel Service Pack 1 di ASP.NET 3.5 è presente però un nuovo assembly System.Web.Routing che contiene alcune nuovi classi, principalmente create e sfruttate da Dynamic Data e dalle applicazioni basate su MVC, che permettono di instradare le richieste su altri URI, effettuando in automatico il parsing delle variabili.
Si ponga per esempio di voler ricevere una richiesta GET su questo url, dove 1 è l'ID del prodotto:
http://miosito/products/1/descrizione%20prodotto

La richiesta in realtà sarà instradata alla pagina che mostra i prodotti, di nome products.aspx.

Per raggiungere questo scopo la nuova classe RouteTable contiene una proprietà statica Routes che permette di inserire una lista di Route che il modulo HTTP analizza per instradare le richieste. Prima di tutto bisogna quindi assicurarsi che il modulo sia installato nel web.config

<httpModules>
  <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</httpModules>

A seconda delle esigenze, nel global.asax, in un modulo HTTP o nel metodo statico AppInitialize, occorre poi popolare la collezione Routes. L'oggetto Route permette di indicare il template dell'URI, specificando tra graffe il nome della variabile e separando obbligatoriamente ogni campo da uno / (slash) o da - (trattino). Non è possibile iniziare con uno / o utilizzare il ?. Nell'esempio preso in considerazione l'URI deve essere quindi products/{idProduct}/{description}. Poiché description non è fondamentale per il funzionamento della pagina, è possibile renderlo facoltativo e dare un valore di default alla variabile, mentre alla variabile idProduct è possibile con una regex consentire un determinato input, ignorando tutte le richieste che non rispettano il vincolo. Ecco quindi il codice che inizializza l'istradamento della richiesta:

// Valori di default, se omessi l'url è comunque valido
// e la variabile ha il valore di default
RouteValueDictionary defaultValues = new RouteValueDictionary();
defaultValues.Add("description", "");

// Vincoli dell'uri
RouteValueDictionary constraints = new RouteValueDictionary();
// Solo richiesta HTTP GET
constraints.Add("httpMethod", "GET");
// Solo i numeri (regex) sono concessi come parametro idProduct
constraints.Add("idProduct", @"\d+");

// Eventuali parametri aggiuntivi da portare
RouteValueDictionary dataTokens = new RouteValueDictionary();
dataTokens.Add("destPath", "~/products.aspx");

// Lock in scrittura
using (RouteTable.Routes.GetWriteLock())
  // Preparo il route per tutta l'applicazione
  RouteTable.Routes.Add(new Route("products/{idProduct}/{description}",
    defaultValues,
    constraints,
    dataTokens,
    new MyRouteHandler()));

E' bene precisare che qualora una richiesta non rispetti i constraint o il template, l'utente non riceve un errore, ma un normale 404.
La classe MyRouteHandler è una classe costruita appositamente che implementa l'interfaccia IRouteHandler, richiamata dal motore di routing per ottenere il gestore effettivo della richiesta. Poiché il destinatario della richiesta è sempre la pagina products.aspx, tale implementazione non deve far altro che ottenere l'istanza della pagina, impostarne l'ID del prodotto tramite una proprietà tipizzata creata appositamente e restituirla al motore:

public class MyRouteHandler : IRouteHandler
{
  IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
  {
    // Uso il parametro aggiuntivo per sapere la pagina di destinazione vera
    string virtualPath = VirtualPathUtility.ToAbsolute(requestContext.RouteData.DataTokens["destPath"].ToString());
    // Dal compilatore ASP.NET ottengo la pagina vera
    IProductPage pp = BuildManager.CreateInstanceFromVirtualPath(virtualPath, typeof(Page)) as IProductPage;
    // Imposto le proprietà che servono alla pagina
    pp.IdProduct = Convert.ToInt32(requestContext.RouteData.Values["idProduct"]);
    return pp;
  }
}

Il tipo RouteData contiene le informazioni sul match della richiesta e permette di accedere alle variabile, mentre l'interfaccia IProductPage è stata creata appositamente ed è implementata dalla pagina products.aspx, poiché dai file presenti in App_Code non è possibile "vedere" le classi delle pagine ASP.NET.

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti

I più letti di oggi