Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Di Ryan Nowak, Kirk Larkin e Rick Anderson
Note
Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 10 di questo articolo.
Warning
Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere .NET e .NET Core Support Policy. Per la versione corrente, vedere la versione .NET 10 di questo articolo.
ASP.NET Core controller usano il routing middleware per trovare le corrispondenze con gli URL delle richieste in ingresso ed eseguirne il mapping a actions. Modelli di route:
- Vengono definiti all'avvio in o negli attributi.
- Descrivere in che modo i percorsi URL vengono confrontati con le azioni.
- Vengono usati per generare URL per i collegamenti. I collegamenti generati vengono in genere restituiti nelle risposte.
Le azioni sono instradate convenzionalmente o instradate con attributi. Inserendo una route sul controller o sull'azione, diventa indirizzata tramite attributi. Per altre informazioni, vedere Routing misto.
Questo documento:
- Illustra le interazioni tra MVC e routing:
- Come le app MVC tipiche usano le funzionalità di routing.
- Copre entrambi:
- Il routing convenzionale viene in genere usato con controller e visualizzazioni.
- Routing degli attributi usato con API. Se sei principalmente interessato al routing per le API , vai alla sezione Routing degli attributi per le API .
- Vedere Routing per informazioni dettagliate sul routing avanzato.
- Fa riferimento al sistema di routing predefinito come routing degli endpoint. È possibile usare i controller con la versione precedente del routing a scopo di compatibilità. Per istruzioni, vedere la guida alla migrazione 2.2-3.0.
Impostare il percorso convenzionale
Il modello MVC ASP.NET Core genera un codice di routing convenzionale simile all'esempio seguente:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
Usare per creare un unico percorso. L'unico percorso è il percorso. La maggior parte delle app con controller e visualizzazioni usa un modello di route simile alla route. Le API devono usare il routing degli attributi.
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Modello di route :
Corrisponde a un percorso URL, ad esempio
Estrae i valori della route suddividendo il percorso in token. L'estrazione dei valori di route genera una corrispondenza se l'app ha un controller denominato e un'azione :
public class ProductsController : Controller { public IActionResult Details(int id) { return ControllerContext.MyDisplayRouteInfo(id); } }MyDisplayRouteInfo viene fornito dal pacchetto NuGet Rick.Docs.Samples.RouteInfo e visualizza le informazioni sulla route.
model collega il valore di per impostare il parametro su . Per altre informazioni, vedere Associazione di modelli.
definisce come valore predefinito .
definisce come valore predefinito .
Il carattere in definisce come facoltativo.
- I parametri di route predefiniti e facoltativi non devono necessariamente essere presenti nel percorso URL per trovare una corrispondenza. Per una descrizione dettagliata della sintassi del modello di route, vedere Riferimento al modello di route.
Trova la corrispondenza con il percorso URL .
Produce i valori di route .
I valori per e usano i valori predefiniti. non produce un valore perché non esiste alcun segmento corrispondente nel percorso URL. corrisponde solo se esiste un'azione e :
public class HomeController : Controller
{
public IActionResult Index() { ... }
}
Usando la definizione del controller e il modello di route precedenti, l'azione viene eseguita per i percorsi URL seguenti:
/Home/Index/17/Home/Index/Home/
Il percorso URL usa il controller e l'azione predefiniti del modello di route. Il percorso URL usa l'azione predefinita del modello di route.
Il metodo pratico :
app.MapDefaultControllerRoute();
Replaces:
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Important
Il routing viene configurato usando il middleware e . Per usare i controller:
- Chiamata per eseguire il mapping dei controller indirizzati con attributi.
- Chiamare o per eseguire il mapping di controller indirizzati convenzionalmente e di controller indirizzati mediante attributi.
Le app in genere non devono chiamare o . configura una pipeline middleware che esegue il wrapping del middleware aggiunto in usando e . Per altre informazioni, vedere Routing in ASP.NET Core.
Routing convenzionale
Usare il routing convenzionale con controller e visualizzazioni. Il percorso è:
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Il codice precedente è un esempio di route convenzionale. Si chiama routing convenzionale perché stabilisce una convenzione per i percorsi URL:
- Il primo segmento di percorso, , corrisponde al nome del controller.
- Il secondo segmento si mappa al nome dell'azione.
- Il terzo segmento viene usato per un facoltativo. l'oggetto in lo rende facoltativo. mappa un'entità modello.
Usando questa route, il percorso URL:
- corrisponde all'azione .
- esegue il mapping a e in genere il modello associa il parametro a 17.
Questa mappatura
- Si basa solo sui nomi di controller e azione.
- Non si basa su spazi dei nomi, percorsi dei file di origine o parametri del metodo.
Usando il routing convenzionale con la route predefinita, non è necessario creare un nuovo modello di URL per ogni azione. Per un'app con azioni di stile CRUD, avere coerenza per gli URL tra i controller:
- Semplifica il codice.
- Rende l'interfaccia utente più prevedibile.
Warning
Il modello di route definisce [la/il] ___ come facoltativo. Le azioni possono essere eseguite senza l'ID facoltativo fornito come parte dell'URL. In genere, quando viene omesso dall'URL:
- L'associazione di modelli imposta su .
- Nessuna entità viene trovata nel database corrispondente a .
Il routing basato su attributi fornisce un controllo granulare per rendere l'ID necessario per alcune azioni e non per altre. Per convenzione, la documentazione include parametri facoltativi come quando è probabile che vengano visualizzati nell'utilizzo corretto.
La maggior parte delle app dovrebbe scegliere uno schema di routing semplice e descrittivo in modo che gli URL siano leggibili e significativi. La route convenzionale predefinita :
- Supporta uno schema di routing semplice e descrittivo.
- È un punto iniziale utile per le app basate su interfaccia utente.
- È l'unico modello di route necessario per molte app dell'interfaccia utente Web. Per le app dell'interfaccia utente Web di grandi dimensioni, un'altra route che usa Aree è spesso tutto ciò che serve.
e :
- Assegnare automaticamente un valore di ordine agli endpoint in base all'ordine in cui vengono richiamati.
Routing degli endpoint in ASP.NET Core:
- Non ha il concetto di percorsi.
- Non fornisce garanzie sull'ordinamento per l'esecuzione dell'estendibilità del sistema. Tutti gli endpoint vengono elaborati contemporaneamente.
Abilitare la registrazione per verificare in che modo le implementazioni del routing predefinite, ad esempio , corrispondono alle richieste.
Il routing degli attributi è illustrato più avanti in questo documento.
Più route convenzionali
È possibile configurare più route convenzionali aggiungendo altre chiamate a e . Quando si aggiungono queste chiamate, è possibile definire più convenzioni o aggiungere route convenzionali dedicate a un'azione specifica, ad esempio:
app.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
La route nel codice precedente è un percorso convenzionale dedicato. Si tratta di un itinerario convenzionale dedicato perché:
- Usa il routing convenzionale.
- È dedicata a un'azione specifica.
Poiché il modello di route non include e come parametri:
- Possono avere solo i valori predefiniti.
- Questa route esegue sempre il mapping all'azione .
, e sono gli unici percorsi URL che corrispondono alla route del blog.
Nell'esempio precedente:
- La rotta ha una priorità maggiore rispetto all'altra per le corrispondenze, poiché si aggiunge per prima.
- È un esempio di routing dello stile Slug in cui è tipico avere un nome di articolo come parte dell'URL.
Warning
In ASP.NET Core il routing non è:
- Definire un concetto denominato route. aggiunge la corrispondenza della route alla pipeline middleware. Il middleware esamina il set di endpoint definiti nell'applicazione e seleziona la migliore corrispondenza per l'endpoint in base alla richiesta.
- Fornire garanzie sull'ordine di esecuzione delle estensioni, come o .
Vedere Routing per materiale di riferimento sul routing.
Ordine di routing convenzionale
Il routing convenzionale corrisponde solo a una combinazione di azioni e controller definiti dall'app. Questo approccio semplifica i casi in cui le route convenzionali si sovrappongono.
Quando si aggiungono route usando , e , gli endpoint ottengono automaticamente un valore dell'ordine in base all'ordine in cui si richiamano questi metodi. Le corrispondenze di una route visualizzata in precedenza nell'elenco hanno una priorità più alta. Il routing convenzionale dipende dall'ordine. In generale, posizionare prima i percorsi con aree perché sono più specifici rispetto alle rotte senza aree. Le rotte convenzionali dedicate con parametri di route catch-all, come
Warning
Un parametro catch-all può corrispondere erroneamente alle route a causa di un bug nel routing. Le app interessate da questo bug presentano le caratteristiche seguenti:
- Un percorso generico, ad esempio
- La route catch-all non riesce a gestire correttamente le richieste che dovrebbe soddisfare.
- La rimozione di altre route fa funzionare il percorso catch-all.
Consultare GitHub bug 18677 e 16579 per esempi di casi che riscontrano questo bug.
Una correzione di consenso esplicito per questo bug è contenuta in .NET Core 3.1.301 o versione successiva dell'SDK. Il codice seguente imposta un commutatore interno che corregge questo bug:
public static void Main(string[] args)
{
AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior",
true);
CreateHostBuilder(args).Build().Run();
}
// Remaining code removed for brevity.
Risoluzione di azioni ambigue
Quando due endpoint corrispondono tramite routing, il routing deve eseguire una delle operazioni seguenti:
- Scegliere il candidato migliore.
- Genera un'eccezione.
Per esempio:
public class Products33Controller : Controller
{
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[HttpPost]
public IActionResult Edit(int id, Product product)
{
return ControllerContext.MyDisplayRouteInfo(id, product.name);
}
}
Il controller precedente definisce due azioni che corrispondono:
- Percorso URL
- Indirizzare i dati .
Si tratta di un modello tipico per i controller MVC:
- visualizza un modulo per modificare un prodotto.
- elabora il modulo pubblicato.
Per risolvere la route corretta:
- viene selezionato quando la richiesta è un http .
- viene selezionato quando il verbo HTTP è qualsiasi altro. viene in genere chiamato tramite .
L'oggetto , viene fornito al routing in modo che possa scegliere in base al metodo HTTP della richiesta. rende una corrispondenza migliore rispetto a .
È importante comprendere il ruolo degli attributi, ad esempio . Gli attributi simili sono definiti per altri verbi HTTP. Nel routing convenzionale, le azioni spesso usano lo stesso nome di azione quando fanno parte di un modulo di presentazione, inviare il flusso di lavoro del modulo. Ad esempio, vedere Esaminare i due metodi di azione di modifica.
Se il routing non è in grado di scegliere il candidato migliore, genera un'eccezione ed elenca i più endpoint corrispondenti.
Nomi di route convenzionali
Le stringhe e negli esempi seguenti sono nomi di route convenzionali:
app.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
I nomi delle route assegnano alla route un nome logico. Il percorso denominato può essere usato per la generazione di URL. L'uso di una route denominata semplifica la creazione di URL quando l'ordinamento delle route potrebbe rendere complessa la generazione di URL. I nomi di route devono essere univoci a livello di applicazione.
Nomi di route:
- Non ha alcun impatto sulla corrispondenza degli URL o sulla gestione delle richieste.
- Vengono usati solo per la generazione di URL.
Il concetto di nome della route è rappresentato nel routing come IEndpointNameMetadata. I termini nome della route e nome dell'endpoint:
- Sono intercambiabili.
- Quello usato nella documentazione e nel codice dipende dall'API descritta.
Routing degli attributi per le API
Le API devono usare il routing degli attributi per modellare la funzionalità dell'app come set di risorse in cui le operazioni sono rappresentate da verbi HTTP.
Il routing con attributi usa un set di attributi per eseguire il mapping delle azioni direttamente ai modelli di route. Il codice seguente è tipico per un'API e viene usato nell'esempio seguente:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Nel codice precedente, chiami una funzione per eseguire il mapping dei controller con routing basato sugli attributi.
Nell'esempio seguente :
- corrisponde a un set di URL simili a quello che corrisponde alla route convenzionale predefinita.
public class HomeController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
[Route("Home/Index/{id?}")]
public IActionResult Index(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[Route("Home/About")]
[Route("Home/About/{id?}")]
public IActionResult About(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
L'azione viene eseguita per uno dei percorsi URL , , o .
In questo esempio viene evidenziata una differenza di programmazione chiave tra il routing degli attributi e il routing convenzionale. Il routing degli attributi richiede più dati per specificare una route. La route predefinita convenzionale gestisce le route in modo più conciso. Tuttavia, il routing degli attributi consente e richiede un controllo preciso dei modelli di route applicabili a ogni azione.
Con il routing degli attributi, i nomi dei controller e delle azioni non influenzano la corrispondenza dell'azione, a meno che non sia utilizzata la sostituzione di token. L'esempio seguente corrisponde agli stessi URL dell'esempio precedente:
public class MyDemoController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
[Route("Home/Index/{id?}")]
public IActionResult MyIndex(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[Route("Home/About")]
[Route("Home/About/{id?}")]
public IActionResult MyAbout(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Il codice seguente usa la sostituzione dei token per e :
public class HomeController : Controller
{
[Route("")]
[Route("Home")]
[Route("[controller]/[action]")]
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
[Route("[controller]/[action]")]
public IActionResult About()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Il codice seguente si applica al controller:
[Route("[controller]/[action]")]
public class HomeController : Controller
{
[Route("~/")]
[Route("/Home")]
[Route("~/Home/Index")]
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
public IActionResult About()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Nel codice precedente, i modelli di metodo devono anteporre o ai modelli di route. I modelli di route applicati a un'azione che iniziano con o non vengono combinati con i modelli di route applicati al controller.
Per informazioni sulla selezione del modello di route, vedere Precedenza del modello di route.
Nomi riservati per il routing
Le parole chiave seguenti sono nomi di parametri di route riservate quando si usano controller o pagine:
actionareacontrollerhandlerpage
L'uso di come parametro di route nel routing degli attributi è un errore comune. Questa scelta comporta comportamenti incoerenti e confusi con la generazione di URL.
public class MyDemo2Controller : Controller
{
[Route("/articles/{page}")]
public IActionResult ListArticles(int page)
{
return ControllerContext.MyDisplayRouteInfo(page);
}
}
La generazione di URL usa questi nomi di parametri speciali per determinare se un'operazione di generazione url fa riferimento a una pagina o a un controller.
Le parole chiave seguenti sono riservate nel contesto di una visualizzazione o di una pagina:
pageusingnamespaceinjectsectioninheritsmodeladdTagHelperremoveTagHelper
Non usare queste parole chiave per le generazioni di collegamenti, i parametri associati al modello o le proprietà di primo livello.
Modelli di verbo HTTP
ASP.NET Core include i modelli di verbi HTTP seguenti:
- [HttpGet]
- [HttpPost]
- [HttpPut]
- [HttpDelete]
- [HttpHead]
- [HttpPatch]
Modelli di route
ASP.NET Core includono i seguenti modelli di route:
- Tutti i modelli verbo HTTP sono modelli di route.
- [Route]
Routing degli attributi con attributi dei verbi HTTP
Prendere in considerazione il controller seguente:
[Route("api/[controller]")]
[ApiController]
public class Test2Controller : ControllerBase
{
[HttpGet] // GET /api/test2
public IActionResult ListProducts()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("{id}")] // GET /api/test2/xyz
public IActionResult GetProduct(string id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[HttpGet("int/{id:int}")] // GET /api/test2/int/3
public IActionResult GetIntProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[HttpGet("int2/{id}")] // GET /api/test2/int2/3
public IActionResult GetInt2Product(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Nel codice precedente:
- Ogni azione contiene l'attributo , che vincola solo la corrispondenza alle richieste HTTP GET.
- L'azione include il template, quindi lo aggiunge al template nel controller. Il modello del metodo è . Pertanto, questa azione corrisponde solo alle richieste GET per il modulo , , e così via.
[HttpGet("{id}")] // GET /api/test2/xyz public IActionResult GetProduct(string id) { return ControllerContext.MyDisplayRouteInfo(id); } - L'azione contiene il modello. La parte del modello vincola i valori di route alle stringhe che possono essere convertite in un numero intero. Una richiesta GET a :
- Non corrisponde a questa azione.
- Restituisce un errore 404 Non trovato .
[HttpGet("int/{id:int}")] // GET /api/test2/int/3 public IActionResult GetIntProduct(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
- L'azione contiene nel modello, ma non limita i valori che possono essere convertiti in un numero intero. Una richiesta GET a :
- Corrisponde a questa route.
- L'associazione di modelli non riesce a eseguire la conversione in un numero intero. Il parametro del metodo è integer.
- Restituisce una richiesta non valida 400 perché l'associazione di modelli non è riuscita a eseguire la conversione in un numero intero.
[HttpGet("int2/{id}")] // GET /api/test2/int2/3 public IActionResult GetInt2Product(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
Il routing degli attributi può usare attributi come , e . Tutti gli attributi del verbo HTTP accettano un modello di route. L'esempio seguente mostra due azioni che corrispondono allo stesso modello di route:
[ApiController]
public class MyProductsController : ControllerBase
{
[HttpGet("/products3")]
public IActionResult ListProducts()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpPost("/products3")]
public IActionResult CreateProduct(MyProduct myProduct)
{
return ControllerContext.MyDisplayRouteInfo(myProduct.Name);
}
}
Uso del percorso URL :
- L'azione viene eseguita quando il verbo HTTP è .
- L'azione viene eseguita quando il verbo HTTP è .
Quando si crea un'API, è raro dover usare un metodo di azione perché l'azione accetta tutti i metodi HTTP. Usare l'attributo di un verbo HTTP più specifico per indicare con precisione ciò che supporta l'API. I client delle API devono conoscere i percorsi e i verbi HTTP mappati a operazioni logiche specifiche.
Le API devono usare il routing degli attributi per modellare la funzionalità dell'app come set di risorse in cui le operazioni sono rappresentate da verbi HTTP. Questa progettazione significa che molte operazioni, ad esempio GET e POST nella stessa risorsa logica, usano lo stesso URL. Il routing degli attributi fornisce il livello di controllo necessario per progettare attentamente il layout dell'endpoint pubblico di un'API.
Poiché una route con attributi si applica a un'azione specifica, è facile fare in modo che i parametri siano richiesti come parte della definizione del modello di route. Nell'esempio seguente è necessario come parte del percorso URL:
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Azione :
- Viene eseguito con il percorso URL, ad esempio
- Non viene eseguito con il percorso URL .
L'attributo [Consumes] consente a un'azione di limitare i tipi di contenuto della richiesta supportati. Per altre informazioni, vedere Definire i tipi di contenuto di richiesta supportati con l'attributo Consumes.
Vedere Routing per una descrizione completa dei modelli di route e delle opzioni correlate.
Per maggiori informazioni su , consultare l'attributo ApiController.
Nome route
Il codice seguente definisce un nome di route di :
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Usare i nomi di route per generare un URL basato su una route specifica. Nomi di route:
- Non influenzare il modo in cui il routing verifica la corrispondenza degli URL.
- Vengono usati solo per la generazione di URL.
I nomi delle route devono essere univoci a livello di applicazione.
Confrontare il codice precedente con la route predefinita convenzionale, che definisce il parametro come facoltativo (). La possibilità di specificare con precisione le API presenta vantaggi, ad esempio consentire di inviare e a diverse azioni.
Combinare i percorsi attributo
Per rendere il routing degli attributi meno ripetitivo, combinare gli attributi di routing sul controller con gli attributi di routing sulle singole azioni. I modelli di route definiti nel controller vengono anteposti ai modelli di route delle azioni. Quando si inserisce un attributo di route nel controller, tutte le azioni nel controller usano il routing degli attributi.
[ApiController]
[Route("products")]
public class ProductsApiController : ControllerBase
{
[HttpGet]
public IActionResult ListProducts()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("{id}")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Nell'esempio precedente:
- Il percorso URL può corrispondere
- Il percorso URL può corrispondere a .
Entrambe queste azioni corrispondono solo a HTTP perché sono contrassegnate con l'attributo .
Modelli di route applicati a un'azione e che iniziano con o non vengono combinati con i modelli di route applicati al controller. L'esempio seguente corrisponde a un set di percorsi URL simili alla route predefinita.
[Route("Home")]
public class HomeController : Controller
{
[Route("")]
[Route("Index")]
[Route("/")]
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
[Route("About")]
public IActionResult About()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
La tabella seguente illustra gli attributi nel codice precedente:
| Attribute | Combina con | Definisce il modello di route |
|---|---|---|
[Route("")] |
Yes | "Home" |
[Route("Index")] |
Yes | "Home/Index" |
[Route("/")] |
No | "" |
[Route("About")] |
Yes | "Home/About" |
Ordine di route degli attributi
Il routing crea un albero e confronta tutti gli endpoint contemporaneamente.
- Le voci di percorso si comportano come se fossero posizionate in un ordine ideale.
- Le rotte più specifiche hanno una possibilità di essere eseguite prima delle rotte più generali.
Ad esempio, una route di attributi come è più specifica di una route di attributi come . La route ha priorità più alta, per impostazione predefinita, perché è più specifica. Usando il routing convenzionale, lo sviluppatore è responsabile dell'inserimento di route nell'ordine desiderato.
Le route degli attributi possono configurare un ordine usando la proprietà . Tutti gli attributi di route forniti dal framework includono . Le rotte vengono elaborate in base a un ordinamento crescente della proprietà . L'ordine predefinito è . Impostare una rotta tramite viene eseguita prima delle rotte che non impostano un ordine. Impostare una route utilizzando viene eseguito dopo l'ordinamento predefinito delle route.
Evitare di fare affidamento su. Se lo spazio URL di un'app richiede valori di ordine espliciti per instradare correttamente, è probabile che sia fonte di confusione anche per i clienti. In generale, l'instradamento basato sugli attributi seleziona il percorso corretto con l'allineamento dell'URL. Se l'ordine predefinito usato per la generazione di URL non funziona, l'uso di un nome di route come override è in genere più semplice rispetto all'applicazione della proprietà .
Si considerino i due controller seguenti che definiscono entrambi la route corrispondente :
public class HomeController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
[Route("Home/Index/{id?}")]
public IActionResult Index(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[Route("Home/About")]
[Route("Home/About/{id?}")]
public IActionResult About(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
public class MyDemoController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
[Route("Home/Index/{id?}")]
public IActionResult MyIndex(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[Route("Home/About")]
[Route("Home/About/{id?}")]
public IActionResult MyAbout(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
La richiesta tramite il codice precedente genera un'eccezione simile alla seguente:
AmbiguousMatchException: The request matched multiple endpoints. Matches:
WebMvcRouting.Controllers.HomeController.Index
WebMvcRouting.Controllers.MyDemoController.MyIndex
L'aggiunta a uno degli attributi di route risolve l'ambiguità:
[Route("")]
[Route("Home", Order = 2)]
[Route("Home/MyIndex")]
public IActionResult MyIndex()
{
return ControllerContext.MyDisplayRouteInfo();
}
Con il codice precedente, esegue l'endpoint . Per accedere a , richiedere . Note:
- Il codice precedente è un esempio di progettazione di routing non ottimale. Illustra la proprietà.
- La proprietà serve unicamente a risolvere l'ambiguità. Tale modello non può essere confrontato. È preferibile rimuovere il modello.
Per informazioni sull'ordine di route con Pages, vedere Pages: convenzioni di route e app: Ordine di route.
In alcuni casi, viene restituito un errore HTTP 500 con delle route ambigue. Utilizzare i log per vedere quali endpoint hanno causato il .
Sostituzione dei token nei modelli di route [controller], [azione], [area]
Per praticità, le route degli attributi supportano la sostituzione dei token racchiudendo un token tra parentesi quadre (, ). I token , e vengono sostituiti con i valori del nome dell'azione, del nome dell'area e del nome del controller dall'azione in cui si definisce la route:
[Route("[controller]/[action]")]
public class Products0Controller : Controller
{
[HttpGet]
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Nel codice precedente:
[HttpGet]
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
- Corrisponde
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
- Corrisponde
La sostituzione dei token viene eseguita come ultimo passaggio della compilazione delle route dell'attributo. L'esempio precedente si comporta come il codice seguente:
public class Products20Controller : Controller
{
[HttpGet("[controller]/[action]")] // Matches '/Products20/List'
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("[controller]/[action]/{id}")] // Matches '/Products20/Edit/{id}'
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Se stai leggendo questo argomento in una lingua diversa dall'inglese, comunicaci in questo GitHub problema di discussione se vuoi visualizzare i commenti del codice nella tua lingua nativa.
È anche possibile combinare route di attributi con ereditarietà. Questa combinazione è efficace quando si usa la sostituzione dei token. La sostituzione dei token si applica anche ai nomi di route definiti dalle route con attributi. genera un nome di route univoco per ogni azione:
[ApiController]
[Route("api/[controller]/[action]", Name = "[controller]_[action]")]
public abstract class MyBase2Controller : ControllerBase
{
}
public class Products11Controller : MyBase2Controller
{
[HttpGet] // /api/products11/list
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("{id}")] // /api/products11/edit/3
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Per verificare la corrispondenza del delimitatore letterale della sostituzione di token o , eseguirne l'escape ripetendo il carattere ( o ).
Usare un trasformatore di parametri per personalizzare la sostituzione dei token
È possibile personalizzare la sostituzione dei token usando un trasformatore di parametro. Un trasformatore di parametri implementa e trasforma il valore dei parametri. Ad esempio, un trasformatore di parametro personalizzato modifica il valore della route in :
using System.Text.RegularExpressions;
public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
public string? TransformOutbound(object? value)
{
if (value == null) { return null; }
return Regex.Replace(value.ToString()!,
"([a-z])([A-Z])",
"$1-$2",
RegexOptions.CultureInvariant,
TimeSpan.FromMilliseconds(100)).ToLowerInvariant();
}
}
è una convenzione del modello di applicazione che:
- Applicare un trasformatore di parametro a tutte le route degli attributi in un'applicazione.
- Personalizza i valori del token di route dell'attributo man mano che vengono sostituiti.
public class SubscriptionManagementController : Controller
{
[HttpGet("[controller]/[action]")]
public IActionResult ListAll()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Il metodo precedente corrisponde a .
l'oggetto viene registrato come opzione:
using Microsoft.AspNetCore.Mvc.ApplicationModels;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews(options =>
{
options.Conventions.Add(new RouteTokenTransformerConvention(
new SlugifyParameterTransformer()));
});
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
Per la definizione di slug, consultare le schede di documentazione di MDN su Slug.
Warning
Quando si usa per elaborare l'input non attendibile, passare un timeout. Un utente malintenzionato può fornire input a causando un attacco Denial-of-Service. ASP.NET Core API del framework che usano RegularExpressions passano un timeout.
Numerose route di attributi
Il routing con attributi supporta la definizione di più route che raggiungono la stessa azione. L'uso più comune è simulare il comportamento della route convenzionale predefinita come illustrato nell'esempio seguente:
[Route("[controller]")]
public class Products13Controller : Controller
{
[Route("")] // Matches 'Products13'
[Route("Index")] // Matches 'Products13/Index'
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
L'inserimento di più attributi di route nel controller significa che ognuno di essi si combina con ognuno degli attributi di route nei metodi di azione:
[Route("Store")]
[Route("[controller]")]
public class Products6Controller : Controller
{
[HttpPost("Buy")] // Matches 'Products6/Buy' and 'Store/Buy'
[HttpPost("Checkout")] // Matches 'Products6/Checkout' and 'Store/Checkout'
public IActionResult Buy()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Tutti i vincoli di route del verbo HTTP implementano .
Quando vengono posizionati più attributi di route che implementano su un'azione:
- Ogni vincolo di azione viene combinato con il modello di route applicato al controller.
[Route("api/[controller]")]
public class Products7Controller : ControllerBase
{
[HttpPut("Buy")] // Matches PUT 'api/Products7/Buy'
[HttpPost("Checkout")] // Matches POST 'api/Products7/Checkout'
public IActionResult Buy()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
L'uso di più route sulle azioni potrebbe sembrare utile e potente, è preferibile mantenere lo spazio URL dell'app di base e ben definito. Usare più route per le azioni solo se necessario, ad esempio, per supportare i client esistenti.
Definizione di parametri facoltativi, valori predefiniti e vincoli della route con attributi
Le route con attributi supportano la stessa sintassi inline delle route convenzionali per specificare i parametri facoltativi, i valori predefiniti e i vincoli.
public class Products14Controller : Controller
{
[HttpPost("product14/{id:int}")]
public IActionResult ShowProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Nel codice precedente applica un vincolo di route. L'azione viene confrontata solo in base ai percorsi URL, ad esempio . La parte del modello di route vincola tale segmento solo a numeri interi.
Per una descrizione dettagliata della sintassi del modello di route, vedere Riferimento al modello di route.
Attributi di route personalizzati con IRouteTemplateProvider
Tutti gli attributi della route implementano . Il runtime di ASP.NET Core:
- Cerca gli attributi nelle classi controller e nei metodi di azione all'avvio dell'app.
- Usa gli attributi che implementano per compilare il set iniziale di route.
Implementare per definire attributi di route personalizzati. Ogni consente di definire una singola route con un modello di route, un ordinamento e un nome personalizzati:
public class MyApiControllerAttribute : Attribute, IRouteTemplateProvider
{
public string Template => "api/[controller]";
public int? Order => 2;
public string Name { get; set; } = string.Empty;
}
[MyApiController]
[ApiController]
public class MyTestApiController : ControllerBase
{
// GET /api/MyTestApi
[HttpGet]
public IActionResult Get()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Il metodo precedente restituisce .
Usare il modello di applicazione per personalizzare le route degli attributi
Il modello di applicazione:
- Modello a oggetti creato all'avvio in .
- Contiene tutti i metadati usati da ASP.NET Core per instradare ed eseguire le azioni in un'app.
Il modello di applicazione include tutti i dati raccolti dagli attributi di route. I dati degli attributi di route vengono forniti dall'implementazione . Conventions:
- Può essere scritto per modificare il modello di applicazione per personalizzare il comportamento del routing.
- Vengono lette all'avvio dell'app.
Questa sezione illustra un esempio di base della personalizzazione del routing usando il modello di applicazione. Il codice seguente rende le route approssimativamente allineate alla struttura di cartelle del progetto.
public class NamespaceRoutingConvention : Attribute, IControllerModelConvention
{
private readonly string _baseNamespace;
public NamespaceRoutingConvention(string baseNamespace)
{
_baseNamespace = baseNamespace;
}
public void Apply(ControllerModel controller)
{
var hasRouteAttributes = controller.Selectors.Any(selector =>
selector.AttributeRouteModel != null);
if (hasRouteAttributes)
{
return;
}
var namespc = controller.ControllerType.Namespace;
if (namespc == null)
return;
var template = new StringBuilder();
template.Append(namespc, _baseNamespace.Length + 1,
namespc.Length - _baseNamespace.Length - 1);
template.Replace('.', '/');
template.Append("/[controller]/[action]/{id?}");
foreach (var selector in controller.Selectors)
{
selector.AttributeRouteModel = new AttributeRouteModel()
{
Template = template.ToString()
};
}
}
}
Il codice seguente impedisce l'applicazione della convenzione ai controller che sono instradati per attributo.
public void Apply(ControllerModel controller)
{
var hasRouteAttributes = controller.Selectors.Any(selector =>
selector.AttributeRouteModel != null);
if (hasRouteAttributes)
{
return;
}
Ad esempio, il controller seguente non usa :
[Route("[controller]/[action]/{id?}")]
public class ManagersController : Controller
{
// /managers/index
public IActionResult Index()
{
var template = ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
return Content($"Index- template:{template}");
}
public IActionResult List(int? id)
{
var path = Request.Path.Value;
return Content($"List- Path:{path}");
}
}
Il metodo :
- Non esegue alcuna operazione se il controller è instradato con attributi.
- Imposta il modello di controller in base a , con la base rimossa.
L'oggetto può essere applicato in :
using My.Application.Controllers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews(options =>
{
options.Conventions.Add(
new NamespaceRoutingConvention(typeof(HomeController).Namespace!));
});
var app = builder.Build();
Si consideri ad esempio il controller seguente:
using Microsoft.AspNetCore.Mvc;
namespace My.Application.Admin.Controllers
{
public class UsersController : Controller
{
// GET /admin/controllers/users/index
public IActionResult Index()
{
var fullname = typeof(UsersController).FullName;
var template =
ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
var path = Request.Path.Value;
return Content($"Path: {path} fullname: {fullname} template:{template}");
}
public IActionResult List(int? id)
{
var path = Request.Path.Value;
return Content($"Path: {path} ID:{id}");
}
}
}
Nel codice precedente:
- La base è .
- Il nome completo del controller precedente è .
- Imposta il modello del controller di su .
Può anche essere applicato come attributo in un controller:
[NamespaceRoutingConvention("My.Application")]
public class TestController : Controller
{
// /admin/controllers/test/index
public IActionResult Index()
{
var template = ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
var actionname = ControllerContext.ActionDescriptor.ActionName;
return Content($"Action- {actionname} template:{template}");
}
public IActionResult List(int? id)
{
var path = Request.Path.Value;
return Content($"List- Path:{path}");
}
}
Routing misto: routing con attributi e routing convenzionale
Le app di ASP.NET Core possono combinare insieme l'uso del routing di tipo convenzionale e del routing degli attributi. In genere, si usano route convenzionali per i controller che servono pagine HTML ai browser e il routing degli attributi per i controller che servono le API.
Le azioni vengono indirizzate in modo convenzionale o mediante l'uso di attributi. Quando si definisce una route sul controller o sull'azione, essa diventa instradata tramite attributi. Non è possibile raggiungere le azioni che definiscono le route degli attributi attraverso le route convenzionali e viceversa. Qualsiasi attributo di route nel controller esegue tutte le azioni nell'attributo controller indirizzato.
Il routing degli attributi e il routing convenzionale usano lo stesso motore di routing.
Routing con caratteri speciali
Il routing con caratteri speciali può causare risultati imprevisti. Si consideri ad esempio un controller con il metodo di azione seguente:
[HttpGet("{id?}/name")]
public async Task<ActionResult<string>> GetName(string id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null || todoItem.Name == null)
{
return NotFound();
}
return todoItem.Name;
}
Quando contiene i valori codificati seguenti, potrebbero verificarsi risultati imprevisti:
| ASCII | Encoded |
|---|---|
/ |
%2F |
|
+ |
I parametri di route non sono sempre decodificati in URL. Questo problema potrebbe essere risolto in futuro. Per altre informazioni, vedere questo problema di GitHub;
Generazione di URL e valori di ambiente
Le app possono usare le funzionalità di generazione url di routing per generare collegamenti URL alle azioni. La generazione di URL elimina l'hardcoding degli URL, rendendo il codice più robusto e manutenibile. Questa sezione è incentrata sulle funzionalità di generazione di URL fornite da MVC e illustrano solo le nozioni di base sul funzionamento della generazione di URL. Vedere Routing per una descrizione dettagliata della generazione di URL.
L'interfaccia è l'elemento sottostante dell'infrastruttura tra MVC e routing per la generazione di URL. Un'istanza di è disponibile tramite la proprietà nei controller, nelle visualizzazioni e nei componenti di visualizzazione.
Nell'esempio seguente l'interfaccia viene usata tramite la proprietà per generare un URL a un'altra azione.
public class UrlGenerationController : Controller
{
public IActionResult Source()
{
// Generates /UrlGeneration/Destination
var url = Url.Action("Destination");
return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
}
public IActionResult Destination()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Se l'app usa la route convenzionale predefinita, il valore della variabile è la stringa di percorso URL . Il routing crea questo percorso URL combinando:
- Valori di route della richiesta corrente, denominati valori di ambiente.
- I valori passati a e i valori sostituiti nel modello di route:
ambient values: { controller = "UrlGeneration", action = "Source" }
values passed to Url.Action: { controller = "UrlGeneration", action = "Destination" }
route template: {controller}/{action}/{id?}
result: /UrlGeneration/Destination
In ogni parametro del modello di route, il valore viene sostituito facendo corrispondere i nomi con i valori specificati e i valori di ambiente. Un parametro di route che non ha un valore può:
- Usare un valore predefinito se ne ha uno.
- Essere ignorato se è facoltativo. Ad esempio, dal modello di percorso .
La generazione dell'URL ha esito negativo se un parametro di route obbligatorio non ha un valore corrispondente. Se la generazione di URL non riesce per una route, viene tentata la route successiva finché non vengono tentate tutte le route o viene trovata una corrispondenza.
L'esempio precedente di presuppone il routing convenzionale. La generazione di URL funziona in modo analogo con il routing degli attributi, anche se i concetti sono diversi. Con il routing convenzionale:
- I valori di route vengono usati per espandere un modello.
- I valori di route per e in genere vengono visualizzati in tale modello. Ciò funziona perché gli URL corrispondenti al routing rispettano una convenzione.
L'esempio seguente usa il routing degli attributi:
public class UrlGenerationAttrController : Controller
{
[HttpGet("custom")]
public IActionResult Source()
{
var url = Url.Action("Destination");
return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
}
[HttpGet("custom/url/to/destination")]
public IActionResult Destination()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
L'azione nel codice precedente genera .
LinkGenerator è stato aggiunto in ASP.NET Core 3.0 come alternativa a IUrlHelper. offre funzionalità simili ma più flessibili. Ogni metodo in ha anche una famiglia di metodi corrispondente su .
Generazione di URL in base al nome dell'azione
Url.Action, LinkGenerator.GetPathByAction e tutti gli overload correlati sono progettati per generare l'endpoint di destinazione specificando un nome del controller e un nome di azione.
Quando si usa , il runtime fornisce i valori di route correnti per e :
- I valori di
e fanno parte sia dei valori ambientali che di altri valori. Il metodo usa sempre i valori correnti di e genera un percorso URL che instrada all'azione corrente.
Il routing tenta di usare i valori nei valori di ambiente per inserire informazioni non specificate durante la generazione di un URL. Si consideri un percorso come con i valori di ambiente .
- Il routing contiene informazioni sufficienti per generare un URL senza valori aggiuntivi.
- Il routing contiene informazioni sufficienti perché tutti i parametri di route hanno un valore.
Se il valore viene aggiunto:
- Il valore viene ignorato.
- Il percorso URL generato è .
Avviso: i percorsi URL sono gerarchici. Nell'esempio precedente, se il valore viene aggiunto:
- Entrambi i valori vengono ignorati.
- Non esiste più un valore per e la generazione di URL ha esito negativo.
- È necessario specificare i valori desiderati di e per generare un URL.
È possibile che si verifichi questo problema con la route predefinita . Questo problema è raro in pratica perché specifica sempre in modo esplicito un valore e .
Diversi overload di Url.Action accettano un oggetto di valori di route per fornire valori per i parametri di route diversi da e . L'oggetto di valori route viene spesso usato con . Ad esempio: . L'oggetto dei valori di percorso
- Per convenzione è in genere un oggetto di tipo anonimo.
- Può essere un oggetto o un POCO.
Tutti i valori di route aggiuntivi che non corrispondono ai parametri di route vengono inseriti nella stringa di query.
public IActionResult Index()
{
var url = Url.Action("Buy", "Products", new { id = 17, color = "red" });
return Content(url!);
}
Il codice precedente genera .
Il codice seguente genera un URL assoluto:
public IActionResult Index2()
{
var url = Url.Action("Buy", "Products", new { id = 17 }, protocol: Request.Scheme);
// Returns https://localhost:5001/Products/Buy/17
return Content(url!);
}
Per creare un URL assoluto, usare una delle opzioni seguenti:
- Un sovraccarico che accetta un parametro . Ad esempio, il codice precedente.
- LinkGenerator.GetUriByAction, che genera gli URI assoluti per impostazione predefinita.
Generare URL per percorso
Il codice precedente ha illustrato la generazione di un URL passando il controller e il nome dell'azione. fornisce anche la famiglia di metodi Url.RouteUrl . Questi metodi sono simili a Url.Action, ma non copiano i valori correnti di e nei valori del percorso. Utilizzo più comune di :
- Specifica un nome di route per generare l'URL.
- In genere non specifica un nome di controller o azione.
public class UrlGeneration2Controller : Controller
{
[HttpGet("")]
public IActionResult Source()
{
var url = Url.RouteUrl("Destination_Route");
return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
}
[HttpGet("custom/url/to/destination2", Name = "Destination_Route")]
public IActionResult Destination()
{
return ControllerContext.MyDisplayRouteInfo();
}
Il file seguente genera un collegamento HTML a :
<h1>Test Links</h1>
<ul>
<li><a href="@Url.RouteUrl("Destination_Route")">Test Destination_Route</a></li>
</ul>
Generare URL in HTML e
fornisce i metodi Html.BeginForm e Html.ActionLink per generare e gli elementi rispettivamente. Questi metodi usano il metodo Url.Action per generare un URL e accettano argomenti simili. Gli oggetti complementi di sono e e hanno una funzionalità simile.
Gli helper tag generano gli URL attraverso l'helper tag e l'helper tag . Entrambi usano per la propria implementazione. Per ulteriori informazioni, consultare i tag helper nei moduli.
All'interno delle visualizzazioni, la proprietà è accessibile per qualsiasi creazione di URL ad hoc non coperta dai metodi precedenti.
Generazione di URL nei risultati dell'azione
Negli esempi precedenti viene illustrato come usare in un controller. L'utilizzo più comune in un controller consiste nel generare un URL come parte di un risultato di un'azione.
Le classi di base e offrono metodi pratici per i risultati delle azioni che fanno riferimento a un'altra azione. Un utilizzo tipico consiste nel reindirizzare dopo aver accettato l'input dell'utente:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(int id, Customer customer)
{
if (ModelState.IsValid)
{
// Update DB with new details.
ViewData["Message"] = $"Successful edit of customer {id}";
return RedirectToAction("Index");
}
return View(customer);
}
I metodi factory dei risultati delle azioni, come e , seguono un modello simile a quello dei metodi su .
Caso speciale per le route convenzionali dedicate
Il routing convenzionale può utilizzare un tipo speciale di definizione di percorso chiamato percorso convenzionale dedicato. Nell'esempio seguente la route denominata è una route convenzionale dedicata:
app.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Usando le definizioni di route precedenti, genera il percorso URL usando la route, ma perché? È possibile che i valori di route siano sufficienti per generare un URL usando e il risultato sarebbe .
Percorsi convenzionali dedicati si basano su un comportamento speciale di valori predefiniti che non hanno un parametro di percorso corrispondente, il che impedisce che il percorso sia troppo eccessivo nella generazione di URL. In questo caso i valori predefiniti sono e né né vengono visualizzati come parametri di route. Quando il routing esegue la generazione di URL, i valori specificati devono corrispondere ai valori predefiniti. La generazione di URL con ha esito negativo perché i valori non corrispondono a . Il routing quindi passa al tentativo di utilizzare , che ha esito positivo.
Areas
Le aree sono una funzionalità MVC usata per organizzare le funzionalità correlate in un gruppo come separato:
- Namespace di routing per le azioni del controllo.
- Struttura di cartelle per le visualizzazioni.
L'uso delle aree consente a un'app di avere più controller con lo stesso nome, purché abbiano aree diverse. Usando le aree si crea una gerarchia per il routing aggiungendo un altro parametro di route, a e . In questa sezione viene illustrato come il routing interagisce con le aree. Per dettagli sull'uso delle aree con le visualizzazioni, consultare Aree.
L'esempio seguente configura MVC per l'uso della route convenzionale predefinita e di una route per un denominato :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapAreaControllerRoute("blog_route", "Blog",
"Manage/{controller}/{action}/{id?}");
app.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
app.Run();
Nel codice precedente, viene chiamato per creare . Il secondo parametro, , è il nome dell'area.
Quando si confronta un percorso URL come , la route genera i valori di route . Il valore della route proviene da un valore predefinito per . La route creata da è equivalente al codice seguente:
app.MapControllerRoute("blog_route", "Manage/{controller}/{action}/{id?}",
defaults: new { area = "Blog" }, constraints: new { area = "Blog" });
app.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
crea una route utilizzando sia un valore predefinito sia un vincolo, impiegando il nome dell'area fornito, in questo caso. Il valore predefinito garantisce che la route produca sempre qualcosa, e che il vincolo richieda il valore per la generazione degli URL.
Il routing convenzionale dipende dall'ordine. In generale, posizionare le rotte con aree in anticipo, poiché sono più specifiche delle rotte senza un'area.
Usando l'esempio precedente, i valori della route corrispondono all'azione seguente:
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace1
{
[Area("Blog")]
public class UsersController : Controller
{
// GET /manage/users/adduser
public IActionResult AddUser()
{
var area = ControllerContext.ActionDescriptor.RouteValues["area"];
var actionName = ControllerContext.ActionDescriptor.ActionName;
var controllerName = ControllerContext.ActionDescriptor.ControllerName;
return Content($"area name:{area}" +
$" controller:{controllerName} action name: {actionName}");
}
}
}
L'attributo [Area] indica un controller come parte di un'area. Questo controller si trova nell'area . I controller senza un attributo non sono membri di alcuna area e non corrispondono quando il valore della route viene fornito dal routing. Nell'esempio seguente solo il primo controller indicato può corrispondere ai valori di route .
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace1
{
[Area("Blog")]
public class UsersController : Controller
{
// GET /manage/users/adduser
public IActionResult AddUser()
{
var area = ControllerContext.ActionDescriptor.RouteValues["area"];
var actionName = ControllerContext.ActionDescriptor.ActionName;
var controllerName = ControllerContext.ActionDescriptor.ControllerName;
return Content($"area name:{area}" +
$" controller:{controllerName} action name: {actionName}");
}
}
}
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace2
{
// Matches { area = Zebra, controller = Users, action = AddUser }
[Area("Zebra")]
public class UsersController : Controller
{
// GET /zebra/users/adduser
public IActionResult AddUser()
{
var area = ControllerContext.ActionDescriptor.RouteValues["area"];
var actionName = ControllerContext.ActionDescriptor.ActionName;
var controllerName = ControllerContext.ActionDescriptor.ControllerName;
return Content($"area name:{area}" +
$" controller:{controllerName} action name: {actionName}");
}
}
}
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace3
{
// Matches { area = string.Empty, controller = Users, action = AddUser }
// Matches { area = null, controller = Users, action = AddUser }
// Matches { controller = Users, action = AddUser }
public class UsersController : Controller
{
// GET /users/adduser
public IActionResult AddUser()
{
var area = ControllerContext.ActionDescriptor.RouteValues["area"];
var actionName = ControllerContext.ActionDescriptor.ActionName;
var controllerName = ControllerContext.ActionDescriptor.ControllerName;
return Content($"area name:{area}" +
$" controller:{controllerName} action name: {actionName}");
}
}
}
Per completezza, lo spazio dei nomi di ogni controller viene visualizzato qui. Se i controller precedenti usavano lo stesso spazio dei nomi, verrà generato un errore del compilatore. Gli spazi dei nomi delle classi non hanno effetto sul routing di MVC.
I primi due controller sono membri di aree e si abbinano solo quando il rispettivo nome dell'area è fornito dal valore di route . Il terzo controller non è un membro di un'area e può corrispondere solo quando non vengono specificati valori per dal routing.
In termini di corrispondenza con nessun valore, l'assenza del valore è come se il valore per fosse Null o la stringa vuota.
Quando si esegue un'azione all'interno di un'area, il valore di route per è disponibile come valore di ambiente per il routing da usare per la generazione di URL. Ciò significa che per impostazione predefinita le aree funzionano in modo persistente per la generazione di URL, come illustrato nell'esempio seguente.
app.MapAreaControllerRoute(name: "duck_route",
areaName: "Duck",
pattern: "Manage/{controller}/{action}/{id?}");
app.MapControllerRoute(name: "default",
pattern: "Manage/{controller=Home}/{action=Index}/{id?}");
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace4
{
[Area("Duck")]
public class UsersController : Controller
{
// GET /Manage/users/GenerateURLInArea
public IActionResult GenerateURLInArea()
{
// Uses the 'ambient' value of area.
var url = Url.Action("Index", "Home");
// Returns /Manage/Home/Index
return Content(url);
}
// GET /Manage/users/GenerateURLOutsideOfArea
public IActionResult GenerateURLOutsideOfArea()
{
// Uses the empty value for area.
var url = Url.Action("Index", "Home", new { area = "" });
// Returns /Manage
return Content(url);
}
}
}
Il codice seguente genera un URL per :
public class HomeController : Controller
{
public IActionResult About()
{
var url = Url.Action("AddUser", "Users", new { Area = "Zebra" });
return Content($"URL: {url}");
}
Definizione dell'azione
I metodi pubblici in un controller, ad eccezione di quelli con l'attributo NonAction , sono azioni.
Codice di esempio
- MyDisplayRouteInfo viene fornito dal pacchetto NuGet Rick.Docs.Samples.RouteInfo e visualizza le informazioni sulla route.
- Visualizzare o scaricare il codice di esempio (procedura per il download)
Diagnostica del debug
Per ottenere un output di diagnostica di routing dettagliato, impostare su . Nell'ambiente impostare il livello di registrazione in :
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Debug",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
ASP.NET Core controller usano il routing middleware per trovare le corrispondenze con gli URL delle richieste in ingresso ed eseguirne il mapping a actions. Modelli di route:
- Vengono definiti nel codice di avvio o negli attributi.
- Descrivere in che modo i percorsi URL vengono confrontati con le azioni.
- Vengono usati per generare URL per i collegamenti. I collegamenti generati vengono in genere restituiti nelle risposte.
Le azioni sono instradate convenzionalmente o instradate con attributi. Inserendo una route sul controller o sull'azione, diventa indirizzata tramite attributi. Per altre informazioni, vedere Routing misto.
Questo documento:
- Illustra le interazioni tra MVC e routing:
- Come le app MVC tipiche usano le funzionalità di routing.
- Copre entrambi:
- Il routing convenzionale viene in genere usato con controller e visualizzazioni.
- Routing degli attributi usato con API. Se sei principalmente interessato al routing per le API , vai alla sezione Routing degli attributi per le API .
- Vedere Routing per informazioni dettagliate sul routing avanzato.
- Fa riferimento al sistema di routing predefinito aggiunto in ASP.NET Core 3.0, denominato routing degli endpoint. È possibile usare i controller con la versione precedente del routing a scopo di compatibilità. Per istruzioni, vedere la guida alla migrazione 2.2-3.0. Per informazioni di riferimento sul sistema di routing legacy, vedere la versione 2.2 di questo documento .
Impostare il percorso convenzionale
in genere ha codice simile all'esempio seguente quando si usa il routing convenzionale:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
All'interno della chiamata a viene usato per creare una singola route. La singola route è denominata route. La maggior parte delle app con controller e visualizzazioni usa un modello di route simile alla route. Le API devono usare il routing degli attributi.
Modello di route :
Corrisponde a un percorso URL, ad esempio
Estrae i valori della route suddividendo il percorso in token. L'estrazione dei valori di route genera una corrispondenza se l'app ha un controller denominato e un'azione :
public class ProductsController : Controller { public IActionResult Details(int id) { return ControllerContext.MyDisplayRouteInfo(id); } }MyDisplayRouteInfo viene fornito dal pacchetto NuGet Rick.Docs.Samples.RouteInfo e visualizza le informazioni sulla route.
model collega il valore di per impostare il parametro su . Per altre informazioni, vedere Associazione di modelli.
definisce come valore predefinito .
definisce come valore predefinito .
Il carattere in definisce come facoltativo.
I parametri di route predefiniti e facoltativi non devono necessariamente essere presenti nel percorso URL per trovare una corrispondenza. Per una descrizione dettagliata della sintassi del modello di route, vedere Riferimento al modello di route.
Trova la corrispondenza con il percorso URL .
Produce i valori di route .
I valori per e usano i valori predefiniti. non produce un valore perché non esiste alcun segmento corrispondente nel percorso URL. corrisponde solo se esiste un'azione e :
public class HomeController : Controller
{
public IActionResult Index() { ... }
}
Usando la definizione del controller e il modello di route precedenti, l'azione viene eseguita per i percorsi URL seguenti:
/Home/Index/17/Home/Index/Home/
Il percorso URL usa il controller e l'azione predefiniti del modello di route. Il percorso URL usa l'azione predefinita del modello di route.
Il metodo pratico :
endpoints.MapDefaultControllerRoute();
Replaces:
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
Important
Il routing viene configurato usando il middleware , e . Per usare i controller:
- Chiamare all'interno di per eseguire il mapping dei controller basati su attributi.
- Chiamare il metodo appropriato per mappare sia i controller con routing convenzionale che quelli con routing basato su attributi.
Routing convenzionale
Usare il routing convenzionale con controller e visualizzazioni. Il percorso è:
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Il codice precedente è un esempio di route convenzionale. Si chiama routing convenzionale perché stabilisce una convenzione per i percorsi URL:
- Il primo segmento di percorso, , corrisponde al nome del controller.
- Il secondo segmento si mappa al nome dell'azione.
- Il terzo segmento viene usato per un facoltativo. l'oggetto in lo rende facoltativo. mappa un'entità modello.
Usando questa route, il percorso URL:
- corrisponde all'azione .
- esegue il mapping a e in genere il modello associa il parametro a 17.
Questa mappatura
- Si basa solo sui nomi di controller e azione.
- Non si basa su spazi dei nomi, percorsi dei file di origine o parametri del metodo.
Usando il routing convenzionale con la route predefinita, non è necessario creare un nuovo modello di URL per ogni azione. Per un'app con azioni di stile CRUD, avere coerenza per gli URL tra i controller:
- Semplifica il codice.
- Rende l'interfaccia utente più prevedibile.
Warning
Il modello di route definisce [la/il] ___ come facoltativo. Le azioni possono essere eseguite senza l'ID facoltativo fornito come parte dell'URL. In genere, quando viene omesso dall'URL:
- L'associazione di modelli imposta su .
- Nessuna entità viene trovata nel database corrispondente a .
Il routing basato su attributi fornisce un controllo granulare per rendere l'ID necessario per alcune azioni e non per altre. Per convenzione, la documentazione include parametri facoltativi come quando è probabile che vengano visualizzati nell'utilizzo corretto.
La maggior parte delle app dovrebbe scegliere uno schema di routing semplice e descrittivo in modo che gli URL siano leggibili e significativi. La route convenzionale predefinita :
- Supporta uno schema di routing semplice e descrittivo.
- È un punto iniziale utile per le app basate su interfaccia utente.
- È l'unico modello di route necessario per molte app dell'interfaccia utente Web. Per le app dell'interfaccia utente Web di grandi dimensioni, un'altra route che usa Aree è spesso tutto ciò che serve.
e :
- Assegnare automaticamente un valore di ordine agli endpoint in base all'ordine in cui vengono richiamati.
Routing degli endpoint in ASP.NET Core 3.0 o successivi:
- Non ha il concetto di percorsi.
- Non fornisce garanzie sull'ordinamento per l'esecuzione dell'estendibilità del sistema. Tutti gli endpoint vengono elaborati contemporaneamente.
Abilitare la registrazione per verificare in che modo le implementazioni del routing predefinite, ad esempio , corrispondono alle richieste.
Il routing degli attributi è illustrato più avanti in questo documento.
Più route convenzionali
È possibile aggiungere più route convenzionali all'interno aggiungendo altre chiamate a e . In questo modo è possibile definire più convenzioni o aggiungere route convenzionali dedicate a un'azione specifica, ad esempio:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
endpoints.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
La route nel codice precedente è un percorso convenzionale dedicato. Si tratta di un itinerario convenzionale dedicato perché:
- Usa il routing convenzionale.
- È dedicata a un'azione specifica.
Poiché il modello di route non include e come parametri:
- Possono avere solo i valori predefiniti.
- Questa route esegue sempre il mapping all'azione .
, e sono gli unici percorsi URL che corrispondono alla route del blog.
Nell'esempio precedente:
- La rotta ha una priorità maggiore rispetto all'altra per le corrispondenze, poiché si aggiunge per prima.
- È un esempio di routing dello stile Slug in cui è tipico avere un nome di articolo come parte dell'URL.
Warning
In ASP.NET Core 3.0 o versione successiva, il routing non esegue:
- Definire un concetto denominato route. aggiunge la corrispondenza della route alla pipeline middleware. Il middleware esamina il set di endpoint definiti nell'applicazione e seleziona la migliore corrispondenza per l'endpoint in base alla richiesta.
- Fornire garanzie sull'ordine di esecuzione delle estensioni, come o .
Vedere Routing per materiale di riferimento sul routing.
Ordine di routing convenzionale
Il routing convenzionale corrisponde solo a una combinazione di azioni e controller definiti dall'app. Questo approccio semplifica i casi in cui le route convenzionali si sovrappongono.
Quando si aggiungono route usando , e , gli endpoint ottengono automaticamente un valore dell'ordine in base all'ordine in cui si richiamano questi metodi. Le corrispondenze di una route visualizzata in precedenza nell'elenco hanno una priorità più alta. Il routing convenzionale dipende dall'ordine. In generale, posizionare prima i percorsi con aree perché sono più specifici rispetto alle rotte senza aree. Le rotte convenzionali dedicate con parametri di route catch-all, come
Warning
Un parametro catch-all può corrispondere erroneamente alle route a causa di un bug nel routing. Le app interessate da questo bug presentano le caratteristiche seguenti:
- Un percorso generico, ad esempio
- La route catch-all non riesce a gestire correttamente le richieste che dovrebbe soddisfare.
- La rimozione di altre route fa funzionare il percorso catch-all.
Consultare i bug di GitHub 18677 e 16579 come esempi di casi che presentano questo bug.
Una correzione di consenso esplicito per questo bug è contenuta in .NET Core 3.1.301 o versione successiva dell'SDK. Il codice seguente imposta un commutatore interno che corregge questo bug:
public static void Main(string[] args)
{
AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior",
true);
CreateHostBuilder(args).Build().Run();
}
// Remaining code removed for brevity.
Risoluzione di azioni ambigue
Quando due endpoint corrispondono tramite routing, il routing deve eseguire una delle operazioni seguenti:
- Scegliere il candidato migliore.
- Genera un'eccezione.
Per esempio:
public class Products33Controller : Controller
{
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[HttpPost]
public IActionResult Edit(int id, Product product)
{
return ControllerContext.MyDisplayRouteInfo(id, product.name);
}
}
}
Il controller precedente definisce due azioni che corrispondono:
- Percorso URL
- Indirizzare i dati .
Si tratta di un modello tipico per i controller MVC:
- visualizza un modulo per modificare un prodotto.
- elabora il modulo pubblicato.
Per risolvere la route corretta:
- viene selezionato quando la richiesta è un http .
- viene selezionato quando il verbo HTTP è qualsiasi altro. viene in genere chiamato tramite .
L'oggetto , viene fornito al routing in modo che possa scegliere in base al metodo HTTP della richiesta. rende una corrispondenza migliore rispetto a .
È importante comprendere il ruolo degli attributi, ad esempio . Gli attributi simili sono definiti per altri verbi HTTP. Nel routing convenzionale, è comune che le azioni usino lo stesso nome dell'azione quando fanno parte di un flusso di lavoro di visualizzazione e invio del modulo. Ad esempio, vedere Esaminare i due metodi di azione di modifica.
Se il routing non è in grado di scegliere il candidato migliore, genera un'eccezione ed elenca i più endpoint corrispondenti.
Nomi di route convenzionali
Le stringhe e negli esempi seguenti sono nomi di route convenzionali:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
endpoints.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
I nomi delle route assegnano alla route un nome logico. Il percorso denominato può essere usato per la generazione di URL. L'uso di una route denominata semplifica la creazione di URL quando l'ordinamento delle route potrebbe rendere complessa la generazione di URL. I nomi di route devono essere univoci a livello di applicazione.
Nomi di route:
- Non ha alcun impatto sulla corrispondenza degli URL o sulla gestione delle richieste.
- Vengono usati solo per la generazione di URL.
Il concetto di nome della route è rappresentato nel routing come IEndpointNameMetadata. I termini nome della route e nome dell'endpoint:
- Sono intercambiabili.
- Quello usato nella documentazione e nel codice dipende dall'API descritta.
Routing degli attributi per le API
Le API devono usare il routing degli attributi per modellare la funzionalità dell'app come set di risorse in cui le operazioni sono rappresentate da verbi HTTP.
Il routing con attributi usa un set di attributi per eseguire il mapping delle azioni direttamente ai modelli di route. Il codice seguente è tipico per un'API e viene usato nell'esempio seguente:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Nel codice precedente, viene chiamato all'interno di per eseguire il mapping dei controller con routing degli attributi.
Nell'esempio seguente :
- corrisponde a un set di URL simili a quello che corrisponde alla route convenzionale predefinita.
public class HomeController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
[Route("Home/Index/{id?}")]
public IActionResult Index(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[Route("Home/About")]
[Route("Home/About/{id?}")]
public IActionResult About(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
L'azione viene eseguita per uno dei percorsi URL , , o .
In questo esempio viene evidenziata una differenza di programmazione chiave tra il routing degli attributi e il routing convenzionale. Il routing degli attributi richiede più dati per specificare una route. La route predefinita convenzionale gestisce le route in modo più conciso. Tuttavia, il routing degli attributi consente e richiede un controllo preciso dei modelli di route applicabili a ogni azione.
Con il routing degli attributi, i nomi dei controller e delle azioni non influenzano la corrispondenza dell'azione, a meno che non sia utilizzata la sostituzione di token. L'esempio seguente corrisponde agli stessi URL dell'esempio precedente:
public class MyDemoController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
[Route("Home/Index/{id?}")]
public IActionResult MyIndex(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[Route("Home/About")]
[Route("Home/About/{id?}")]
public IActionResult MyAbout(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Il codice seguente usa la sostituzione dei token per e :
public class HomeController : Controller
{
[Route("")]
[Route("Home")]
[Route("[controller]/[action]")]
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
[Route("[controller]/[action]")]
public IActionResult About()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Il codice seguente si applica al controller:
[Route("[controller]/[action]")]
public class HomeController : Controller
{
[Route("~/")]
[Route("/Home")]
[Route("~/Home/Index")]
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
public IActionResult About()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Nel codice precedente, i modelli di metodo devono anteporre o ai modelli di route. I modelli di route applicati a un'azione che iniziano con o non vengono combinati con i modelli di route applicati al controller.
Per informazioni sulla selezione del modello di route, vedere Precedenza del modello di route.
Nomi riservati per il routing
Le parole chiave seguenti sono nomi di parametri di route riservate quando si usano controller o pagine:
actionareacontrollerhandlerpage
L'uso di come parametro di route nel routing degli attributi è un errore comune. Questa scelta comporta comportamenti incoerenti e confusi con la generazione di URL.
public class MyDemo2Controller : Controller
{
[Route("/articles/{page}")]
public IActionResult ListArticles(int page)
{
return ControllerContext.MyDisplayRouteInfo(page);
}
}
La generazione di URL usa questi nomi di parametri speciali per determinare se un'operazione di generazione url fa riferimento a una pagina o a un controller.
Le parole chiave seguenti sono riservate nel contesto di una visualizzazione o di una pagina:
pageusingnamespaceinjectsectioninheritsmodeladdTagHelperremoveTagHelper
Non usare queste parole chiave per le generazioni di collegamenti, i parametri associati al modello o le proprietà di primo livello.
Modelli di verbo HTTP
ASP.NET Core include i modelli per i verbi HTTP seguenti:
- [HttpGet]
- [HttpPost]
- [HttpPut]
- [HttpDelete]
- [HttpHead]
- [HttpPatch]
Modelli di route
ASP.NET Core includono i modelli di route seguenti:
- Tutti i modelli verbo HTTP sono modelli di route.
- [Route]
Routing degli attributi con attributi verbi HTTP
Prendere in considerazione il controller seguente:
[Route("api/[controller]")]
[ApiController]
public class Test2Controller : ControllerBase
{
[HttpGet] // GET /api/test2
public IActionResult ListProducts()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("{id}")] // GET /api/test2/xyz
public IActionResult GetProduct(string id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[HttpGet("int/{id:int}")] // GET /api/test2/int/3
public IActionResult GetIntProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[HttpGet("int2/{id}")] // GET /api/test2/int2/3
public IActionResult GetInt2Product(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Nel codice precedente:
- Ogni azione contiene l'attributo , che vincola solo la corrispondenza alle richieste HTTP GET.
- L'azione include il template, quindi si aggiunge al template sul controller. Il modello del metodo è . Pertanto, questa azione corrisponde solo alle richieste GET per il modulo , , e così via.
[HttpGet("{id}")] // GET /api/test2/xyz public IActionResult GetProduct(string id) { return ControllerContext.MyDisplayRouteInfo(id); } - L'azione contiene il modello. La parte del modello vincola i valori di route alle stringhe che possono essere convertite in un numero intero. Una richiesta GET a :
- Non corrisponde a questa azione.
- Restituisce un errore 404 Non trovato .
[HttpGet("int/{id:int}")] // GET /api/test2/int/3 public IActionResult GetIntProduct(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
- L'azione contiene nel modello, ma non limita i valori che possono essere convertiti in un numero intero. Una richiesta GET a :
- Corrisponde a questa route.
- L'associazione di modelli non riesce a eseguire la conversione in un numero intero. Il parametro del metodo è integer.
- Restituisce una richiesta non valida 400 perché l'associazione di modelli non è riuscita a eseguire la conversione in un numero intero.
[HttpGet("int2/{id}")] // GET /api/test2/int2/3 public IActionResult GetInt2Product(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
Il routing degli attributi può usare attributi come , e . Tutti gli attributi del verbo HTTP accettano un modello di route. L'esempio seguente mostra due azioni che corrispondono allo stesso modello di route:
[ApiController]
public class MyProductsController : ControllerBase
{
[HttpGet("/products3")]
public IActionResult ListProducts()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpPost("/products3")]
public IActionResult CreateProduct(MyProduct myProduct)
{
return ControllerContext.MyDisplayRouteInfo(myProduct.Name);
}
}
Uso del percorso URL :
- L'azione viene eseguita quando il verbo HTTP è .
- L'azione viene eseguita quando il verbo HTTP è .
Quando si compila un'API , è raro che sia necessario usare in un metodo di azione perché l'azione accetta tutti i metodi HTTP. È preferibile usare l'attributo verbo HTTP più specifico per essere preciso su ciò che supporta l'API. I client delle API devono conoscere i percorsi e i verbi HTTP mappati a operazioni logiche specifiche.
Le API devono usare il routing degli attributi per modellare la funzionalità dell'app come set di risorse in cui le operazioni sono rappresentate da verbi HTTP. Questa progettazione significa che molte operazioni, ad esempio GET e POST nella stessa risorsa logica, usano lo stesso URL. Il routing degli attributi fornisce il livello di controllo necessario per progettare attentamente il layout dell'endpoint pubblico di un'API.
Poiché una route con attributi si applica a un'azione specifica, è facile fare in modo che i parametri siano richiesti come parte della definizione del modello di route. Nell'esempio seguente è necessario come parte del percorso URL:
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Azione :
- Viene eseguito con il percorso URL, ad esempio
- Non viene eseguito con il percorso URL .
L'attributo [Consumes] consente a un'azione di limitare i tipi di contenuto della richiesta supportati. Per altre informazioni, vedere Definire i tipi di contenuto di richiesta supportati con l'attributo Consumes.
Vedere Routing per una descrizione completa dei modelli di route e delle opzioni correlate.
Per maggiori informazioni su , consultare l'attributo ApiController.
Nome percorso
Il codice seguente definisce un nome di route di :
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Usare i nomi di route per generare un URL basato su una route specifica. Nomi di route:
- Non influenzare il modo in cui il routing verifica la corrispondenza degli URL.
- Vengono usati solo per la generazione di URL.
I nomi delle route devono essere univoci a livello di applicazione.
Confrontare il codice precedente con la route predefinita convenzionale, che definisce il parametro come facoltativo (). La possibilità di specificare con precisione le API presenta vantaggi, ad esempio consentire di inviare e a diverse azioni.
Combinare i percorsi attributo
Per rendere il routing degli attributi meno ripetitivo, combinare gli attributi di percorso sul controller con quelli sulle singole azioni. I modelli di route che definisci nel controller vengono anteposti ai modelli di route nelle azioni. Quando si inserisce un attributo di route nel controller, tutte le azioni nel controller usano il routing degli attributi.
[ApiController]
[Route("products")]
public class ProductsApiController : ControllerBase
{
[HttpGet]
public IActionResult ListProducts()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("{id}")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Nell'esempio precedente:
- Il percorso URL può corrispondere
- Il percorso URL può corrispondere a .
Entrambe queste azioni corrispondono solo a HTTP perché sono contrassegnate con l'attributo .
Modelli di route applicati a un'azione e che iniziano con o non vengono combinati con i modelli di route applicati al controller. L'esempio seguente corrisponde a un set di percorsi URL simili alla route predefinita.
[Route("Home")]
public class HomeController : Controller
{
[Route("")]
[Route("Index")]
[Route("/")]
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
[Route("About")]
public IActionResult About()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
La tabella seguente illustra gli attributi nel codice precedente:
| Attribute | Combina con | Definisce il modello di route |
|---|---|---|
[Route("")] |
Yes | "Home" |
[Route("Index")] |
Yes | "Home/Index" |
[Route("/")] |
No | "" |
[Route("About")] |
Yes | "Home/About" |
Ordine di route degli attributi
Il routing crea un albero e confronta tutti gli endpoint contemporaneamente.
- Le voci di percorso si comportano come se fossero inserite in un ordinamento ideale.
- Le rotte più specifiche hanno una possibilità di essere eseguite prima delle rotte più generali.
Ad esempio, una route di attributi come è più specifica di una route di attributi come . La route ha priorità più alta, per impostazione predefinita, perché è più specifica. Usando il routing convenzionale, lo sviluppatore è responsabile dell'inserimento di route nell'ordine desiderato.
Le route degli attributi possono configurare un ordine usando la proprietà . Tutti gli attributi di route forniti dal framework includono . Le rotte vengono elaborate in base a un ordinamento crescente della proprietà . L'ordine predefinito è . Impostare una rotta tramite viene eseguita prima delle rotte che non impostano un ordine. Impostare una route utilizzando viene eseguito dopo l'ordinamento predefinito delle route.
Evitare di fare affidamento su. Se lo spazio URL di un'app richiede valori di ordine espliciti per instradare correttamente, è probabile che sia fonte di confusione anche per i clienti. In generale, l'instradamento basato sugli attributi seleziona il percorso corretto con l'allineamento dell'URL. Se l'ordine predefinito usato per la generazione di URL non funziona, l'uso di un nome di route come override è in genere più semplice rispetto all'applicazione della proprietà .
Si considerino i due controller seguenti che definiscono entrambi la route corrispondente :
public class HomeController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
[Route("Home/Index/{id?}")]
public IActionResult Index(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[Route("Home/About")]
[Route("Home/About/{id?}")]
public IActionResult About(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
public class MyDemoController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
[Route("Home/Index/{id?}")]
public IActionResult MyIndex(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[Route("Home/About")]
[Route("Home/About/{id?}")]
public IActionResult MyAbout(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
La richiesta tramite il codice precedente genera un'eccezione simile alla seguente:
AmbiguousMatchException: The request matched multiple endpoints. Matches:
WebMvcRouting.Controllers.HomeController.Index
WebMvcRouting.Controllers.MyDemoController.MyIndex
L'aggiunta a uno degli attributi di route risolve l'ambiguità:
[Route("")]
[Route("Home", Order = 2)]
[Route("Home/MyIndex")]
public IActionResult MyIndex()
{
return ControllerContext.MyDisplayRouteInfo();
}
Con il codice precedente, esegue l'endpoint . Per accedere a , richiedere . Note:
- Il codice precedente è un esempio di progettazione di routing non ottimale. Illustra la proprietà.
- La proprietà risolve solo l'ambiguità. Tale modello non può essere confrontato. È preferibile rimuovere il modello.
Per informazioni sull'ordine delle route in Pages, vedere le convenzioni relative alle route e alle app in Pages: ordine delle route.
In alcuni casi, viene restituito un errore HTTP 500 con delle route ambigue. Utilizzare i log per vedere quali endpoint hanno causato il .
Sostituzione dei token nei modelli di route [controller], [azione], [area]
Per praticità, le route degli attributi supportano la sostituzione dei token racchiudendo un token tra parentesi quadre (, ). I token , e vengono sostituiti con i valori del nome dell'azione, del nome dell'area e del nome del controller dall'azione in cui si definisce la route:
[Route("[controller]/[action]")]
public class Products0Controller : Controller
{
[HttpGet]
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Nel codice precedente:
[HttpGet]
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
- Corrisponde
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
- Corrisponde
La sostituzione dei token viene eseguita come ultimo passaggio della compilazione delle route dell'attributo. L'esempio precedente si comporta come il codice seguente:
public class Products20Controller : Controller
{
[HttpGet("[controller]/[action]")] // Matches '/Products20/List'
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("[controller]/[action]/{id}")] // Matches '/Products20/Edit/{id}'
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Se stai leggendo questo argomento in una lingua diversa dall'inglese, comunicaci in questo GitHub problema di discussione se vuoi visualizzare i commenti del codice nella tua lingua nativa.
È anche possibile combinare route di attributi con ereditarietà. Questa combinazione è efficace quando si usa la sostituzione dei token. La sostituzione dei token si applica anche ai nomi di route definiti dalle route con attributi. genera un nome di route univoco per ogni azione:
[ApiController]
[Route("api/[controller]/[action]", Name = "[controller]_[action]")]
public abstract class MyBase2Controller : ControllerBase
{
}
public class Products11Controller : MyBase2Controller
{
[HttpGet] // /api/products11/list
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("{id}")] // /api/products11/edit/3
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Per verificare la corrispondenza del delimitatore letterale della sostituzione di token o , eseguirne l'escape ripetendo il carattere ( o ).
Usare un trasformatore di parametri per personalizzare la sostituzione dei token
È possibile personalizzare la sostituzione dei token usando un trasformatore di parametro. Un trasformatore di parametri implementa e trasforma il valore dei parametri. Ad esempio, un trasformatore di parametro personalizzato modifica il valore della route in :
public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
public string TransformOutbound(object value)
{
if (value == null) { return null; }
return Regex.Replace(value.ToString(),
"([a-z])([A-Z])",
"$1-$2",
RegexOptions.CultureInvariant,
TimeSpan.FromMilliseconds(100)).ToLowerInvariant();
}
}
è una convenzione del modello di applicazione che:
- Applicare un trasformatore di parametro a tutte le route degli attributi in un'applicazione.
- Personalizza i valori del token di route dell'attributo man mano che vengono sostituiti.
public class SubscriptionManagementController : Controller
{
[HttpGet("[controller]/[action]")]
public IActionResult ListAll()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Il metodo precedente corrisponde a .
La è registrata come opzione in .
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Conventions.Add(new RouteTokenTransformerConvention(
new SlugifyParameterTransformer()));
});
}
Per la definizione di slug, vedere la documentazione Web MDN su Slug.
Warning
Quando si usa per elaborare l'input non attendibile, passare un timeout. Un utente malintenzionato può fornire input a causando un attacco Denial-of-Service. Le API del framework ASP.NET Core che utilizzano RegularExpressions, passano un timeout.
Numerose route di attributi
Il routing con attributi supporta la definizione di più route che raggiungono la stessa azione. L'uso più comune è simulare il comportamento della route convenzionale predefinita come illustrato nell'esempio seguente:
[Route("[controller]")]
public class Products13Controller : Controller
{
[Route("")] // Matches 'Products13'
[Route("Index")] // Matches 'Products13/Index'
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
L'inserimento di più attributi di route nel controller significa che ognuno di essi si combina con ognuno degli attributi di route nei metodi di azione:
[Route("Store")]
[Route("[controller]")]
public class Products6Controller : Controller
{
[HttpPost("Buy")] // Matches 'Products6/Buy' and 'Store/Buy'
[HttpPost("Checkout")] // Matches 'Products6/Checkout' and 'Store/Checkout'
public IActionResult Buy()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Tutti i vincoli di route del verbo HTTP implementano .
Quando vengono posizionati più attributi di route che implementano su un'azione:
- Ogni vincolo di azione viene combinato con il modello di route applicato al controller.
[Route("api/[controller]")]
public class Products7Controller : ControllerBase
{
[HttpPut("Buy")] // Matches PUT 'api/Products7/Buy'
[HttpPost("Checkout")] // Matches POST 'api/Products7/Checkout'
public IActionResult Buy()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
L'uso di più route sulle azioni potrebbe sembrare utile e potente, è preferibile mantenere lo spazio URL dell'app di base e ben definito. Usare più route per le azioni solo se necessario, ad esempio, per supportare i client esistenti.
Definizione di parametri facoltativi, valori predefiniti e vincoli della route con attributi
Le route con attributi supportano la stessa sintassi inline delle route convenzionali per specificare i parametri facoltativi, i valori predefiniti e i vincoli.
public class Products14Controller : Controller
{
[HttpPost("product14/{id:int}")]
public IActionResult ShowProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Nel codice precedente applica un vincolo di route. L'azione viene confrontata solo in base ai percorsi URL, ad esempio . La parte del modello di route vincola tale segmento solo a numeri interi.
Per una descrizione dettagliata della sintassi del modello di route, vedere Riferimento al modello di route.
Attributi di route personalizzati con IRouteTemplateProvider
Tutti gli attributi della route implementano . Il runtime di ASP.NET Core
- Cerca gli attributi nelle classi controller e nei metodi di azione all'avvio dell'app.
- Usa gli attributi che implementano per compilare il set iniziale di route.
Implementare per definire attributi di route personalizzati. Ogni consente di definire una singola route con un modello di route, un ordinamento e un nome personalizzati:
public class MyApiControllerAttribute : Attribute, IRouteTemplateProvider
{
public string Template => "api/[controller]";
public int? Order => 2;
public string Name { get; set; }
}
[MyApiController]
[ApiController]
public class MyTestApiController : ControllerBase
{
// GET /api/MyTestApi
[HttpGet]
public IActionResult Get()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Il metodo precedente restituisce .
Usare il modello di applicazione per personalizzare le route degli attributi
Il modello di applicazione:
- Modello a oggetti creato all'avvio.
- Contiene tutti i metadati usati da ASP.NET Core per instradare ed eseguire le azioni in un'app.
Il modello di applicazione include tutti i dati raccolti dagli attributi di route. I dati degli attributi di route vengono forniti dall'implementazione . Conventions:
- Può essere scritto per modificare il modello di applicazione per personalizzare il comportamento del routing.
- Vengono lette all'avvio dell'app.
Questa sezione illustra un esempio di base della personalizzazione del routing usando il modello di applicazione. Il codice seguente rende le route approssimativamente allineate alla struttura di cartelle del progetto.
public class NamespaceRoutingConvention : Attribute, IControllerModelConvention
{
private readonly string _baseNamespace;
public NamespaceRoutingConvention(string baseNamespace)
{
_baseNamespace = baseNamespace;
}
public void Apply(ControllerModel controller)
{
var hasRouteAttributes = controller.Selectors.Any(selector =>
selector.AttributeRouteModel != null);
if (hasRouteAttributes)
{
return;
}
var namespc = controller.ControllerType.Namespace;
if (namespc == null)
return;
var template = new StringBuilder();
template.Append(namespc, _baseNamespace.Length + 1,
namespc.Length - _baseNamespace.Length - 1);
template.Replace('.', '/');
template.Append("/[controller]/[action]/{id?}");
foreach (var selector in controller.Selectors)
{
selector.AttributeRouteModel = new AttributeRouteModel()
{
Template = template.ToString()
};
}
}
}
Il codice seguente impedisce l'applicazione della convenzione ai controller che sono instradati per attributo.
public void Apply(ControllerModel controller)
{
var hasRouteAttributes = controller.Selectors.Any(selector =>
selector.AttributeRouteModel != null);
if (hasRouteAttributes)
{
return;
}
Ad esempio, il controller seguente non usa :
[Route("[controller]/[action]/{id?}")]
public class ManagersController : Controller
{
// /managers/index
public IActionResult Index()
{
var template = ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
return Content($"Index- template:{template}");
}
public IActionResult List(int? id)
{
var path = Request.Path.Value;
return Content($"List- Path:{path}");
}
}
Il metodo :
- Non esegue alcuna operazione se il controller è instradato con attributi.
- Imposta il modello di controller in base a , con la base rimossa.
L'oggetto può essere applicato in :
namespace My.Application
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Conventions.Add(
new NamespaceRoutingConvention(typeof(Startup).Namespace));
});
}
// Remaining code ommitted for brevity.
Si consideri ad esempio il controller seguente:
using Microsoft.AspNetCore.Mvc;
namespace My.Application.Admin.Controllers
{
public class UsersController : Controller
{
// GET /admin/controllers/users/index
public IActionResult Index()
{
var fullname = typeof(UsersController).FullName;
var template =
ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
var path = Request.Path.Value;
return Content($"Path: {path} fullname: {fullname} template:{template}");
}
public IActionResult List(int? id)
{
var path = Request.Path.Value;
return Content($"Path: {path} ID:{id}");
}
}
}
Nel codice precedente:
- La base è .
- Il nome completo del controller precedente è .
- Imposta il modello del controller di su .
Può anche essere applicato come attributo in un controller:
[NamespaceRoutingConvention("My.Application")]
public class TestController : Controller
{
// /admin/controllers/test/index
public IActionResult Index()
{
var template = ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
var actionname = ControllerContext.ActionDescriptor.ActionName;
return Content($"Action- {actionname} template:{template}");
}
public IActionResult List(int? id)
{
var path = Request.Path.Value;
return Content($"List- Path:{path}");
}
}
Routing misto: routing con attributi e routing convenzionale
Le app ASP.NET Core possono combinare l'uso del routing convenzionale e del routing basato sugli attributi. In genere, si usano route convenzionali per i controller che servono pagine HTML ai browser e il routing degli attributi per i controller che servono le API.
Le azioni vengono indirizzate in modo convenzionale o mediante l'uso di attributi. Quando una route viene aggiunta al controller o all'azione, viene configurata per l'instradamento basato su attributi. Le azioni che definiscono le route con attributi non possono essere raggiunte usando le route convenzionali e viceversa. Qualsiasi attributo di route nel controller esegue tutte le azioni nell'attributo controller indirizzato.
Il routing degli attributi e il routing convenzionale usano lo stesso motore di routing.
Generazione di URL e valori di ambiente
Le app possono usare le funzionalità di generazione url di routing per generare collegamenti URL alle azioni. La generazione di URL elimina gli URL hardcoding, rendendo il codice più affidabile e gestibile. Questa sezione è incentrata sulle funzionalità di generazione di URL fornite da MVC e illustrano solo le nozioni di base sul funzionamento della generazione di URL. Vedere Routing per una descrizione dettagliata della generazione di URL.
L'interfaccia è l'elemento sottostante dell'infrastruttura tra MVC e routing per la generazione di URL. Un'istanza di è disponibile tramite la proprietà nei controller, nelle visualizzazioni e nei componenti di visualizzazione.
Nell'esempio seguente l'interfaccia viene usata tramite la proprietà per generare un URL a un'altra azione.
public class UrlGenerationController : Controller
{
public IActionResult Source()
{
// Generates /UrlGeneration/Destination
var url = Url.Action("Destination");
return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
}
public IActionResult Destination()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Se l'app usa la route convenzionale predefinita, il valore della variabile è la stringa di percorso URL . Il routing crea questo percorso URL combinando:
- Valori di route della richiesta corrente, denominati valori di ambiente.
- I valori passati a e i valori sostituiti nel modello di route:
ambient values: { controller = "UrlGeneration", action = "Source" }
values passed to Url.Action: { controller = "UrlGeneration", action = "Destination" }
route template: {controller}/{action}/{id?}
result: /UrlGeneration/Destination
In ogni parametro del modello di route, il valore viene sostituito facendo corrispondere i nomi con i valori specificati e i valori di ambiente. Un parametro di route che non ha un valore può:
- Usare un valore predefinito se ne ha uno.
- Essere ignorato se è facoltativo. Ad esempio, dal modello di percorso .
La generazione dell'URL ha esito negativo se un parametro di route obbligatorio non ha un valore corrispondente. Se la generazione di URL non riesce per una route, viene tentata la route successiva finché non vengono tentate tutte le route o viene trovata una corrispondenza.
L'esempio precedente di presuppone il routing convenzionale. La generazione di URL funziona in modo analogo con il routing degli attributi, anche se i concetti sono diversi. Con il routing convenzionale:
- I valori di route vengono usati per espandere un modello.
- I valori di route per e in genere vengono visualizzati in tale modello. Ciò funziona perché gli URL corrispondenti al routing rispettano una convenzione.
L'esempio seguente usa il routing degli attributi:
public class UrlGenerationAttrController : Controller
{
[HttpGet("custom")]
public IActionResult Source()
{
var url = Url.Action("Destination");
return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
}
[HttpGet("custom/url/to/destination")]
public IActionResult Destination()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
L'azione nel codice precedente genera .
LinkGenerator è stato aggiunto in ASP.NET Core 3.0 come alternativa a IUrlHelper. offre funzionalità simili ma più flessibili. Ogni metodo in ha anche una famiglia di metodi corrispondente su .
Generazione di URL in base al nome dell'azione
Url.Action, LinkGenerator.GetPathByAction e tutti gli overload correlati sono progettati per generare l'endpoint di destinazione specificando un nome del controller e un nome di azione.
Quando si usa , il runtime fornisce i valori di route correnti per e :
- I valori di
e fanno parte sia dei valori ambientali sia dei valori generali. Il metodo usa sempre i valori correnti di e genera un percorso URL che instrada all'azione corrente.
Il routing tenta di usare i valori nei valori di ambiente per inserire informazioni non specificate durante la generazione di un URL. Si consideri un percorso come con i valori di ambiente .
- Il routing contiene informazioni sufficienti per generare un URL senza valori aggiuntivi.
- Il routing contiene informazioni sufficienti perché tutti i parametri di route hanno un valore.
Se il valore viene aggiunto:
- Il valore viene ignorato.
- Il percorso URL generato è .
Avviso: i percorsi URL sono gerarchici. Nell'esempio precedente, se il valore viene aggiunto:
- Entrambi i valori vengono ignorati.
- Non esiste più un valore per e la generazione di URL ha esito negativo.
- È necessario specificare i valori desiderati di e per generare un URL.
È possibile che si verifichi questo problema con la route predefinita . Questo problema è raro in pratica perché specifica sempre in modo esplicito un valore e .
Diversi overload di Url.Action accettano un oggetto di valori di route per fornire valori per i parametri di route diversi da e . L'oggetto di valori route viene spesso usato con . Ad esempio: . L'oggetto dei valori di percorso
- Per convenzione è in genere un oggetto di tipo anonimo.
- Può essere un oggetto o un POCO.
Tutti i valori di route aggiuntivi che non corrispondono ai parametri di route vengono inseriti nella stringa di query.
public IActionResult Index()
{
var url = Url.Action("Buy", "Products", new { id = 17, color = "red" });
return Content(url);
}
Il codice precedente genera .
Il codice seguente genera un URL assoluto:
public IActionResult Index2()
{
var url = Url.Action("Buy", "Products", new { id = 17 }, protocol: Request.Scheme);
// Returns https://localhost:5001/Products/Buy/17
return Content(url);
}
Per creare un URL assoluto, usare una delle opzioni seguenti:
- Un sovraccarico che accetta un parametro . Ad esempio, il codice precedente.
- LinkGenerator.GetUriByAction, che genera gli URI assoluti per impostazione predefinita.
Generare URL per percorso
Il codice precedente ha illustrato la generazione di un URL passando il controller e il nome dell'azione. fornisce anche la famiglia di metodi Url.RouteUrl . Questi metodi sono simili a Url.Action, ma non copiano i valori correnti di e nei valori del percorso. Utilizzo più comune di :
- Specifica un nome di route per generare l'URL.
- In genere non specifica un nome di controller o azione.
public class UrlGeneration2Controller : Controller
{
[HttpGet("")]
public IActionResult Source()
{
var url = Url.RouteUrl("Destination_Route");
return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
}
[HttpGet("custom/url/to/destination2", Name = "Destination_Route")]
public IActionResult Destination()
{
return ControllerContext.MyDisplayRouteInfo();
}
Il file seguente genera un collegamento HTML a :
<h1>Test Links</h1>
<ul>
<li><a href="@Url.RouteUrl("Destination_Route")">Test Destination_Route</a></li>
</ul>
Generare URL in HTML e
fornisce i metodi Html.BeginForm e Html.ActionLink per generare e gli elementi rispettivamente. Questi metodi usano il metodo Url.Action per generare un URL e accettano argomenti simili. Gli oggetti complementi di sono e e hanno una funzionalità simile.
Gli helper tag generano gli URL attraverso l'helper tag e l'helper tag . Entrambi usano per la propria implementazione. Per ulteriori informazioni, vedere i Tag helper nei moduli.
All'interno delle visualizzazioni, la proprietà è disponibile per qualsiasi generazione di URL ad hoc non coperta dai metodi precedenti.
Generazione di URL nei risultati dell'azione
Negli esempi precedenti viene illustrato come usare in un controller. L'utilizzo più comune in un controller consiste nel generare un URL come parte di un risultato di un'azione.
Le classi di base e offrono metodi pratici per i risultati delle azioni che fanno riferimento a un'altra azione. Un utilizzo tipico consiste nel reindirizzare dopo aver accettato l'input dell'utente:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(int id, Customer customer)
{
if (ModelState.IsValid)
{
// Update DB with new details.
ViewData["Message"] = $"Successful edit of customer {id}";
return RedirectToAction("Index");
}
return View(customer);
}
I metodi factory dei risultati delle azioni, come e , seguono un modello simile a quello dei metodi su .
Caso speciale per le route convenzionali dedicate
Il routing convenzionale può utilizzare un tipo speciale di definizione di percorso chiamato percorso convenzionale dedicato. Nell'esempio seguente la route denominata è una route convenzionale dedicata:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
endpoints.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
Utilizzando le precedenti definizioni di percorso, genera il percorso URL utilizzando il percorso, ma perché? È possibile che i valori di route siano sufficienti per generare un URL utilizzando X e il risultato sarebbe Y.
Percorsi convenzionali dedicati si basano su un comportamento speciale di valori predefiniti che non hanno un parametro di percorso corrispondente, il che impedisce che il percorso sia troppo eccessivo nella generazione di URL. In questo caso i valori predefiniti sono e né né vengono visualizzati come parametri di route. Quando il routing esegue la generazione di URL, i valori specificati devono corrispondere ai valori predefiniti. La generazione di URL con ha esito negativo perché i valori non corrispondono a . Il routing quindi passa al tentativo di utilizzare , che ha esito positivo.
Areas
Le aree sono una funzionalità MVC usata per organizzare le funzionalità correlate in un gruppo come separato:
- Namespace di routing per le azioni del controllo.
- Struttura di cartelle per le visualizzazioni.
L'uso delle aree consente a un'app di avere più controller con lo stesso nome, purché abbiano aree diverse. Usando le aree si crea una gerarchia per il routing aggiungendo un altro parametro di route, a e . In questa sezione viene illustrato come il routing interagisce con le aree. Per dettagli sull'uso delle aree con le visualizzazioni, consultare Aree.
L'esempio seguente configura MVC per l'uso della route convenzionale predefinita e di una route per un denominato :
app.UseEndpoints(endpoints =>
{
endpoints.MapAreaControllerRoute("blog_route", "Blog",
"Manage/{controller}/{action}/{id?}");
endpoints.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
});
Nel codice precedente, viene chiamato per creare . Il secondo parametro, , è il nome dell'area.
Quando si confronta un percorso URL come , la route genera i valori di route . Il valore della route proviene da un valore predefinito per . La route creata da è equivalente al codice seguente:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute("blog_route", "Manage/{controller}/{action}/{id?}",
defaults: new { area = "Blog" }, constraints: new { area = "Blog" });
endpoints.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
});
crea una route usando sia un valore predefinito che un vincolo per usando il nome dell'area specificato, in questo caso . Il valore predefinito garantisce che il percorso produca sempre un risultato, e che il vincolo richieda il valore specifico per la generazione di URL.
Il routing convenzionale dipende dall'ordine. In generale, posizionare prima gli itinerari con aree, poiché sono più specifici rispetto agli itinerari senza area.
Usando l'esempio precedente, i valori della route corrispondono all'azione seguente:
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace1
{
[Area("Blog")]
public class UsersController : Controller
{
// GET /manage/users/adduser
public IActionResult AddUser()
{
var area = ControllerContext.ActionDescriptor.RouteValues["area"];
var actionName = ControllerContext.ActionDescriptor.ActionName;
var controllerName = ControllerContext.ActionDescriptor.ControllerName;
return Content($"area name:{area}" +
$" controller:{controllerName} action name: {actionName}");
}
}
}
L'attributo [Area] indica un controller come parte di un'area. Questo controller si trova nell'area . I controller senza un attributo non sono membri di alcuna area e non corrispondono quando il valore della route viene fornito dal routing. Nell'esempio seguente solo il primo controller indicato può corrispondere ai valori di route .
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace1
{
[Area("Blog")]
public class UsersController : Controller
{
// GET /manage/users/adduser
public IActionResult AddUser()
{
var area = ControllerContext.ActionDescriptor.RouteValues["area"];
var actionName = ControllerContext.ActionDescriptor.ActionName;
var controllerName = ControllerContext.ActionDescriptor.ControllerName;
return Content($"area name:{area}" +
$" controller:{controllerName} action name: {actionName}");
}
}
}
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace2
{
// Matches { area = Zebra, controller = Users, action = AddUser }
[Area("Zebra")]
public class UsersController : Controller
{
// GET /zebra/users/adduser
public IActionResult AddUser()
{
var area = ControllerContext.ActionDescriptor.RouteValues["area"];
var actionName = ControllerContext.ActionDescriptor.ActionName;
var controllerName = ControllerContext.ActionDescriptor.ControllerName;
return Content($"area name:{area}" +
$" controller:{controllerName} action name: {actionName}");
}
}
}
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace3
{
// Matches { area = string.Empty, controller = Users, action = AddUser }
// Matches { area = null, controller = Users, action = AddUser }
// Matches { controller = Users, action = AddUser }
public class UsersController : Controller
{
// GET /users/adduser
public IActionResult AddUser()
{
var area = ControllerContext.ActionDescriptor.RouteValues["area"];
var actionName = ControllerContext.ActionDescriptor.ActionName;
var controllerName = ControllerContext.ActionDescriptor.ControllerName;
return Content($"area name:{area}" +
$" controller:{controllerName} action name: {actionName}");
}
}
}
Per completezza, il namespace di ogni controller è mostrato qui. Se i controller precedenti usavano lo stesso spazio dei nomi, verrà generato un errore del compilatore. Gli spazi dei nomi delle classi non hanno effetto sul routing di MVC.
I primi due controller sono membri di aree e si abbinano solo quando il rispettivo nome dell'area è fornito dal valore di route . Il terzo controller non è un membro di un'area e può corrispondere solo quando non vengono specificati valori per dal routing.
In termini di corrispondenza con nessun valore, l'assenza del valore è come se il valore per fosse Null o la stringa vuota.
Quando si esegue un'azione all'interno di un'area, il valore di route per è disponibile come valore di ambiente per il routing da usare per la generazione di URL. Ciò significa che per impostazione predefinita le aree funzionano in modo persistente per la generazione di URL, come illustrato nell'esempio seguente.
app.UseEndpoints(endpoints =>
{
endpoints.MapAreaControllerRoute(name: "duck_route",
areaName: "Duck",
pattern: "Manage/{controller}/{action}/{id?}");
endpoints.MapControllerRoute(name: "default",
pattern: "Manage/{controller=Home}/{action=Index}/{id?}");
});
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace4
{
[Area("Duck")]
public class UsersController : Controller
{
// GET /Manage/users/GenerateURLInArea
public IActionResult GenerateURLInArea()
{
// Uses the 'ambient' value of area.
var url = Url.Action("Index", "Home");
// Returns /Manage/Home/Index
return Content(url);
}
// GET /Manage/users/GenerateURLOutsideOfArea
public IActionResult GenerateURLOutsideOfArea()
{
// Uses the empty value for area.
var url = Url.Action("Index", "Home", new { area = "" });
// Returns /Manage
return Content(url);
}
}
}
Il codice seguente genera un URL per :
public class HomeController : Controller
{
public IActionResult About()
{
var url = Url.Action("AddUser", "Users", new { Area = "Zebra" });
return Content($"URL: {url}");
}
Definizione dell'azione
I metodi pubblici in un controller, ad eccezione di quelli con l'attributo NonAction , sono azioni.
Codice di esempio
- MyDisplayRouteInfo viene fornito dal pacchetto NuGet Rick.Docs.Samples.RouteInfo e visualizza le informazioni sulla route.
- Visualizzare o scaricare il codice di esempio (procedura per il download)
Diagnostica del debug
Per ottenere un output di diagnostica di routing dettagliato, impostare su . Nell'ambiente impostare il livello di registrazione in :
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Debug",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}