Condividi tramite


Routing di ASP.NET

Aggiornamento: novembre 2007

Il routing di ASP.NET consente di utilizzare URL di cui non è necessario eseguire il mapping a specifici file in un sito Web. Di conseguenza, in un'applicazione Web è possibile utilizzare URL descrittivi dell'azione dell'utente e pertanto più facilmente comprensibili da parte di quest'ultimo.

In un'applicazione ASP.NET che non utilizza il routing, in genere viene eseguito il mapping di una richiesta in entrata di un URL a un file fisico su disco quale un file ASPX. Ad esempio, nel caso di una richiesta per https://server/application/Products.aspx?id=4, ne viene eseguito il mapping a un file denominato Products.aspx che contiene codice e markup per il rendering di una risposta nel browser. La pagina Web utilizza il valore della stringa di query id=4 per determinare il tipo di contenuto da visualizzare, ma è probabile che il valore abbia scarso significato per l'utente.

Nel routing di ASP.NET vengono definiti i modelli di URL che contengono segnaposto per i valori utilizzati durante la gestione delle richieste di URL. In fase di esecuzione le parti di URL che seguono il nome dell'applicazione vengono analizzate in valori discreti, in base a un modello di URL definito dall'utente. Ad esempio, nella richiesta per https://server/application/Products/show/beverages, il parser di routing può passare i valori Products, show e beverages a un gestore per la richiesta. Al contrario, in una richiesta non gestita dal routing di URL, il frammento /Products/show/beverages verrebbe interpretato come percorso di un file nell'applicazione.

È anche possibile utilizzare i modelli di URL per creare URL a livello di codice che corrispondano alle route, in modo da centralizzare la logica per la creazione di collegamenti ipertestuali nell'applicazione ASP.NET.

Confronto tra il routing di ASP.NET e la riscrittura degli URL

Il routing di ASP.NET è diverso dagli altri schemi di riscrittura degli URL. Nella riscrittura degli URL le richieste in entrata vengono elaborate mediante la modifica effettiva dell'URL prima che questo invii la richiesta alla pagina Web. Ad esempio, in un'applicazione che utilizza la riscrittura degli URL, un URL potrebbe essere modificato da /Products/Widgets/ in /Products.aspx?id=4. Inoltre, la riscrittura degli URL non dispone in genere di un'API per la creazione di URL basati su modelli personalizzati. Pertanto, se si modifica un modello di URL, è necessario aggiornare manualmente tutti i collegamenti ipertestuali che contengono l'URL originale.

Con il routing di ASP.NET, l'URL non viene modificato durante la gestione di una richiesta in entrata, poiché il routing può estrarre i valori dall'URL. Quando è necessario creare un URL, è possibile passare i valori di parametro in un metodo che genera l'URL automaticamente. Per modificare il modello di URL, è sufficiente modificarlo in un solo percorso e tutti i collegamenti creati nell'applicazione e basati su tale modello utilizzeranno automaticamente il nuovo modello.

Definizione delle route di URL

I modelli di URL definiti dall'utente sono noti come route. In una route vengono specificati i segnaposto di cui viene eseguito il mapping ai valori analizzati dalla richiesta di URL. È anche possibile specificare i valori costanti utilizzati per le richieste di URL corrispondenti.

In una route i segnaposto, denominati parametri URL, vengono definiti racchiudendoli tra parentesi graffe ( { e } ). Il carattere / viene interpretato come delimitatore quando l'URL viene analizzato. Le informazioni nella definizione di route, che non rappresentano un delimitatore e non sono racchiuse tra parentesi graffe, vengono considerate come valore costante. I valori estratti tra i delimitatori vengono assegnati ai segnaposto.

È possibile definire più segnaposto tra i delimitatori, ma devono essere separati da un valore costante. Ad esempio, {language}-{country}/{action} è un modello di route valido, mentre {language}{country}/{action} non è un modello valido in quanto non sono presenti costanti o delimitatori tra i segnaposto. Pertanto, il routing non è in grado di determinare la posizione in cui separare il valore del segnaposto language dal valore del segnaposto country.

Nella tabella riportata di seguito vengono illustrati modelli di route validi ed esempi di richieste di URL che corrispondono ai modelli.

Definizione di route

Esempio di URL corrispondente

{controller}/{action}/{id}

/Products/show/beverages

{table}/Details.aspx

/Products/Details.aspx

blog/{action}/{entry}

/blog/show/123

{reporttype}/{year}/{month}/{day}

/sales/2008/1/5

{locale}/{action}

/en-US/show

{language}-{country}/{action}

/en-US/show

