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
Nota
Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 9 di questo articolo.
Avviso
Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere i criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 9 di questo articolo.
Importante
Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.
Per la versione corrente, vedere la versione .NET 9 di questo articolo.
I controller di ASP.NET Core usano il middleware di Routing per trovare le corrispondenze con gli URL delle richieste in ingresso ed eseguirne il mapping alle azioni. Modelli di route:
- Vengono definiti all'avvio in
Program.cs
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 REST API. Se sei principalmente interessato al routing per le API REST, vai alla sezione Routing degli attributi per le API REST.
- Vedere Routing per informazioni dettagliate sul routing avanzato.
- Fa riferimento al sistema di routing predefinito 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.
Impostare il percorso convenzionale
Il modello ASP.NET Core MVC genera codice di routing convenzionale simile al 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();
MapControllerRoute viene usato per creare una singola route. La singola route è denominata default
route. La maggior parte delle app con controller e visualizzazioni usa un modello di route simile alla default
route.
REST Le API devono usare il routing degli attributi.
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Modello di route "{controller=Home}/{action=Index}/{id?}"
:
Corrisponde a un percorso URL, ad esempio
/Products/Details/5
Estrae i valori
{ controller = Products, action = Details, id = 5 }
della route suddividendo il percorso in token. L'estrazione dei valori di route genera una corrispondenza se l'app ha un controller denominatoProductsController
e un'azioneDetails
: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.
/Products/Details/5
model collega il valore diid = 5
per impostare il parametroid
su5
. Per altri dettagli, vedere Associazione di modelli.{controller=Home}
definisceHome
come valore predefinitocontroller
.{action=Index}
definisceIndex
come valore predefinitoaction
.Il
?
carattere in{id?}
definisceid
come facoltativo.- I parametri di route predefiniti e facoltativi non devono necessariamente essere presenti nel percorso URL per trovare una corrispondenza. Vedere Riferimento per i modelli di route per una descrizione dettagliata della sintassi del modello di route.
Trova la corrispondenza con il percorso URL
/
.Produce i valori di route
{ controller = Home, action = Index }
.
I valori per controller
e action
usano i valori predefiniti.
id
non produce un valore perché non esiste alcun segmento corrispondente nel percorso URL.
/
corrisponde solo se esiste un'azione HomeController
e Index
:
public class HomeController : Controller
{
public IActionResult Index() { ... }
}
Usando la definizione del controller e il modello di route precedenti, l'azione HomeController.Index
viene eseguita per i percorsi URL seguenti:
/Home/Index/17
/Home/Index
/Home
/
Il percorso URL /
utilizza i controller predefiniti Home
e l'azione predefinita Index
del modello di route. Il percorso /Home
URL usa l'azione predefinita Index
del modello di route.
Il metodo pratico MapDefaultControllerRoute:
app.MapDefaultControllerRoute();
Sostituisce:
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Importante
Il routing viene configurato usando il UseRouting e il UseEndpoints middleware. Per usare i controller:
- Chiamata MapControllers per eseguire il mapping dei controller indirizzati con attributi.
- Chiamare MapControllerRoute o MapAreaControllerRoute per eseguire il mapping di controller indirizzati convenzionalmente e di controller indirizzati mediante attributi.
Le app in genere non devono chiamare UseRouting
o UseEndpoints
.
WebApplicationBuilder configura una pipeline middleware che avvolge il middleware aggiunto in Program.cs
con UseRouting
e UseEndpoints
. Per altre informazioni, vedere Routing in ASP.NET Core.
Routing convenzionale
Il routing convenzionale viene usato con controller e visualizzazioni. La route predefinita (default
):
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
L'esempio precedente è un esempio di percorso convenzionale. Si chiama routing convenzionale perché stabilisce una convenzione per i percorsi URL:
- Il primo segmento di percorso,
{controller=Home}
, corrisponde al nome del controller. - Il secondo segmento,
{action=Index}
, esegue il mapping al nome dell'azione. - Il terzo segmento
{id?}
viene usato per unid
facoltativo. l'oggetto?
in{id?}
lo rende facoltativo.id
viene usato per eseguire il mapping a un'entità del modello.
Usando questa default
route, il percorso URL:
-
/Products/List
corrisponde all'azioneProductsController.List
. -
/Blog/Article/17
esegue il mapping aBlogController.Article
e in genere il modello associa ilid
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.
L'uso del routing convenzionale con la route predefinita consente di creare l'app senza dover 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.
Avviso
Il id
nel codice precedente è definito come facoltativo dal modello di route. Le azioni possono essere eseguite senza l'ID facoltativo fornito come parte dell'URL. In genere, quando id
viene omesso dall'URL:
-
id
è impostato su per associazione di0
modelli. - Nessuna entità viene trovata nel database corrispondente a
id == 0
.
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 id
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 {controller=Home}/{action=Index}/{id?}
:
- 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.
MapControllerRoute e MapAreaRoute :
- Assegnare automaticamente un valore di ordine agli endpoint in base all'ordine in cui vengono richiamati.
Endpoint routing in ASP.NET Core:
- Non ha il concetto di percorsi.
- Non fornisce garanzie di ordinamento per l'esecuzione dell'estendibilità, tutti gli endpoint vengono elaborati contemporaneamente.
Abilitare la registrazione per verificare in che modo le implementazioni del routing predefinite, ad esempio Route, 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 MapControllerRoute e MapAreaControllerRoute. In questo modo è 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 blog
route nel codice precedente è un percorso convenzionale dedicato. Si chiama route convenzionale dedicata perché:
Poiché controller
e action
non vengono visualizzati nel modello "blog/{*article}"
di route come parametri:
- Possono avere solo i valori
{ controller = "Blog", action = "Article" }
predefiniti. - Questa route esegue sempre il mapping all'azione
BlogController.Article
.
/Blog
, /Blog/Article
e /Blog/{any-string}
sono gli unici percorsi URL che corrispondono alla route del blog.
L'esempio precedente:
-
blog
il percorso ha una priorità più alta per le corrispondenze rispetto aldefault
perché viene aggiunto per primo. - Ecco un esempio di routing in stile Slug, in cui è tipico includere il nome di un articolo come parte dell'URL.
Avviso
In ASP.NET Core il routing non è:
- Definire un concetto denominato route.
UseRouting
aggiunge la corrispondenza della route alla pipeline middleware. IlUseRouting
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 IRouteConstraint o IActionConstraint.
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 è progettato per semplificare i casi in cui le route convenzionali si sovrappongono.
L'aggiunta di route tramite MapControllerRoute, MapDefaultControllerRoutee MapAreaControllerRoute assegna automaticamente un valore di ordine agli endpoint in base all'ordine in cui vengono richiamate. Le corrispondenze di una route visualizzata in precedenza hanno una priorità più alta. Il routing convenzionale dipende dall'ordine. In generale, le rotte con aree devono essere posizionate in precedenza perché sono più specifiche di quelle senza un'area.
Percorsi convenzionali dedicati con parametri di percorso catch-all come {*article}
possono rendere un percorso troppo onnivoro, ovvero corrisponde agli URL che si intendeva associare ad altri percorsi. Inserire le rotte avide più avanti nella tabella delle rotte per impedire corrispondenze avide.
Avviso
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
{**slug}"
- 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 per casi di esempio che riscontrano questo bug.
Una correzione di consenso esplicito per questo bug è contenuta in .NET Core 3.1.301 SDK e versioni successive. 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.
Ad 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
/Products33/Edit/17
- Indirizzare i dati
{ controller = Products33, action = Edit, id = 17 }
.
Si tratta di un modello tipico per i controller MVC:
-
Edit(int)
visualizza un modulo per modificare un prodotto. -
Edit(int, Product)
elabora il modulo pubblicato.
Per risolvere la route corretta:
-
Edit(int, Product)
viene selezionato quando la richiesta è un httpPOST
. -
Edit(int)
viene selezionato quando il verbo HTTP è qualsiasi altro.Edit(int)
viene in genere chiamato tramiteGET
.
L'oggetto HttpPostAttribute, [HttpPost]
viene fornito al routing in modo che possa scegliere in base al metodo HTTP della richiesta. rende HttpPostAttribute
Edit(int, Product)
una corrispondenza migliore rispetto a Edit(int)
.
È importante comprendere il ruolo degli attributi, ad esempio HttpPostAttribute
. 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 un candidato migliore, viene generata un'eccezione AmbiguousMatchException , elencando più endpoint corrispondenti.
Nomi di route convenzionali
Le stringhe "blog"
e "default"
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 REST API
REST 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 REST 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, MapControllers viene chiamato per mappare i controller con routing basato su attributi.
Nell'esempio seguente :
-
HomeController
corrisponde a un set di URL simili a quello che corrisponde alla route{controller=Home}/{action=Index}/{id?}
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 HomeController.Index
viene eseguita per uno dei percorsi /
URL , , /Home
/Home/Index
o /Home/Index/3
.
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 action
e controller
:
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 [Route("[controller]/[action]")]
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 Index
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 Razor pagine:
action
area
controller
handler
page
L'uso di page
come parametro di route nel routing degli attributi è un errore comune. In questo modo si verifica un comportamento incoerente e confuso con la generazione di URL.
public class MyDemo2Controller : Controller
{
[Route("/articles/{page}")]
public IActionResult ListArticles(int page)
{
return ControllerContext.MyDisplayRouteInfo(page);
}
}
I nomi dei parametri speciali vengono usati dalla generazione di URL per determinare se un'operazione di generazione url fa riferimento a una Razor pagina o a un controller.
Le parole chiave seguenti sono riservate nel contesto di una Razor visualizzazione o di una Razor pagina:
page
using
namespace
inject
section
inherits
model
addTagHelper
removeTagHelper
Queste parole chiave non devono essere usate per le generazioni di collegamenti, i parametri associati al modello o le proprietà di livello superiore.
Modelli di verbo HTTP
ASP.NET Core include i seguenti modelli di verbi HTTP:
Modelli di route
ASP.NET Core ha i seguenti modelli di route:
- Tutti i modelli verbo HTTP sono modelli di route.
- [Route]
Routing basato su attributi con attribuzione di 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
[HttpGet]
, che vincola solo la corrispondenza alle richieste HTTP GET. - L'azione
GetProduct
include il modello"{id}"
, quindiid
viene aggiunto al modello nel controller"api/[controller]"
. Il modello di metodi è"api/[controller]/{id}"
. Pertanto, questa azione corrisponde solo alle richieste GET per il modulo/api/test2/xyz
,/api/test2/123
/api/test2/{any string}
e così via.[HttpGet("{id}")] // GET /api/test2/xyz public IActionResult GetProduct(string id) { return ControllerContext.MyDisplayRouteInfo(id); }
- L'azione
GetIntProduct
contiene il"int/{id:int}"
modello. La:int
parte del modello vincola i valori diid
route alle stringhe che possono essere convertite in un numero intero. Una richiesta GET a/api/test2/int/abc
:- 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
GetInt2Product
contiene{id}
nel modello, ma non limitaid
i valori che possono essere convertiti in un numero intero. Richiesta GET a/api/test2/int2/abc
:- Corrisponde a questa route.
- L'associazione di modelli non riesce a eseguire la conversione
abc
in un numero intero. Ilid
parametro del metodo è integer. - Restituisce una richiesta non valida 400 perché l'associazione di modelli non è riuscita a eseguire la conversione
abc
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 HttpMethodAttribute attributi come HttpPostAttribute, HttpPutAttributee HttpDeleteAttribute. 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 /products3
URL :
- L'azione
MyProductsController.ListProducts
viene eseguita quando il verbo HTTP èGET
. - L'azione
MyProductsController.CreateProduct
viene eseguita quando il verbo HTTP èPOST
.
Quando si compila un'API REST , è raro che sia necessario usare [Route(...)]
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 REST API devono conoscere i percorsi e i verbi HTTP mappati a operazioni logiche specifiche.
REST 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. Ciò significa che molte operazioni, ad esempio GET e POST nella stessa risorsa logica, usano lo stesso URL. Il routing degli attributi offre un livello di controllo necessario per progettare con attenzione 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 id
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 Products2ApiController.GetProduct(int)
:
- Viene eseguito con il percorso URL, ad esempio
/products2/3
- Non viene eseguito con il percorso URL
/products2
.
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 [ApiController]
, consultare l'attributo ApiController.
Nome del percorso
Il codice seguente definisce un nome di route di Products_List
:
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
I nomi di route possono essere usati per generare un URL in base a un percorso specifico. 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 id
parametro come facoltativo ({id?}
). La possibilità di specificare con precisione le API presenta vantaggi, ad esempio consentire di inviare /products
e /products/5
a diverse azioni.
Combinare i percorsi attributo
Per rendere il routing con attributi meno ripetitivo, gli attributi di route del controller vengono combinati con gli attributi di route delle singole azioni. I modelli di route definiti per il controller vengono anteposti ai modelli di route delle azioni. Inserendo un attributo di route nel controller, tutte le azioni presenti nel controller useranno il routing con 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
/products
può corrispondereProductsApi.ListProducts
- Il percorso
/products/5
URL può corrispondere aProductsApi.GetProduct(int)
.
Entrambe queste azioni corrispondono solo a HTTP GET
perché sono contrassegnate con l'attributo [HttpGet]
.
I modelli di route applicati a un'azione 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 [Route]
attributi nel codice precedente:
Attributo | Combina con [Route("Home")] |
Definisce il modello di route |
---|---|---|
[Route("")] |
Sì | "Home" |
[Route("Index")] |
Sì | "Home/Index" |
[Route("/")] |
No | "" |
[Route("About")] |
Sì | "Home/About" |
Ordine di route degli attributi
Il routing crea un albero e confronta tutti gli endpoint contemporaneamente.
- Le voci di route 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 blog/search/{topic}
è più specifica di una route di attributi come blog/{*article}
. La blog/search/{topic}
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 Order proprietà . Tutti gli attributi di route forniti dal framework includono Order
. Le rotte vengono elaborate in base a un ordinamento crescente della proprietà Order
. L'ordine predefinito è 0
. Impostare una rotta tramite Order = -1
viene eseguita prima delle rotte che non impostano un ordine. Impostare una route utilizzando Order = 1
viene eseguito dopo l'ordinamento predefinito delle route.
Evitare di fare affidamento suOrder
. 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 Order
proprietà .
Si considerino i due controller seguenti che definiscono entrambi la route corrispondente /home
:
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 /home
con 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 Order
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, /home
esegue l'endpoint HomeController.Index
. Per accedere a MyDemoController.MyIndex
, richiedere /home/MyIndex
.
Nota:
- Il codice precedente è un esempio o una progettazione di routing scadente. È stato usato per illustrare la
Order
proprietà . - La proprietà
Order
risolve solo l'ambiguità, impedendo che il modello venga corrisposto. Sarebbe preferibile rimuovere il[Route("Home")]
modello.
Per informazioni sull'ordine dei percorsi con Razor Pagine, vedi Convenzioni di percorso e app: Ordine.
In alcuni casi, viene restituito un errore HTTP 500 con delle route ambigue. Utilizzare i log per vedere quali endpoint hanno causato il AmbiguousMatchException
.
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 [action]
, [area]
e [controller]
vengono sostituiti con i valori del nome dell'azione, del nome dell'area e del nome del controller dall'azione in cui è definita 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
/Products0/List
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
- Corrisponde
/Products0/Edit/{id}
La sostituzione dei token avviene come ultimo passaggio della creazione delle route con attributi. 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 lo si legge in una lingua diversa dall'inglese, segnalarlo in questo problema di discussione su GitHub se si vogliono visualizzare i commenti del codice nella lingua nativa.
Le rotte con attributi possono anche essere combinate con l'ereditarietà. Questo è potente se combinato con la sostituzione dei token. La sostituzione dei token si applica anche ai nomi di route definiti dalle route con attributi.
[Route("[controller]/[action]", Name="[controller]_[action]")]
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
La sostituzione dei token può essere personalizzata usando un trasformatore di parametri. Un trasformatore di parametri implementa IOutboundParameterTransformer e trasforma il valore dei parametri. Ad esempio, un trasformatore di parametro personalizzato SlugifyParameterTransformer
modifica il valore della SubscriptionManagement
route in subscription-management
:
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();
}
}
RouteTokenTransformerConvention è una convenzione del modello di applicazione che:
- Applica un trasformatore di parametri a tutte le route di attributi in un'applicazione.
- Personalizza i valori dei token delle route di attributi quando vengono sostituiti.
public class SubscriptionManagementController : Controller
{
[HttpGet("[controller]/[action]")]
public IActionResult ListAll()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Il metodo precedente ListAll
corrisponde a /subscription-management/list-all
.
l'oggetto RouteTokenTransformerConvention
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, vedere la documentazione Web MDN su Slug .
Avviso
Quando si usa System.Text.RegularExpressions per elaborare l'input non attendibile, passare un timeout. Un utente malintenzionato può fornire input a RegularExpressions
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 IActionConstraint
.
Quando vengono posizionati più attributi di route che implementano IActionConstraint 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 [HttpPost("product14/{id:int}")]
precedente applica un vincolo di route. L'azione Products14Controller.ShowProduct
viene confrontata solo in base ai percorsi URL, ad esempio /product14/3
. La parte {id:int}
del modello di route vincola tale segmento solo a numeri interi.
Vedere Riferimento per i modelli di route per una descrizione dettagliata della sintassi del modello di route.
Attributi di route personalizzati con IRouteTemplateProvider
Tutti gli attributi della route implementano IRouteTemplateProvider. 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
IRouteTemplateProvider
per compilare il set iniziale di route.
Implementare IRouteTemplateProvider
per definire attributi di route personalizzati. Ogni IRouteTemplateProvider
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 Get
restituisce Order = 2, Template = api/MyTestApi
.
Usare il modello di applicazione per personalizzare le route degli attributi
Il modello di applicazione:
- Modello a oggetti creato all'avvio in
Program.cs
. - 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 IRouteTemplateProvider
. Convenzioni:
- 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 tramite 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 namespace
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 NamespaceRoutingConvention
:
[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 NamespaceRoutingConvention.Apply
:
- Non esegue alcuna operazione se il controller è instradato con attributi.
- Imposta il modello di controller in base a
namespace
, con la basenamespace
rimossa.
L'oggetto NamespaceRoutingConvention
può essere applicato in Program.cs
:
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
namespace
èMy.Application
. - Il nome completo del controller precedente è
My.Application.Admin.Controllers.UsersController
. - Imposta il modello del controller di
NamespaceRoutingConvention
suAdmin/Controllers/Users/[action]/{id?
.
Può NamespaceRoutingConvention
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 applicazioni ASP.NET Core possono combinare l'uso del routing convenzionale e del routing basato sugli attributi. È tipico usare route convenzionali per i controller che servono pagine HTML per i browser e il routing degli attributi per le API servite dai controller REST.
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.
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 string id
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 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 IUrlHelper è l'elemento sottostante dell'infrastruttura tra MVC e routing per la generazione di URL. Un'istanza di IUrlHelper
è disponibile tramite la Url
proprietà nei controller, nelle visualizzazioni e nei componenti di visualizzazione.
Nell'esempio seguente l'interfaccia IUrlHelper
viene usata tramite la Controller.Url
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 url
variabile è la stringa /UrlGeneration/Destination
di percorso URL . Questo percorso URL viene creato tramite il routing combinando:
- Valori di route della richiesta corrente, denominati valori di ambiente.
- I valori passati a
Url.Action
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
id
modello di percorso{controller}/{action}/{id?}
.
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 Url.Action
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
controller
eaction
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 Source
nel codice precedente genera custom/url/to/destination
.
LinkGenerator è stato aggiunto in ASP.NET Core 3.0 come alternativa a IUrlHelper
.
LinkGenerator
offre funzionalità simili ma più flessibili. Ogni metodo in IUrlHelper
ha anche una famiglia di metodi corrispondente su LinkGenerator
.
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 Url.Action
, i valori di route correnti per controller
e action
vengono forniti dal runtime:
- Il valore di
controller
eaction
fa parte sia dei valori di ambiente che degli altri valori. Il metodoUrl.Action
usa sempre i valori correnti diaction
econtroller
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 {a}/{b}/{c}/{d}
con i valori di ambiente { a = Alice, b = Bob, c = Carol, d = David }
.
- 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 { d = Donovan }
viene aggiunto:
- Il valore
{ d = David }
viene ignorato. - Il percorso URL generato è
Alice/Bob/Carol/Donovan
.
Avviso: i percorsi URL sono gerarchici. Nell'esempio precedente, se il valore { c = Cheryl }
viene aggiunto:
- Entrambi i valori
{ c = Carol, d = David }
vengono ignorati. - Non esiste più un valore per
d
e la generazione di URL non riesce. - I valori desiderati di
c
ed
devono essere specificati per generare un URL.
È possibile che si verifichi questo problema con la route {controller}/{action}/{id?}
predefinita . Questo problema è raro in pratica perché Url.Action
specifica sempre in modo esplicito un controller
valore e action
.
Diversi overload di Url.Action accettano un oggetto di valori di route per fornire valori per i parametri di route diversi da controller
e action
. L'oggetto di valori route viene spesso usato con id
. Ad esempio: Url.Action("Buy", "Products", new { id = 17 })
. L'oggetto dei valori di percorso
- Per convenzione è in genere un oggetto di tipo anonimo.
- Può essere un oggetto
IDictionary<>
o un POCO.
I valori di route aggiuntivi che non corrispondono a 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 /Products/Buy/17?color=red
.
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
protocol
. 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.
IUrlHelper
fornisce anche la famiglia di metodi Url.RouteUrl . Questi metodi sono simili a Url.Action, ma non copiano i valori correnti di action
e controller
nei valori del percorso. Utilizzo più comune di Url.RouteUrl
:
- 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 Razor genera un collegamento HTML a Destination_Route
:
<h1>Test Links</h1>
<ul>
<li><a href="@Url.RouteUrl("Destination_Route")">Test Destination_Route</a></li>
</ul>
Generare URL in HTML e Razor
IHtmlHelper fornisce i HtmlHelper metodi Html.BeginForm e Html.ActionLink per generare <form>
e <a>
gli elementi rispettivamente. Questi metodi usano il metodo Url.Action per generare un URL e accettano argomenti simili. Gli oggetti Url.RouteUrl
complementi di HtmlHelper
sono Html.BeginRouteForm
e Html.RouteLink
e hanno una funzionalità simile.
Gli helper tag generano gli URL attraverso l'helper tag form
e l'helper tag <a>
. Entrambi usano IUrlHelper
per la propria implementazione. Per ulteriori informazioni, vedere Helper tag nei moduli.
All'interno delle visualizzazioni, IUrlHelper
è disponibile tramite la proprietà Url
per qualsiasi generazione di URL ad hoc che non rientra nelle situazioni descritte in precedenza.
Generazione di URL nei risultati dell'azione
Negli esempi precedenti è stato illustrato l'uso di IUrlHelper
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 ControllerBase e Controller 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 RedirectToAction e CreatedAtAction, seguono un modello simile a quello dei metodi su IUrlHelper
.
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 blog
è 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, Url.Action("Index", "Home")
genera il percorso /
URL usando la default
route, ma perché? Si potrebbe pensare che i valori di route { controller = Home, action = Index }
siano sufficienti per generare un URL usando blog
e che il risultato sia /blog?action=Index&controller=Home
.
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 { controller = Blog, action = Article }
e né controller
né action
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 blog
ha esito negativo perché i valori { controller = Home, action = Index }
non corrispondono a { controller = Blog, action = Article }
. Il routing quindi passa al tentativo di utilizzare default
, che ha esito positivo.
Aree
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, area
a controller
e action
. 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 area
route per un area
denominato Blog
:
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, MapAreaControllerRoute viene chiamato per creare "blog_route"
. Il secondo parametro, "Blog"
, è il nome dell'area.
Quando si confronta un percorso URL come /Manage/Users/AddUser
, la route "blog_route"
genera i valori di route { area = Blog, controller = Users, action = AddUser }
. Il valore della area
route viene prodotto da un valore predefinito per area
. La route creata da MapAreaControllerRoute
è equivalente alla seguente:
app.MapControllerRoute("blog_route", "Manage/{controller}/{action}/{id?}",
defaults: new { area = "Blog" }, constraints: new { area = "Blog" });
app.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
MapAreaControllerRoute
crea una route che usa sia un valore predefinito che un vincolo per area
usando il nome di area specificato, in questo caso Blog
. Il valore predefinito assicura che la route generi sempre { area = Blog, ... }
, il vincolo richiede il valore { area = Blog, ... }
per la generazione di URL.
Il routing convenzionale dipende dall'ordine. In generale, le rotte con aree devono essere posizionate in precedenza perché sono più specifiche di quelle senza un'area.
Usando l'esempio precedente, i valori { area = Blog, controller = Users, action = AddUser }
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 Blog
. I controller senza un [Area]
attributo non sono membri di alcuna area e non corrispondono quando il valore della area
route viene fornito dal routing. Nell'esempio seguente solo il primo controller indicato può corrispondere ai valori di route { area = Blog, controller = Users, action = AddUser }
.
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}");
}
}
}
Lo spazio dei nomi di ogni controller viene visualizzato qui per completezza. 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 area
. Il terzo controller non è un membro di un'area e può corrispondere solo quando non vengono specificati valori per area
dal routing.
In termini di corrispondenza con nessun valore, l'assenza del valore area
è come se il valore per area
fosse Null o la stringa vuota.
Quando si esegue un'azione all'interno di un'area, il valore di route per area
è 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 /Zebra/Users/AddUser
:
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 di debug
Per ottenere un output di diagnostica di routing dettagliato, impostare Logging:LogLevel:Microsoft
su Debug
. Nell'ambiente di sviluppo impostare il livello di log in appsettings.Development.json
:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Debug",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
I controller di ASP.NET Core usano il middleware di Routing per confrontare gli URL delle richieste in ingresso e mappare alle azioni. 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. L'inserimento di una route sul controller o sull'azione lo rende basato su 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 utilizzato con le REST API. Se sei principalmente interessato al routing per REST API, passa alla sezione sul routing basato su attributi per le REST 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à. Vedere la guida alla migrazione 2.2-3.0 per istruzioni. Per informazioni di riferimento sul sistema di routing legacy, vedere la versione 2.2 di questo documento .
Configurare il percorso convenzionale
Startup.Configure
in genere ha codice simile al 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 UseEndpointsviene MapControllerRoute usato per creare una singola route. La singola route è denominata default
route. La maggior parte delle app con controller e visualizzazioni usa un modello di route simile alla default
route.
REST Le API devono usare il routing degli attributi.
Modello di route "{controller=Home}/{action=Index}/{id?}"
:
Corrisponde a un percorso URL, ad esempio
/Products/Details/5
Estrae i valori del percorso
{ controller = Products, action = Details, id = 5 }
tramite tokenizzazione. L'estrazione dei valori di route genera una corrispondenza se l'app ha un controller denominatoProductsController
e un'azioneDetails
: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.
/Products/Details/5
model associa il valore diid = 5
per impostare il parametroid
su5
. Per altri dettagli, vedere Associazione di modelli.{controller=Home}
definisceHome
come valore predefinitocontroller
.{action=Index}
definisceIndex
come valore predefinitoaction
.Il
?
carattere in{id?}
definisceid
come facoltativo.I parametri di route predefiniti e facoltativi non devono necessariamente essere presenti nel percorso URL per trovare una corrispondenza. Vedere Riferimento per i modelli di route per una descrizione dettagliata della sintassi del modello di route.
Corrisponde al percorso URL
/
.Produce i valori
{ controller = Home, action = Index }
di route .
I valori per controller
e action
usano i valori predefiniti.
id
non produce un valore perché non esiste alcun segmento corrispondente nel percorso URL.
/
corrisponde solo se esiste un'azione HomeController
e Index
:
public class HomeController : Controller
{
public IActionResult Index() { ... }
}
Usando la definizione del controller e il modello di route precedenti, l'azione HomeController.Index
viene eseguita per i percorsi URL seguenti:
/Home/Index/17
/Home/Index
/Home
/
Il percorso URL /
usa i controller predefiniti Home
e l'azione predefinita Index
del modello di route. Il percorso URL /Home
utilizza il modello di routing predefinito Index
.
Il metodo pratico MapDefaultControllerRoute:
endpoints.MapDefaultControllerRoute();
Sostituisce:
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
Importante
Il routing viene configurato utilizzando il middleware UseRouting, MapControllerRoute
e MapAreaControllerRoute
. Per usare i controller:
- Chiamare MapControllers all'interno di
UseEndpoints
per eseguire il mapping dei controller basati su attributi. - Chiamare MapControllerRoute o MapAreaControllerRoute per eseguire il mapping di controller indirizzati convenzionalmente e controller indirizzati con attributi.
Instradamento convenzionale
Il routing convenzionale viene usato con controller e visualizzazioni. Il percorso default
:
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Il precedente è un esempio di un percorso convenzionale. Si chiama routing convenzionale perché stabilisce una convenzione per i percorsi URL:
- Il primo segmento di percorso,
{controller=Home}
, mappa al nome del controller. - Il secondo segmento,
{action=Index}
, esegue il mapping al nome dell'azione. - Il terzo segmento viene
{id?}
usato per un oggetto facoltativoid
. l'oggetto?
in{id?}
lo rende facoltativo.id
viene usato per eseguire il mapping a un'entità del modello.
Usando questo percorso default
, il percorso URL:
-
/Products/List
mappa all'azioneProductsController.List
. -
/Blog/Article/17
esegue il mapping aBlogController.Article
e in genere il modello associa ilid
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.
L'uso del routing convenzionale con la route predefinita consente di creare l'app senza dover 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.
Avviso
Nel id
codice precedente viene definito come facoltativo dal modello di route. Le azioni possono essere eseguite senza l'ID facoltativo fornito come parte dell'URL. In genere, quando id
viene omesso dall'URL:
-
id
è impostato su0
tramite associazione di modelli. - Nessuna entità viene trovata nel database corrispondente a
id == 0
.
Il routing degli attributi fornisce un controllo granulare per rendere l'ID necessario per alcune azioni e non per altri. Per convenzione, la documentazione include parametri facoltativi come id
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 {controller=Home}/{action=Index}/{id?}
:
- 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 percorso alternativo che usa Aree è spesso tutto ciò che serve.
MapControllerRoute e MapAreaRoute :
- 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 e versioni successive:
- Non ha un concetto di percorsi.
- Non fornisce garanzie di ordinamento per l'esecuzione dell'estendibilità, tutti gli endpoint vengono elaborati contemporaneamente.
Abilitare la registrazione per verificare in che modo le implementazioni del routing predefinite, ad esempio Route, corrispondono alle richieste.
Il routing degli attributi è illustrato più avanti in questo documento.
Più route convenzionali
È possibile aggiungere più route convenzionali all'interno UseEndpoints
aggiungendo altre chiamate a MapControllerRoute e MapAreaControllerRoute. 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 blog
rotta nel codice precedente è una rotta convenzionale dedicata. Si chiama route convenzionale dedicata perché:
Poiché controller
e action
non vengono visualizzati nel modello "blog/{*article}"
di route come parametri:
- Possono avere solo i valori
{ controller = "Blog", action = "Article" }
predefiniti. - Questa route esegue sempre il mapping dell'azione
BlogController.Article
.
/Blog
, /Blog/Article
e /Blog/{any-string}
sono gli unici percorsi URL che corrispondono alla route del blog.
L'esempio precedente:
-
blog
il percorso ha una priorità più alta per le corrispondenze rispetto adefault
il percorso perché viene aggiunto per primo. - Esempio di routing dello stile Slug in cui è tipico avere un nome di articolo come parte dell'URL.
Avviso
In ASP.NET Core 3.0 e versioni successive, il routing non esegue:
- Definire un concetto denominato route.
UseRouting
aggiunge l'associazione del percorso alla pipeline middleware. IlUseRouting
middleware esamina il set di endpoint definiti nell'app e seleziona la migliore corrispondenza dell'endpoint in base alla richiesta ricevuta. - Fornire garanzie sull'ordine di esecuzione dell'estendibilità, ad esempio IRouteConstraint o IActionConstraint.
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 è progettato per semplificare i casi in cui le route convenzionali si sovrappongono.
L'aggiunta di route tramite MapControllerRoute, MapDefaultControllerRoutee MapAreaControllerRoute assegna automaticamente un valore di ordine agli endpoint in base all'ordine in cui vengono richiamate. Le corrispondenze di una route visualizzata in precedenza hanno una priorità più alta. Il routing convenzionale è ordine-dipendente. In generale, le rotte con aree devono essere posizionate in precedenza perché sono più specifiche di quelle senza un'area.
Le rotte convenzionali dedicate con parametri di route catch-all come {*article}
possono creare delle rotte troppo avide, ovvero possono corrispondere agli URL che si intende associare ad altre rotte. Inserire le route esigenti più avanti nella tabella di instradamento per impedire corrispondenze esigenti.
Avviso
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 itinerario catch-all, ad esempio
{**slug}"
- La route catch-all non riesce a trovare una corrispondenza con le richieste che deve corrispondere.
- La rimozione di altri percorsi fa sì che il percorso catch-all inizi a funzionare.
Consultare i bug di GitHub 18677 e 16579 per casi di esempio che esemplificano questo bug.
Una correzione di consenso esplicito per questo bug è contenuta in .NET Core 3.1.301 SDK e versioni successive. 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.
- Generazione di un'eccezione.
Ad 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
/Products33/Edit/17
- Indirizzare i dati
{ controller = Products33, action = Edit, id = 17 }
.
Si tratta di un modello tipico per i controller MVC:
-
Edit(int)
visualizza un modulo per modificare un prodotto. -
Edit(int, Product)
elabora il modulo pubblicato.
Per risolvere la route corretta:
-
Edit(int, Product)
viene selezionato quando la richiesta è un httpPOST
. -
Edit(int)
viene selezionato quando il verbo HTTP è diverso.Edit(int)
viene in genere chiamato tramiteGET
.
L'oggetto HttpPostAttribute, [HttpPost]
viene fornito al routing in modo che possa scegliere in base al metodo HTTP della richiesta. rende HttpPostAttribute
Edit(int, Product)
una corrispondenza migliore rispetto a Edit(int)
.
È importante comprendere il ruolo degli attributi, ad esempio HttpPostAttribute
. Gli attributi simili sono definiti per altri verbi HTTP. Nel routing convenzionale, è comune che le azioni usino lo stesso nome di azione quando fanno parte di un modulo di presentazione, inviare il flusso di lavoro del modulo. Ad esempio, vedere Esamina i due metodi dell'azione di modifica.
Se il routing non è in grado di scegliere un candidato migliore, viene generata un'eccezione AmbiguousMatchException , elencando più endpoint corrispondenti.
Nomi di route convenzionali
Le stringhe "blog"
e "default"
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 utilizzato 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 influire 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 REST
REST 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 StartUp.Configure
è tipico per un'API REST 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, MapControllers viene chiamato all'interno di UseEndpoints
per eseguire il mapping dei controller con routing degli attributi.
Nell'esempio seguente :
-
HomeController
corrisponde a un set di URL simili a quello che corrisponde alla route{controller=Home}/{action=Index}/{id?}
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 HomeController.Index
viene eseguita per uno qualsiasi dei percorsi URL /
, /Home
, /Home/Index
o /Home/Index/3
.
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ù input per specificare un percorso. 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 venga utilizzata la sostituzione dei 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 action
e controller
:
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 [Route("[controller]/[action]")]
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 Index
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 di routing
Le parole chiave seguenti sono nomi di parametri di route riservate quando si usano controller o Razor pagine:
action
area
controller
handler
page
L'uso di page
come parametro di rotta con il routing basato sugli attributi è un errore comune. In questo modo si verifica un comportamento incoerente e confuso con la generazione di URL.
public class MyDemo2Controller : Controller
{
[Route("/articles/{page}")]
public IActionResult ListArticles(int page)
{
return ControllerContext.MyDisplayRouteInfo(page);
}
}
I nomi dei parametri speciali vengono usati dalla generazione di URL per determinare se un'operazione di generazione url fa riferimento a una Razor pagina o a un controller.
Le parole chiave seguenti sono riservate nel contesto di una Razor visualizzazione o di una Razor pagina:
page
using
namespace
inject
section
inherits
model
addTagHelper
removeTagHelper
Queste parole chiave non devono essere usate per le generazioni di collegamenti, i parametri associati al modello o le proprietà di livello superiore.
Modelli di verbo HTTP
ASP.NET Core ha i seguenti modelli di verbo HTTP:
Modelli di route
ASP.NET Core include i modelli di route seguenti:
- Tutti i template di verbo HTTP sono template di route.
- [Route]
Routing degli attributi con attributi di verbo 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
[HttpGet]
, che vincola solo la corrispondenza alle richieste HTTP GET. - L'azione
GetProduct
include il"{id}"
modello, quindiid
viene aggiunto al modello nel"api/[controller]"
controller. Il modello di metodi è"api/[controller]/{id}"
. Pertanto, questa azione corrisponde solo alle richieste GET per il modulo/api/test2/xyz
,/api/test2/123
/api/test2/{any string}
e così via.[HttpGet("{id}")] // GET /api/test2/xyz public IActionResult GetProduct(string id) { return ControllerContext.MyDisplayRouteInfo(id); }
- L'azione
GetIntProduct
contiene il"int/{id:int}"
modello. La:int
parte del modello vincola i valori diid
route alle stringhe che possono essere convertite in un numero intero. Richiesta GET a/api/test2/int/abc
:- 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
GetInt2Product
contiene{id}
nel modello, ma non limitaid
i valori che possono essere convertiti in un numero intero. Richiesta GET a/api/test2/int2/abc
:- Corrisponde a questo percorso.
- L'associazione di modelli non riesce a eseguire la conversione
abc
in un numero intero. Ilid
parametro del metodo è integer. - Restituisce una richiesta non valida 400 perché l'associazione di modelli non è riuscita a eseguire la conversione
abc
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 HttpMethodAttribute attributi come HttpPostAttribute, HttpPutAttributee HttpDeleteAttribute. 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);
}
}
Utilizzo del percorso URL /products3
:
- L'azione
MyProductsController.ListProducts
viene eseguita quando il verbo HTTP èGET
. - L'azione
MyProductsController.CreateProduct
viene eseguita quando il verbo HTTP èPOST
.
Quando si compila un'API REST , è raro che sia necessario usare [Route(...)]
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 REST API devono conoscere i percorsi e i verbi HTTP mappati a operazioni logiche specifiche.
REST 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. Ciò significa che molte operazioni, ad esempio GET e POST nella stessa risorsa logica, usano lo stesso URL. Il routing degli attributi offre un livello di controllo necessario per progettare con attenzione 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 id
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 Products2ApiController.GetProduct(int)
:
- Viene eseguito con il percorso URL, ad esempio
/products2/3
- Non viene eseguito con il percorso URL
/products2
.
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 ulteriori informazioni su [ApiController]
, vedere l'attributo ApiController.
Nome percorso
Il codice seguente definisce un nome di route di Products_List
:
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
I nomi di route possono essere usati per generare un URL in base a un percorso specifico. Nomi di route:
- Non avere alcun impatto sul comportamento di corrispondenza dell'URL del routing.
- 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 id
parametro come facoltativo ({id?}
). La possibilità di specificare le API con precisione presenta vantaggi, ad esempio consentire a /products
e /products/5
di essere indirizzati a diverse azioni.
Combinazione di route degli attributi
Per rendere il routing con attributi meno ripetitivo, gli attributi di route del controller vengono combinati con gli attributi di route delle singole azioni. I modelli di route definiti per il controller vengono anteposti ai modelli di route delle azioni. Inserendo un attributo di route nel controller, tutte le azioni presenti nel controller useranno il routing con 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
/products
URL può corrispondereProductsApi.ListProducts
- Il percorso
/products/5
URL può corrispondere aProductsApi.GetProduct(int)
.
Entrambe queste azioni corrispondono solo a HTTP GET
perché sono contrassegnate con l'attributo [HttpGet]
.
I modelli di route applicati a un'azione 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 [Route]
attributi nel codice precedente:
Attributo | Combina con [Route("Home")] |
Definisce il modello di route |
---|---|---|
[Route("")] |
Sì | "Home" |
[Route("Index")] |
Sì | "Home/Index" |
[Route("/")] |
No | "" |
[Route("About")] |
Sì | "Home/About" |
Ordine di route degli attributi
Il routing costruisce un albero e abbina tutti gli endpoint contemporaneamente.
- Le voci di percorso si comportano come se fossero inserite in un ordine ideale.
- Le route più specifiche possono essere eseguite prima delle route più generali.
Ad esempio, una route di attributi come blog/search/{topic}
è più specifica di una route di attributi come blog/{*article}
. La blog/search/{topic}
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à Order. Tutti gli attributi di route forniti dal framework includono Order
. I percorsi vengono elaborati in base a un ordinamento crescente della proprietà Order
. L'ordine predefinito è 0
. Impostare una route con Order = -1
viene eseguito prima delle route che non impostano un ordine. Impostazione di una route usando Order = 1
viene eseguita dopo l'ordinamento predefinito della route.
Evitare di dipendere da Order
. Se lo spazio URL di un'app richiede valori di ordine espliciti per instradare correttamente, è probabile che confonda anche i client. In generale, l'instradamento basato su attributi seleziona la route corretta tramite la corrispondenza 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 Order
proprietà .
Si considerino i due controller seguenti che definiscono entrambi la route corrispondente /home
:
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 /home
con 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 Order
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, /home
esegue l'endpoint HomeController.Index
. Per accedere a MyDemoController.MyIndex
, richiedere /home/MyIndex
.
Nota:
- Il codice precedente è un esempio o una progettazione di routing scadente. È stato usato per illustrare la
Order
proprietà . - La
Order
proprietà risolve solo l'ambiguità, che il modello non può corrispondere. Sarebbe preferibile rimuovere il[Route("Home")]
modello.
Vedi Razor Pagine: convenzioni di route e app: ordine delle route per informazioni sull'ordine delle route con Razor Pagine.
In alcuni casi, viene restituito un errore HTTP 500 con percorsi ambigui. Usare il logging per vedere quali endpoint hanno causato il AmbiguousMatchException
.
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 [action]
, [area]
e [controller]
vengono sostituiti con i valori del nome dell'azione, del nome dell'area e del nome del controller dall'azione in cui è definita 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();
}
- Corrispondenze
/Products0/List
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
- Corrisponde
/Products0/Edit/{id}
La sostituzione dei token avviene come ultimo passaggio della creazione delle route con attributi. 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 lo si legge in una lingua diversa dall'inglese, segnalarlo in questo problema di discussione su GitHub se si vogliono visualizzare i commenti del codice nella lingua nativa.
Le rotte con attributi possono anche essere unite con l'ereditarietà. Questa è una combinazione potente con la sostituzione dei token. La sostituzione dei token si applica anche ai nomi delle route definiti dagli attributi delle route.
[Route("[controller]/[action]", Name="[controller]_[action]")]
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 ]
, effettuare l'escape ripetendo il carattere ([[
o ]]
).
Usare un trasformatore di parametri per personalizzare la sostituzione dei token
La sostituzione dei token può essere personalizzata usando un trasformatore di parametri. Un trasformatore di parametri implementa IOutboundParameterTransformer e trasforma il valore dei parametri. Ad esempio, un trasformatore di parametro personalizzato SlugifyParameterTransformer
modifica il valore della SubscriptionManagement
route in subscription-management
:
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();
}
}
RouteTokenTransformerConvention è una convenzione del modello di applicazione che:
- Applica un trasformatore di parametri a tutte le route di attributi in un'applicazione.
- Personalizza i valori dei token delle route di attributi quando vengono sostituiti.
public class SubscriptionManagementController : Controller
{
[HttpGet("[controller]/[action]")]
public IActionResult ListAll()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Il metodo precedente ListAll
corrisponde a /subscription-management/list-all
.
La RouteTokenTransformerConvention
è registrata come opzione in ConfigureServices
.
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 .
Avviso
Quando si usa System.Text.RegularExpressions per elaborare l'input non attendibile, passare un timeout. Un utente malintenzionato può fornire input a RegularExpressions
, causando un attacco Denial-of-Service. Le API del framework ASP.NET Core che utilizzano RegularExpressions
gestiscono un timeout.
Più 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 relativi al verbo HTTP implementano IActionConstraint
.
Quando vengono posizionati più attributi di route che implementano IActionConstraint 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ù percorsi su azioni solo se necessario, ad esempio, per supportare i clienti esistenti.
Specificare i parametri facoltativi, i valori predefiniti e i 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 [HttpPost("product14/{id:int}")]
precedente applica un vincolo di route. L'azione Products14Controller.ShowProduct
viene confrontata solo in base ai percorsi URL, ad esempio /product14/3
. La parte {id:int}
del modello di route vincola tale segmento solo a numeri interi.
Vedere Riferimento per i modelli di route per una descrizione dettagliata della sintassi del modello di route.
Attributi di route personalizzati con IRouteTemplateProvider
Tutti gli attributi della route implementano IRouteTemplateProvider. 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
IRouteTemplateProvider
per compilare il set iniziale di route.
Implementare IRouteTemplateProvider
per definire attributi di route personalizzati. Ogni IRouteTemplateProvider
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 Get
restituisce Order = 2, Template = api/MyTestApi
.
Usare il modello di applicazione per personalizzare le route basate sugli 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 IRouteTemplateProvider
. Convenzioni:
- 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 tramite 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 namespace
ai controller che utilizzano il routing basato su attributi.
public void Apply(ControllerModel controller)
{
var hasRouteAttributes = controller.Selectors.Any(selector =>
selector.AttributeRouteModel != null);
if (hasRouteAttributes)
{
return;
}
Ad esempio, il controller seguente non usa NamespaceRoutingConvention
:
[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 NamespaceRoutingConvention.Apply
:
- Non esegue alcuna operazione se il controller è instradato.
- Imposta il modello di controller in base a
namespace
, con la basenamespace
rimossa.
L'oggetto NamespaceRoutingConvention
può essere applicato in Startup.ConfigureServices
:
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
namespace
èMy.Application
. - Il nome completo del controller precedente è
My.Application.Admin.Controllers.UsersController
. - Imposta
NamespaceRoutingConvention
il modello controller suAdmin/Controllers/Users/[action]/{id?
.
Può NamespaceRoutingConvention
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
ASP.NET le app core possono combinare l'uso del routing convenzionale e del routing degli attributi. È tipico usare le route convenzionali per i controller che servono pagine HTML ai browser e il routing basato su attributi per i controller che servono le API.
Le azioni vengono indirizzate secondo convenzioni o mediante il routing con attributi. Se una route viene inserita nel controller o nell'azione, viene indirizzata con attributi. Le azioni che definiscono le route basate su attributi non sono raggiungibili 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 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 IUrlHelper è l'elemento sottostante dell'infrastruttura tra MVC e routing per la generazione di URL. Un'istanza di IUrlHelper
è disponibile tramite la Url
proprietà nei controller, nelle visualizzazioni e nei componenti di visualizzazione.
Nell'esempio seguente l'interfaccia IUrlHelper
viene usata tramite la Controller.Url
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 url
variabile è la stringa /UrlGeneration/Destination
di percorso URL . Questo percorso URL viene creato tramite il routing combinando:
- Valori di route della richiesta corrente, denominati valori di ambiente.
- I valori passati a
Url.Action
e la sostituzione di tali valori 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
Il valore di ogni parametro di route del modello di route viene sostituito attraverso la corrispondenza dei nomi con i valori e i valori di ambiente. Un parametro di route che non ha un valore può:
- Usare un valore predefinito se ne ha uno.
- Ignorare se opzionale. Ad esempio, il
id
dalla route del modello{controller}/{action}/{id?}
.
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 Url.Action
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
controller
eaction
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 Source
nel codice precedente genera custom/url/to/destination
.
LinkGenerator è stato aggiunto in ASP.NET Core 3.0 come alternativa a IUrlHelper
.
LinkGenerator
offre funzionalità simili ma più flessibili. Ogni metodo in IUrlHelper
ha anche una famiglia di metodi corrispondente su LinkGenerator
.
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 Url.Action
, i valori di route correnti per controller
e action
vengono forniti dal runtime:
- Il valore di
controller
eaction
fa parte sia dei valori di ambiente che dei valori. Il metodoUrl.Action
usa sempre i valori correnti diaction
econtroller
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 una route come {a}/{b}/{c}/{d}
con i valori di ambiente { a = Alice, b = Bob, c = Carol, d = David }
:
- 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 { d = Donovan }
viene aggiunto:
- Il valore
{ d = David }
viene ignorato. - Il percorso URL generato è
Alice/Bob/Carol/Donovan
.
Avviso: i percorsi URL sono gerarchici. Nell'esempio precedente, se il valore { c = Cheryl }
viene aggiunto:
- Entrambi i valori
{ c = Carol, d = David }
vengono ignorati. - Non esiste più un valore per
d
e la generazione di URL non riesce. - I valori desiderati di
c
ed
devono essere specificati per generare un URL.
È possibile che si verifichi questo problema con la route {controller}/{action}/{id?}
predefinita . Questo problema è raro in pratica perché Url.Action
specifica sempre in modo esplicito un controller
valore e action
.
Diversi overload di Url.Action accettano un oggetto valori di route per fornire valori per i parametri di route diversi da controller
e action
. L'oggetto valori di route viene spesso usato con id
. Ad esempio: Url.Action("Buy", "Products", new { id = 17 })
. L'oggetto dei valori di route:
- Per convenzione è in genere un oggetto di tipo anonimo.
- Può essere un
IDictionary<>
oppure un POCO.
I valori di route aggiuntivi che non corrispondono a 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 /Products/Buy/17?color=red
.
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:
- Overload che accetta un oggetto
protocol
. 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.
IUrlHelper
fornisce anche la famiglia di metodi Url.RouteUrl . Questi metodi sono simili a Url.Action, ma non copiano i valori correnti di action
e controller
nei valori di route. Utilizzo più comune di Url.RouteUrl
:
- 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 Razor genera un collegamento HTML a Destination_Route
:
<h1>Test Links</h1>
<ul>
<li><a href="@Url.RouteUrl("Destination_Route")">Test Destination_Route</a></li>
</ul>
Generare gli URL in HTML e Razor
IHtmlHelper fornisce i HtmlHelper metodi Html.BeginForm e Html.ActionLink per generare <form>
e <a>
gli elementi rispettivamente. Questi metodi usano il metodo Url.Action per generare un URL e accettano argomenti simili. Gli oggetti Url.RouteUrl
complementi di HtmlHelper
sono Html.BeginRouteForm
e Html.RouteLink
e hanno una funzionalità simile.
Gli TagHelpers generano gli URL tramite il TagHelper form
e il TagHelper <a>
. Entrambi usano IUrlHelper
per la propria implementazione. Per ulteriori informazioni, vedere Tag Helpers nei moduli.
All'interno delle visualizzazioni, IUrlHelper
è disponibile attraverso la proprietà Url
per qualsiasi generazione di URL ad hoc non coperte da quanto sopra.
Generazione di URL nei risultati dell'azione
Negli esempi precedenti è stato illustrato come usare IUrlHelper
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 ControllerBase e Controller 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 di fabbrica dei risultati dell'azione, come RedirectToAction e CreatedAtAction, seguono un modello simile ai metodi su IUrlHelper
.
Caso speciale per percorsi convenzionali dedicati
Il routing convenzionale può utilizzare un tipo speciale di definizione di percorso denominato percorso convenzionale dedicato. Nell'esempio seguente la route denominata blog
è 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?}");
});
Usando le definizioni di route precedenti, Url.Action("Index", "Home")
genera il percorso /
URL usando la default
route, ma perché? Si potrebbe pensare che i valori di route { controller = Home, action = Index }
siano sufficienti per generare un URL usando blog
e che il risultato sia /blog?action=Index&controller=Home
.
Le route convenzionali dedicate si basano su un comportamento speciale di valori predefiniti che non hanno un parametro di route corrispondente, impedendo alla route di essere troppo avida nella generazione degli URL. In questo caso i valori predefiniti sono { controller = Blog, action = Article }
e né controller
né action
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 blog
ha esito negativo perché i valori { controller = Home, action = Index }
non corrispondono a { controller = Blog, action = Article }
. Il routing quindi ricorre a default
, che ha esito positivo.
Aree
Le aree sono una funzionalità MVC usata per organizzare le funzionalità correlate in un gruppo come separato:
- Spazio dei nomi di routing per le azioni del controller.
- 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, area
a controller
e action
. In questa sezione viene illustrato come il routing interagisce con le aree. Vedere Aree per informazioni dettagliate su come le aree vengono utilizzate con le visualizzazioni.
L'esempio seguente configura MVC per l'uso della route convenzionale predefinita e di una area
route per un area
denominato Blog
:
app.UseEndpoints(endpoints =>
{
endpoints.MapAreaControllerRoute("blog_route", "Blog",
"Manage/{controller}/{action}/{id?}");
endpoints.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
});
Nel codice precedente, MapAreaControllerRoute viene chiamato per creare il "blog_route"
. Il secondo parametro, "Blog"
, è il nome dell'area.
Quando si confronta un percorso URL come /Manage/Users/AddUser
, la route "blog_route"
genera i valori del percorso { area = Blog, controller = Users, action = AddUser }
. Il valore della area
route viene prodotto da un valore predefinito per area
. La route creata da MapAreaControllerRoute
è equivalente alla 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?}");
});
MapAreaControllerRoute
crea una route che usa sia un valore predefinito che un vincolo per area
usando il nome di area specificato, in questo caso Blog
. Il valore predefinito assicura che la route generi sempre { area = Blog, ... }
, il vincolo richiede il valore { area = Blog, ... }
per la generazione di URL.
Il routing convenzionale dipende dalla sequenza. In generale, le rotte con aree devono essere posizionate in precedenza perché sono più specifiche di quelle senza un'area.
Usando l'esempio precedente, i valori { area = Blog, controller = Users, action = AddUser }
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 Blog
. I controller che non hanno un attributo [Area]
non sono associati ad alcuna area e non corrispondono nel caso in cui il valore del percorso area
sia fornito dal sistema di routing. Nell'esempio seguente solo il primo controller indicato può corrispondere ai valori di route { area = Blog, controller = Users, action = AddUser }
.
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}");
}
}
}
Lo spazio dei nomi di ogni controller viene visualizzato qui per completezza. 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 corrispondono solo quando il rispettivo nome di area è fornito dal valore di route area
. Il terzo controller non è un membro di un'area e può corrispondere solo quando non vengono specificati valori per area
dal routing.
In termini di corrispondenza con nessun valore, l'assenza del valore area
è come se il valore per area
fosse Null o la stringa vuota.
Quando si esegue un'azione all'interno di un'area, il valore di route per area
è disponibile come valore di ambiente per il routing da usare per la generazione di URL. Ciò significa che di default le aree agiscono come aderenti 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 /Zebra/Users/AddUser
:
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)
Eseguire il debug della diagnostica
Per un output di diagnostica del routing dettagliato, impostare Logging:LogLevel:Microsoft
su Debug
. Nell'ambiente di sviluppo impostare il livello di log in appsettings.Development.json
:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Debug",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}