Routing e dynamic data control di ASP.NET 3.5 SP1

di Cristian Civera, in ASP.NET 3.5,


Questo articolo è tratto dal capitolo 29 (Routing e dynamic data control) del libro ASP.NET 3.5 per tutti di Daniele Bochicchio, Cristian Civera, Riccardo Golia e Stefano Mostarda.

Acquista subito la tua copia ad un prezzo vantaggioso!

ASP.NET 3.5 è una tecnologia che si basa sul CLR 2.0 e non è altro che un'evoluzione del .NET Framework 2.0, disponibile ormai da tre anni. Le solide basi e l'ottima architettura sulle quali si fonda permettono a Microsoft di poter aggiungere nuovi pezzi ad ASP.NET sfruttando i medesimi strumenti che lo sviluppatore ha, senza stravolgere quanto è già stato scritto.

Sebbene in genere tramite queste tipologie di aggiornamento si correggano solo bug, nel SP 1 di ASP.NET 3.5 sono state apportate piccole modifiche, ma soprattutto sono state aggiunte due nuove funzionalità al fine di rendere sempre più produttivo lo sviluppatore: Routing e dynamic data control.

Instradamento delle richieste mediante Routing

In applicazioni dal contenuto dinamico è frequente l'uso di parametri inseriti in querystring per caricare informazioni e generare pagine per ogni specifica richiesta. È il metodo più semplice, per esempio, per indicare il prodotto da caricare in una pagina di dettaglio, perché tale parametro si può leggere tramite Request.QueryString o un QueryStringParameter da associare ai controlli data source.

Per rendere però più comprensibile l'URL si tende ad evitare l'uso di querystring, sfruttando il percorso stesso dell'indirizzo per inserire parametri (come gli ID) e descrizioni o titoli che permettono all'utente di capire cosa la pagina contiene, oltre a permettere una migliore indicizzazione da parte dei motori di ricerca.

Per realizzare questa funzionalità con ASP.NET si usa una tecnica chiamata URL rewriting, che consiste, solitamente tramite HttpModule, nel riscrivere l'indirizzo richiesto dall'utente mappandolo su HttpHandler, dove normali pagine, estrapolando dall'indirizzo i parametri e passandoli con varie tecniche, solitamente facendo sempre uso di querystring. A partire da ASP.NET 2.0 è consentito mappare un indirizzo su un altro in modo molto semplice, tramite la sezione system.web/urlMapping, come mostrato nell'esempio seguente.

<urlMappings enabled="true">
  <add url= "~/product1.aspx"
    mappedUrl="~/products.aspx?ProductID=1"/>
</urlMappings>

Questa sezione purtroppo è abbastanza limitata e non permette scenari più complessi dove sono richieste la validazione dei valori predefiniti o l'uso di HttpHandler, costringendo lo sviluppatore alla creazione di un HttpModule personalizzato che implementi la logica specifica.

A partire da ASP.NET 3.5 SP1 è presente un nuovo assembly System.Web.Routing, per facilitare questi scenari, che contiene principalmente un modulo per l'instradamento delle richieste. Sebbene possa sembrare un rimpiazzamento del mapping degli URL, in realtà è qualcosa di più evoluto che non si limita alla semplice riscrittura dell'indirizzo, ma reimposta l'HttpHandler che prende in carico la richiesta. Tra URL rewritng e URL routing c'è quindi molta differenza ed è bene tenerlo a mente, sebbene il risultato sia spesso simile.

Innanzitutto per sfruttare questa nuova caratteristica è necessario referenziare il nuovo assembly e configurare nel web.config il modulo, come mostrato nell'esempio seguente.

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

Il modulo appena aggiunto intercetta tutte le richieste e utilizza una tabella, accessibile tramite la proprietà statica RouteTable.Routes, per ottenere la RouteData per ognuna di esse. La definizione di tale tabella può essere creata o modificata in qualsiasi momento, ma quello migliore è tipicamente l'avvio dell'applicazione, così da rendere operativi fin da subito gli instradamenti. La tabella da definire consiste in una coppia formata dal nome e dalla classe astratta RouteBase, la cui unica implementazione, Route, ammette un URL ed un oggetto di tipo IRouteHandler. Nell'esempio riportato di seguito viene mostrata una semplice definizione sull'indirizzo products/{ProductID} così da consentire, nonostante il percorso sul web server non esista, di poter richiamare uno specifico gestore per tutti gli indirizzi che seguono il percorso products seguito dall'ID del prodotto (per esempio http://miosito.it/products/1).