In genere, si aggiungono route in un metodo chiamato dal gestore dell'evento Application_Start nel file Global.asax. Questo approccio garantisce la disponibilità delle route quando viene avviata l'applicazione e consente, inoltre, di chiamare il metodo direttamente quando si esegue lo unit test dell'applicazione. Se si desidera chiamare direttamente un metodo durante l'esecuzione dello unit test dell'applicazione, il metodo che registra le route deve essere statico (Shared in Visual Basic) e deve disporre di un parametro°RouteCollection.

È possibile aggiungere route aggiungendole alla proprietà Routes statica della classe RouteTable. La proprietà Routes rappresenta un oggettoRouteCollection in cui vengono archiviate tutte le route per l'applicazione ASP.NET. Nell'esempio riportato di seguito viene illustrato il codice di un file Global.asax in cui viene aggiunto un oggetto Route che definisce due parametri URL denominati action e categoryName.

Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
    RegisterRoutes(RouteTable.Routes)
End Sub

Shared Sub RegisterRoutes(routes As RouteCollection)
    Dim urlPattern As String
    Dim categoryRoute As Route

    urlPattern = "Category/{action}/{categoryName}"
    categoryRoute = New Route(urlPattern, New CategoryRouteHandler)

    routes.Add(categoryRoute)
End Sub
protected void Application_Start(object sender, EventArgs e)
{
    RegisterRoutes(RouteTable.Routes);
}

public static void RegisterRoutes(RouteCollection routes)
{
    routes.Add(new Route
    (
         "Category/{action}/{categoryName}"
         , new CategoryRouteHandler()
    ));
}

Impostazione dei valori predefiniti per i parametri di route

Quando si definisce una route, è possibile assegnare un valore predefinito per un parametro. Il valore predefinito viene utilizzato se nell'URL non è incluso un valore per tale parametro. È possibile impostare valori predefiniti per una route assegnando un dizionario alla proprietà Defaults della classe Route. Nell'esempio riportato di seguito viene illustrata una route con valori predefiniti.

Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
    RegisterRoutes(RouteTable.Routes)
End Sub

Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
    Dim urlPattern As String
    Dim categoryRoute As Route

    urlPattern = "Category/{action}/{categoryName}"
    categoryRoute = New Route(urlPattern, New CategoryRouteHandler)
    categoryRoute.Defaults = New RouteValueDictionary(New With _
        {.categoryName = "food", _
         .action = "show"} )

    routes.Add(categoryRoute)
End Sub
void Application_Start(object sender, EventArgs e) 
{
    RegisterRoutes(RouteTable.Routes);
}

public static void RegisterRoutes(RouteCollection routes)
{
  routes.Add(new Route
  (
     "Category/{action}/{categoryName}"
          new CategoryRouteHandler()
  )
    {
       Defaults = new RouteValueDictionary 
           {{"categoryName", "food"}, {"action", "show"}}
     }
  );
}

Quando il routing di ASP.NET gestisce una richiesta di URL, la definizione di route illustrata nell'esempio (con i valori predefiniti food per categoryName e show per action) produce i risultati elencati nella tabella riportata di seguito.

URL

Valori di parametri.

/Category

action = "show" (valore predefinto)

categoryName = "food" (valore predefinito)

/Category/add

action = "add"

categoryName = "food" (valore predefinito)

/Category/add/beverages

action = "add"

categoryName = "beverages"

Gestione di un numero variabile di segmenti

Talvolta è necessario gestire richieste di URL che contengono un numero variabile di segmenti di URL. Quando si definisce una route, è possibile specificare che l'ultimo parametro corrisponda al resto dell'URL contrassegnando il parametro con un asterisco (*). Tale parametro viene quindi definito catch-all. Una route con un parametro catch-all corrisponderà anche agli URL che non contengono valori per l'ultimo parametro. Nell'esempio riportato di seguito viene illustrato un modello di route che corrisponde a un numero sconosciuto di segmenti.

query/{queryname}/{*queryvalues}

Quando il routing di ASP.NET gestisce una richiesta di URL, la definizione di route illustrata nell'esempio produce i risultati elencati nella tabella riportata di seguito.

URL

Valori di parametri.

/query/select/bikes/onsale

nomequery = "select"

valoriquery = "bikes/onsale"

/query/select/bikes

nomequery = "select"

valoriquery = "bikes"

/query/select

nomequery = "select"

valoriquery = stringa vuota

Aggiunta di vincoli alle route

Oltre alla corrispondenza di una richiesta di URL a una definizione di route in base al numero di parametri nell'URL, è possibile specificare che i valori nei parametri soddisfino determinati vincoli. Se un URL contiene valori al di fuori dei vincoli di una route, tale route non viene utilizzata per gestire la richiesta. I vincoli vengono aggiunti per assicurarsi che i parametri URL contengano valori che verranno utilizzati nell'applicazione.

I vincoli vengono definiti mediante espressioni regolari o oggetti che implementano l'interfaccia IRouteConstraint. Quando si aggiunge la definizione di route all'insieme Routes, i vincoli vengono aggiunti mediante la creazione di un oggetto RouteValueDictionary che contiene il test di verifica. Questo oggetto viene quindi assegnato alla proprietà Constraints. La chiave nel dizionario identifica il parametro a cui si applica il vincolo. Il valore nel dizionario può essere una stringa che rappresenta un'espressione regolare o un oggetto che implementa l'interfaccia IRouteConstraint.

Se si fornisce una stringa, il routing la considera come espressione regolare e controlla l'eventuale validità del valore di parametro chiamando il metodo IsMatch della classe Regex. L'espressione regolare viene considerata sempre come priva di distinzione tra maiuscole e minuscole. Per ulteriori informazioni, vedere la classe Espressioni regolari di .NET Framework.

Se si fornisce un oggetto IRouteConstraint, il routing di ASP.NET controlla l'eventuale validità del valore di parametro chiamando il metodo Match dell'oggetto IRouteConstraint. Il metodo Match restituisce un valore Boolean che indica se il valore di parametro è valido.

Nell'esempio riportato di seguito vengono illustrati i vincoli che limitano i valori che possono essere inclusi nei parametri locale e year.

Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
    Dim urlPattern As String
    Dim reportRoute As Route

    urlPattern = "{locale}/{year}"
    reportRoute = New Route(urlPattern, New ReportRouteHandler)
    reportRoute.Constraints = New RouteValueDictionary(New With _
        {.locale = "[a-z]{2}-[a-z]{2}", .year = "\d{4}"})

    routes.Add(reportRoute) 
End Sub
void Application_Start(object sender, EventArgs e) 
{
    RegisterRoutes(RouteTable.Routes);
}

public static void RegisterRoutes(RouteCollection routes)
{
    routes.Add(new Route
    (
      "{locale}/{year}"
         , new ReportRouteHandler()
    )
       {
          Constraints = new RouteValueDictionary 
          {{"locale", "[a-z]{2}-[a-z]{2}"},{year, @"\d{4}"}}
       });
}

Quando il routing gestisce una richiesta di URL, la definizione di route illustrata nell'esempio precedente produce i risultati elencati nella tabella riportata di seguito.

URL

Risultato

/en-US

Nessuna corrispondenza. Sono necessari sia locale sia year.

/en-US/08

Nessuna corrispondenza. Il vincolo su year richiede 4 cifre.

/en-US/2008

locale = "en-US"

year = "2008"

Scenari in caso di non applicazione del routing

Per impostazione predefinita, il routing non gestisce richieste di cui viene eseguito il mapping a un file fisico esistente sul server Web. Ad esempio, una richiesta per https://server/application/Products/Beverages/Coffee.aspx non viene gestita dal routing se esiste un file fisico in corrispondenza di Products/Beverages/Coffee.aspx. Il routing non gestisce la richiesta anche se quest'ultima corrisponde a un modello definito, ad esempio {controller}/{action}/{id}.

Se si desidera che il routing gestisca tutte le richieste, anche quelle che puntano a file, è possibile sovrascrivere il comportamento predefinito impostando la proprietà RouteExistingFiles dell'oggetto RouteCollection su true. Quando si imposta questo valore su true, tutte le richieste che corrispondono a un modello definito vengono gestite dal routing.

È anche possibile specificare che il routing non gestisca determinate richieste di URL. Per impedire che il routing gestisca determinate richieste, è necessario definire una route e specificare che la classe StopRoutingHandler venga utilizzata per gestire tale modello. Quando una richiesta viene gestita da un oggetto StopRoutingHandler, l'oggetto StopRoutingHandler blocca tutte le altre elaborazioni della richiesta come route. La richiesta viene invece elaborata come pagina ASP.NET, servizio Web o altro endpoint ASP.NET. Ad esempio, è possibile aggiungere la definizione di route riportata di seguito per impedire che il routing gestisca richieste per il file WebResource.axd.

Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
    routes.Add(New Route("{resource}.axd/{*pathInfo}", New StopRouteHandler()))
End Sub
public static void RegisterRoutes(RouteCollection routes)
{
  routes.Add(new Route("{resource}.axd/{*pathInfo}", new StopRouteHandler()));
}

Modalità di corrispondenza tra gli URL e le route