Mediante l'uso delle graffe si ha la possibilità di definire dei segnaposto nominali che vengono poi passati al gestore come parametri così da poter essere consumati per ogni specifica esigenza. Ogni segnaposto deve essere separato l'uno dall'altro da una costante, cioè da un qualsiasi carattere valido per gli Uri, così da permette all'interprete di individuare i valori. Non è valido quindi un percorso come products/{action}{ProductID}, ma lo è invece uno come products/{actions}-{ProductID}.

void Application_Start(object sender, EventArgs e)
{
  RouteTable.Routes.Add("productsRoute",
    new Route("products/{ProductID}",
    new ProductRouteHandler()));
}

La classe ProductRouteHandler dell'esempio mostrato è un tipo personalizzato che implementa l'interfaccia IRouteHandler e nel suo metodo GetHttpHandler definisce la logica per la creazione e la restituzione di un'istanza di IHttpHandler usata per l'effettiva esecuzione della richiesta. In ASP.NET 3.5 SP 1 sono già presenti due implementazioni di tale interfaccia:

  • System.Web.Routing.StopRoutingHandler: non restituisce alcun HttpHandler, non soddisfacendo di conseguenza la richiesta;
  • System.Web.DynamicData.DynamicDataRouteHandler: usato in unione ai dynamic data control, permette di creare maschere automatiche di consultazione e modifica dei dati.

Nell'esempio di visualizzazione dei prodotti appena preso in considerazione, la classe ProductRouteHandler ottiene l'HttpHandler chiamando il metodo CreateInstanceFromVirtualPath della classe BuildManager, per avere il risultato da una pagina chiamata products.aspx presente nell'applicazione web.

public class ProductRouteHandler : IRouteHandler
{
  IHttpHandler IRouteHandler.GetHttpHandler(RequestContext rc)
  {
    // Dal compilatore ASP.NET ottengo la pagina vera
    return BuildManager.CreateInstanceFromVirtualPath(
      "products.aspx", typeof(Page)) as IHttpHandler;
  }
}

L'oggetto che si ottiene è un'istanza della classe specializzata Page, perciò su di essa è possibile chiamare metodi o proprietà, per eseguire operazioni personalizzate o per passare i parametri ottenuti dai segnaposto dell'indirizzo, leggibili attraverso il dizionario requestContext.RouteData.Values.

Per poter passare i parametri alla classe dei prodotti occorre però far notare che per motivi di visibilità (i code file non sono visibili da classi definite in /App_Code/ o in assembly esterni) è necessario utilizzare un'interfaccia, definita come nell'estratto di codice a seguire, implementata dalla pagina sulla quale effettuare il cast.

public interface IProductPage : IHttpHandler
{
  int ProductID { get; set; }
}

La classe ProductRouteHandler va di conseguenza modificata, come mostrato nell'esempio seguente, per farle effettuare il cast su tale interfaccia e impostare la proprietà ProductID, consumabile a piacimento all'interno della pagina.

// Dal compilatore ASP.NET si ottiene la pagina vera
IProductPage p =
  BuildManager.CreateInstanceFromVirtualPath(
    "products.aspx", typeof(Page)) as IProductPage;

// le proprietà che servono alla pagina
p.ProductID = Convert.ToInt32(rc.RouteData.Values["ProductID"]);

La classe della pagina product.aspx a questo punto non deve far altro che implementare l'interfaccia IProductPage, definire la proprietà e interrogarla al posto della normale lettura della collezione Request.QueryString.

public partial class ProductsPage : Page, IProductPage
{
  protected void Page_Load(object sender, EventArgs e)
  {
    // Mostro il prodotto corrente
    description.Text = "Prodotto " + ProductID;
  }

  public int ProductID { get; set; }
}

Una possibile alternativa a questa tecnica può consistere nell'utilizzare HttpContext.Items, che in base ad una chiave permette di associare un valore per tutta la durata della richiesta. Concordando la chiave basta semplicemente scrivere nel dizionario i valori dei segnaposti per poi leggerli effettuandone il cast, con un minore impatto in termini di modifiche necessarie sulla pagina.

5 pagine in totale: 1 2 3 4 5
Contenuti dell'articolo

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