Quando il routing gestisce le richieste di URL, tenta di creare una corrispondenza tra l'URL della richiesta e una route. La corrispondenza tra una richiesta di URL e una route dipende da tutte le seguenti condizioni:

  • Modelli di route definiti dall'utente o modelli di route predefiniti, se presenti, inclusi nel tipo di progetto.

  • Ordine in cui sono stati aggiunti all'insieme Routes.

  • Eventuali valori predefiniti forniti per una route.

  • Eventuali vincoli forniti per una route.

  • Eventuale definizione del routing per la gestione delle richieste che corrispondono a un file fisico.

Per evitare che una richiesta venga gestita dal gestore errato, è necessario considerare tutte queste condizioni quando si definiscono le route. L'ordine in cui gli oggetti Route vengono visualizzati nell'insieme Routes è significativo. La corrispondenza a una route viene tentata dalla prima all'ultima route nell'insieme e, quando si verifica, non vengono valutate altre route. In generale, è consigliabile aggiungere route alla proprietà Routes in un ordine che va dalle definizioni di route più specifiche a quelle meno specifiche.

Ad esempio, si supponga di aggiungere route con i seguenti modelli:

  • Route 1: {controller}/{action}/{id}

  • Route 2: products/show/{id}

La route 2 non gestirà mai una richiesta, poiché la route 1 viene valutata per prima e corrisponderà sempre alle richieste che potrebbero essere valide anche per la route 2. Una richiesta per https://server/application/products/show/bikes sembra corrispondere maggiormente alla route 2, ma viene gestita dalla route 1 con i seguenti valori:

  • controller = products

  • action = show

  • id = bikes

I valori predefiniti vengono utilizzati se manca un parametro nella richiesta. Pertanto, tali valori possono determinare la corrispondenza tra una route e una richiesta non prevista. Ad esempio, si supponga di aggiungere route con i seguenti modelli:

  • Route 1: {report}/{year}/{month}, con valori predefiniti per year e month.

  • Route 2: {report}/{year}, con un valore predefinito per year.

La route 2 non gestirà mai una richiesta. La route 1 potrebbe essere destinata a un report mensile, mentre la route 2 a un report annuale. Tuttavia, i valori predefiniti nella route 1 indicano che quest'ultima corrisponderà a qualsiasi richiesta che potrebbe essere valida anche per la route 2.

È possibile evitare l'ambiguità nei modelli includendo costanti, quali annual/{report}/{year} e monthly/{report}/{year}/{month}.

Se un URL non corrisponde a un oggetto Route definito nell'insieme RouteTable, il routing di ASP.NET non elabora la richiesta. L'elaborazione viene invece passata a una pagina ASP.NET, a un servizio Web o a un altro endpoint ASP.NET.

Creazione di URL dalle route

È possibile utilizzare le route per generare URL quando si desidera centralizzare la logica per la costruzione di URL. Un URL viene creato passando i valori di parametro come dizionario nel metodo GetVirtualPath dell'oggetto RouteCollection. Il metodo GetVirtualPath cerca la prima route nell'oggetto RouteCollection che corrisponde ai parametri nel dizionario. La route corrispondente viene utilizzata per generare l'URL. Nell'esempio riportato di seguito viene illustrata una definizione di route.

Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
    routes.Add(New Route( _
      "Category/{action}/{categoryName}", _
      New RouteValueDictionary(New With _
          {.categoryName = "food", _
           .action = "show"}), _
           New CategoryRouteHandler()) )
End Sub
public static void RegisterRoutes(RouteCollection routes)
{
  routes.Add(new Route
  (
     "Category/{action}/{categoryName}"
          new CategoryRouteHandler()
  )
    {
       Defaults = new RouteValueDictionary {{"categoryName", "food"}, 
           {"action", "show"}}
     }
  );
}

Nell'esempio riportato di seguito viene illustrato un controllo che crea un URL in base alla route.

Dim urlParameters As RouteValueDictionary

urlParameters = New RouteValueDictionary(New With {.categoryName = "beverages", _
        .action = "summarize"})
HyperLink1.href = RouteTable.Routes.GetVirtualPath _
    (context, urlParameters).VirtualPath
HyperLink1.href = RouteTable.Routes.GetVirtualPath
  (context,
  new RouteValueDictionary { 
    { "categoryName", "beverages" }, 
    {"action", "summarize" }}
  ).VirtualPath;

Quando viene eseguito questo codice, il controllo HyperLink1 conterrà il valore "Category/summarize/beverages" nella proprietà href.

Quando si crea un URL da una route, è possibile specificare la route da utilizzare includendo un nome per essa. Per ulteriori informazioni, vedere la classe Procedura: costruire un URL da una route.

Vedere anche

Concetti

Informazioni sull'infrastruttura ASP.NET