Routing do akcji kontrolera na platformie ASP.NET Core
Ryan Nowak, Kirk Larkin i Rick Anderson
Uwaga
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
Ważne
Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
kontrolery ASP.NET Core używają oprogramowania pośredniczącego routingu, aby dopasować adresy URL żądań przychodzących i mapować je na akcje. Szablony tras:
- Są definiowane podczas uruchamiania w
Program.cs
atrybutach lub w. - Opisz, jak ścieżki adresów URL są dopasowywane do akcji.
- Są używane do generowania adresów URL łączy. Wygenerowane linki są zwykle zwracane w odpowiedziach.
Akcje są tradycyjnie kierowane lub kierowane przez atrybuty. Umieszczenie trasy na kontrolerze lub akcji sprawia, że jest kierowany atrybutem. Aby uzyskać więcej informacji, zobacz Routing mieszany .
Ten dokument:
- Objaśnia interakcje między mvc i routingiem:
- Jak typowe aplikacje MVC korzystają z funkcji routingu.
- Obejmuje oba:
- Konwencjonalny routing zwykle używany z kontrolerami i widokami.
- Routing atrybutów używany z REST interfejsami API. Jeśli interesuje Cię przede wszystkim routing dla REST interfejsów API, przejdź do sekcji Routing atrybutów dla REST interfejsów API.
- Zobacz Routing , aby uzyskać szczegółowe informacje na temat routingu zaawansowanego.
- Odwołuje się do domyślnego systemu routingu o nazwie routing punktów końcowych. Do celów zgodności można używać kontrolerów z poprzednią wersją routingu. Aby uzyskać instrukcje, zobacz przewodnik migracji 2.2-3.0.
Konfigurowanie konwencjonalnej trasy
Szablon ASP.NET Core MVC generuje konwencjonalny kod routingu podobny do następującego:
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 służy do tworzenia pojedynczej trasy. Pojedyncza trasa nosi nazwę default
route. Większość aplikacji z kontrolerami i widokami używa szablonu trasy podobnego do default
trasy. REST Interfejsy API powinny używać routingu atrybutów.
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Szablon "{controller=Home}/{action=Index}/{id?}"
trasy:
Pasuje do ścieżki adresu URL, takiej jak
/Products/Details/5
Wyodrębnia wartości
{ controller = Products, action = Details, id = 5 }
tras, tokenizując ścieżkę. Wyodrębnianie wartości tras powoduje dopasowanie, jeśli aplikacja ma kontroler o nazwieProductsController
iDetails
akcję:public class ProductsController : Controller { public IActionResult Details(int id) { return ControllerContext.MyDisplayRouteInfo(id); } }
MyDisplayRouteInfo jest dostarczany przez pakiet NuGet Rick.Docs.Samples.RouteInfo i wyświetla informacje o trasie.
/Products/Details/5
model wiąże wartość parametruid = 5
z wartościąid
, aby ustawić parametr na5
. Aby uzyskać więcej informacji, zobacz Powiązanie modelu.{controller=Home}
definiujeHome
jako wartość domyślnącontroller
.{action=Index}
definiujeIndex
jako wartość domyślnąaction
.Znak
?
w pliku{id?}
definiujeid
się jako opcjonalny.- Domyślne i opcjonalne parametry trasy nie muszą być obecne w ścieżce adresu URL dla dopasowania. Zobacz Dokumentacja szablonu trasy, aby uzyskać szczegółowy opis składni szablonu trasy.
Pasuje do ścieżki
/
adresu URL .Tworzy wartości
{ controller = Home, action = Index }
tras .
Wartości i controller
action
używają wartości domyślnych. id
nie generuje wartości, ponieważ w ścieżce adresu URL nie ma odpowiedniego segmentu. /
odpowiada tylko wtedy, gdy istnieje akcja HomeController
i Index
:
public class HomeController : Controller
{
public IActionResult Index() { ... }
}
Korzystając z powyższej definicji kontrolera i szablonu trasy, HomeController.Index
akcja jest uruchamiana dla następujących ścieżek URL:
/Home/Index/17
/Home/Index
/Home
/
Ścieżka adresu URL /
używa domyślnych Home
kontrolerów i Index
akcji szablonu trasy. Ścieżka adresu URL /Home
używa domyślnej Index
akcji szablonu trasy.
Metoda MapDefaultControllerRoutewygody:
app.MapDefaultControllerRoute();
Zastępuje:
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Ważne
Routing jest konfigurowany przy użyciu oprogramowania pośredniczącego UseRouting i UseEndpoints . Aby użyć kontrolerów:
- Wywołaj metodę MapControllers mapowania atrybutów kierowanych kontrolerów.
- Wywołaj MapControllerRoute metodę lub MapAreaControllerRoute, aby mapować zarówno konwencjonalne kontrolery trasowane, jak i atrybuty kierowane kontrolery.
Aplikacje zazwyczaj nie muszą wywoływać ani UseRouting
UseEndpoints
. WebApplicationBuilder Konfiguruje potok oprogramowania pośredniczącego, który opakowuje oprogramowanie pośredniczące dodane za Program.cs
pomocą poleceń UseRouting
i UseEndpoints
. Aby uzyskać więcej informacji, zobacz Routing na platformie ASP.NET Core.
Routing konwencjonalny
Routing konwencjonalny jest używany z kontrolerami i widokami. Trasa default
:
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Powyższy jest przykładem konwencjonalnej trasy. Jest to nazywane routingiem konwencjonalnym, ponieważ ustanawia konwencję dla ścieżek adresów URL:
- Pierwszy segment ścieżki,
{controller=Home}
, mapuje na nazwę kontrolera. - Drugi segment,
{action=Index}
, mapuje na nazwę akcji . - Trzeci segment
{id?}
jest używany dla opcjonalnegoid
elementu . Element?
in{id?}
sprawia, że jest opcjonalny.id
służy do mapowania na jednostkę modelu.
Korzystając z tej default
trasy, ścieżka adresu URL:
/Products/List
mapuje naProductsController.List
akcję./Blog/Article/17
mapuje naBlogController.Article
i zazwyczaj model wiążeid
parametr z wartością 17.
To mapowanie:
- Jest oparty tylko na kontrolerach i nazwach akcji.
- Nie jest oparta na przestrzeniach nazw, lokalizacjach plików źródłowych ani parametrach metody.
Użycie konwencjonalnego routingu z trasą domyślną umożliwia utworzenie aplikacji bez konieczności tworzenia nowego wzorca adresu URL dla każdej akcji. W przypadku aplikacji z akcjami stylu CRUD spójność adresów URL między kontrolerami:
- Ułatwia uproszczenie kodu.
- Sprawia, że interfejs użytkownika jest bardziej przewidywalny.
Ostrzeżenie
Element id
w poprzednim kodzie jest definiowany jako opcjonalny przez szablon trasy. Akcje mogą być wykonywane bez opcjonalnego identyfikatora podanego jako część adresu URL. Ogólnie rzecz biorąc, w przypadku id
pominięcia z adresu URL:
id
parametr jest ustawiany na0
wartość według powiązania modelu.- W bazie danych pasującej
id == 0
nie można odnaleźć jednostki .
Routing atrybutów zapewnia szczegółową kontrolę, aby identyfikator był wymagany dla niektórych akcji, a nie dla innych. Zgodnie z konwencją dokumentacja zawiera opcjonalne parametry, takie jak id
wtedy, gdy prawdopodobnie pojawią się w poprawnym użyciu.
Większość aplikacji powinna wybrać podstawowy i opisowy schemat routingu, aby adresy URL mogły być czytelne i zrozumiałe. Domyślna trasa konwencjonalna {controller=Home}/{action=Index}/{id?}
:
- Obsługuje podstawowy i opisowy schemat routingu.
- Jest przydatnym punktem wyjścia dla aplikacji opartych na interfejsie użytkownika.
- Jest jedynym szablonem trasy wymaganym dla wielu aplikacji internetowych interfejsu użytkownika. W przypadku większych aplikacji interfejsu użytkownika sieci Web kolejna trasa korzystająca z obszarów jest często wymagana.
MapControllerRoute i MapAreaRoute :
- Automatycznie przypisz wartość zamówienia do swoich punktów końcowych na podstawie kolejności, w której są wywoływane.
Routing punktów końcowych w ASP.NET Core:
- Nie ma pojęcia tras.
- Nie zapewnia gwarancji kolejności wykonywania rozszerzalności, wszystkie punkty końcowe są przetwarzane jednocześnie.
Włącz rejestrowanie , aby zobaczyć, jak wbudowane implementacje routingu, takie jak Route, pasują do żądań.
Routing atrybutów wyjaśniono w dalszej części tego dokumentu.
Wiele konwencjonalnych tras
Wiele konwencjonalnych tras można skonfigurować, dodając więcej wywołań do MapControllerRoute i MapAreaControllerRoute. Umożliwia to zdefiniowanie wielu konwencji lub dodanie konwencjonalnych tras przeznaczonych do określonej akcji, takich jak:
app.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Trasa blog
w poprzednim kodzie jest dedykowaną trasą konwencjonalną. Nazywa się to dedykowaną tradycyjną trasą, ponieważ:
- Korzysta z konwencjonalnego routingu.
- Jest ona przeznaczona dla określonej akcji.
Ponieważ controller
i action
nie są wyświetlane w szablonie "blog/{*article}"
trasy jako parametry:
- Mogą mieć tylko wartości
{ controller = "Blog", action = "Article" }
domyślne . - Ta trasa zawsze mapuje na akcję
BlogController.Article
.
/Blog
, /Blog/Article
i /Blog/{any-string}
są jedynymi ścieżkami adresów URL pasujących do trasy blogu.
Powyższy przykład:
blog
trasa ma wyższy priorytet dla dopasowań niżdefault
trasa, ponieważ jest dodawana jako pierwsza.- To przykład routingu stylu Slug , w którym zazwyczaj jest używana nazwa artykułu jako część adresu URL.
Ostrzeżenie
W ASP.NET Core routing nie:
- Definiowanie koncepcji nazywanej trasą.
UseRouting
Dodaje trasę zgodną z potokiem oprogramowania pośredniczącego. OprogramowanieUseRouting
pośredniczące analizuje zestaw punktów końcowych zdefiniowanych w aplikacji i wybiera najlepsze dopasowanie punktu końcowego na podstawie żądania. - Zapewnianie gwarancji dotyczących kolejności wykonywania rozszerzalności, takich jak IRouteConstraint lub IActionConstraint.
Zobacz Routing , aby uzyskać informacje referencyjne dotyczące routingu.
Tradycyjna kolejność routingu
Routing konwencjonalny pasuje tylko do kombinacji akcji i kontrolera, które są zdefiniowane przez aplikację. Ma to na celu uproszczenie przypadków, w których nakładają się konwencjonalne trasy.
Dodawanie tras przy użyciu metody MapControllerRoute, MapDefaultControllerRoutei MapAreaControllerRoute automatyczne przypisywanie wartości zamówienia do ich punktów końcowych na podstawie kolejności, w której są wywoływane. Dopasowania z wyświetlonej wcześniej trasy mają wyższy priorytet. Routing konwencjonalny jest zależny od kolejności. Ogólnie rzecz biorąc, trasy z obszarami powinny być umieszczane wcześniej, ponieważ są bardziej szczegółowe niż trasy bez obszaru. Dedykowane trasy konwencjonalne z parametrami trasy typu catch-all, takie jak {*article}
mogą sprawić, że trasa będzie zbyt chciwa, co oznacza, że pasuje do adresów URL, które mają być dopasowane przez inne trasy. Umieść chciwe trasy później w tabeli tras, aby zapobiec chciwym dopasowaniom.
Ostrzeżenie
Parametr catch-all może być niepoprawnie zgodny z trasami z powodu błędu w routingu. Aplikacje, których dotyczy ta usterka, mają następujące cechy:
- Trasa typu catch-all, na przykład
{**slug}"
- Trasa catch-all nie pasuje do żądań, które powinna być zgodna.
- Usunięcie innych tras sprawia, że trasa catch-all zacznie działać.
Zobacz Usterki usługi GitHub 18677 i 16579 , na przykład przypadki, w których wystąpiła ta usterka.
Poprawka zgody na tę usterkę jest zawarta w zestawie .NET Core 3.1.301 SDK i nowszych wersjach. Poniższy kod ustawia przełącznik wewnętrzny, który naprawia tę usterkę:
public static void Main(string[] args)
{
AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior",
true);
CreateHostBuilder(args).Build().Run();
}
// Remaining code removed for brevity.
Rozwiązywanie niejednoznacznych akcji
Gdy dwa punkty końcowe są zgodne z routingiem, routing musi wykonać jedną z następujących czynności:
- Wybierz najlepszego kandydata.
- Zgłaszanie wyjątku.
Na przykład:
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);
}
}
Poprzedni kontroler definiuje dwie akcje zgodne z następującymi akcjami:
- Ścieżka adresu URL
/Products33/Edit/17
- Kierowanie danych
{ controller = Products33, action = Edit, id = 17 }
.
Jest to typowy wzorzec dla kontrolerów MVC:
Edit(int)
wyświetla formularz do edycji produktu.Edit(int, Product)
przetwarza opublikowany formularz.
Aby rozwiązać prawidłową trasę:
Edit(int, Product)
jest wybierany, gdy żądanie jest httpPOST
.Edit(int)
jest wybierany, gdy czasownik HTTP jest czymkolwiek innym.Edit(int)
jest zwykle wywoływana za pośrednictwem .GET
[HttpPost]
Element HttpPostAttribute, jest dostarczany do routingu, aby można było wybrać go na podstawie metody HTTP żądania. Funkcja HttpPostAttribute
sprawia, że Edit(int, Product)
lepsze dopasowanie niż Edit(int)
.
Ważne jest, aby zrozumieć rolę atrybutów, takich jak HttpPostAttribute
. Podobne atrybuty są definiowane dla innych czasowników HTTP. W przypadku routingu konwencjonalnego akcje używają tej samej nazwy akcji, gdy są częścią formularza pokazu, przesyłaj przepływ pracy formularza. Na przykład zobacz Badanie dwóch metod akcji Edytuj.
Jeśli routing nie może wybrać najlepszego kandydata, AmbiguousMatchException zostanie zgłoszony element z listą wielu pasowanych punktów końcowych.
Konwencjonalne nazwy tras
Ciągi i "default"
w następujących przykładach "blog"
to konwencjonalne nazwy tras:
app.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Nazwy tras dają trasie nazwę logiczną. Nazwana trasa może służyć do generowania adresów URL. Użycie nazwanej trasy upraszcza tworzenie adresów URL, gdy kolejność tras może spowodować, że generowanie adresów URL jest skomplikowane. Nazwy tras muszą być unikatowe dla całej aplikacji.
Nazwy tras:
- Nie ma wpływu na dopasowywanie adresów URL ani obsługę żądań.
- Są używane tylko do generowania adresów URL.
Koncepcja nazwy trasy jest reprezentowana w routingu jako IEndpointNameMetadata. Nazwa trasy i nazwa punktu końcowego terminów:
- Są zamienne.
- Który z nich jest używany w dokumentacji i kodzie, zależy od opisanego interfejsu API.
Routing atrybutów dla REST interfejsów API
REST Interfejsy API powinny używać routingu atrybutów do modelowania funkcjonalności aplikacji jako zestawu zasobów, w których operacje są reprezentowane przez czasowniki HTTP.
Routing atrybutów używa zestawu atrybutów do mapowania akcji bezpośrednio na szablony tras. Poniższy kod jest typowy dla interfejsu REST API i jest używany w następnym przykładzie:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
W poprzednim kodzie jest wywoływana do MapControllers mapowania atrybutów kierowanych kontrolerów.
W poniższym przykładzie:
HomeController
pasuje do zestawu adresów URL podobnych do domyślnej konwencjonalnej trasy{controller=Home}/{action=Index}/{id?}
.
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);
}
}
Akcja HomeController.Index
jest uruchamiana dla dowolnej ścieżki /
adresu URL , /Home
, /Home/Index
lub /Home/Index/3
.
W tym przykładzie wyróżniono kluczową różnicę programową między routingiem atrybutów a routingiem konwencjonalnym. Routing atrybutów wymaga większej ilości danych wejściowych w celu określenia trasy. Tradycyjna trasa domyślna obsługuje trasy bardziej zwięźle. Jednak routing atrybutów zezwala i wymaga dokładnej kontroli, które szablony tras mają zastosowanie do każdej akcji.
W przypadku routingu atrybutów nazwy kontrolera i akcji nie odgrywają żadnej roli, w której akcja jest dopasowywana, chyba że jest używana zamiana tokenu. Poniższy przykład jest zgodny z tymi samymi adresami URL co w poprzednim przykładzie:
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);
}
}
Poniższy kod używa zamiany tokenu dla action
elementów i 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();
}
}
Następujący kod ma zastosowanie [Route("[controller]/[action]")]
do kontrolera:
[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();
}
}
W poprzednim kodzie Index
szablony metod muszą poprzedzać /
szablony tras lub ~/
do nich. Szablony tras stosowane do akcji rozpoczynającej się /
od lub ~/
nie są łączone z szablonami tras zastosowanymi do kontrolera.
Zobacz Pierwszeństwo szablonu trasy, aby uzyskać informacje na temat wyboru szablonu trasy.
Nazwy routingu zarezerwowanego
Następujące słowa kluczowe to zastrzeżone nazwy parametrów tras podczas korzystania z kontrolerów lub Razor stron:
action
area
controller
handler
page
Używanie page
jako parametru trasy z routingiem atrybutów jest typowym błędem. Powoduje to niespójne i mylące zachowanie podczas generowania adresów URL.
public class MyDemo2Controller : Controller
{
[Route("/articles/{page}")]
public IActionResult ListArticles(int page)
{
return ControllerContext.MyDisplayRouteInfo(page);
}
}
Nazwy parametrów specjalnych są używane przez generowanie adresów URL w celu określenia, czy operacja generowania adresu URL odnosi się do Razor strony lub kontrolera.
Następujące słowa kluczowe są zastrzeżone w kontekście Razor widoku lub Razor strony:
page
using
namespace
inject
section
inherits
model
addTagHelper
removeTagHelper
Te słowa kluczowe nie powinny być używane w przypadku generowania linków, parametrów powiązanych z modelem ani właściwości najwyższego poziomu.
Szablony czasowników HTTP
ASP.NET Core ma następujące szablony czasowników HTTP:
Szablony tras
ASP.NET Core ma następujące szablony tras:
Routing atrybutów z atrybutami czasownika Http
Rozważmy następujący kontroler:
[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);
}
}
Powyższy kod:
- Każda akcja zawiera
[HttpGet]
atrybut, który ogranicza dopasowanie tylko do żądań HTTP GET. - Akcja
GetProduct
zawiera"{id}"
szablon, dlategoid
jest dołączana do"api/[controller]"
szablonu na kontrolerze. Szablon metod to"api/[controller]/{id}"
. W związku z tym ta akcja odpowiada tylko żądaniom GET dla formularza/api/test2/xyz
,/api/test2/123
/api/test2/{any string}
itp.[HttpGet("{id}")] // GET /api/test2/xyz public IActionResult GetProduct(string id) { return ControllerContext.MyDisplayRouteInfo(id); }
- Akcja
GetIntProduct
zawiera"int/{id:int}"
szablon. Część:int
szablonu ograniczaid
wartości trasy do ciągów, które można przekonwertować na liczbę całkowitą. Żądanie GET do/api/test2/int/abc
:- Nie pasuje do tej akcji.
- Zwraca błąd 404 Nie znaleziono.
[HttpGet("int/{id:int}")] // GET /api/test2/int/3 public IActionResult GetIntProduct(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
- Akcja
GetInt2Product
zawiera{id}
element w szablonie, ale nie ograniczaid
się do wartości, które można przekonwertować na liczbę całkowitą. Żądanie GET do/api/test2/int2/abc
:- Pasuje do tej trasy.
- Nie można przekonwertować
abc
powiązania modelu na liczbę całkowitą. Parametrid
metody to liczba całkowita. - Zwraca nieprawidłowe żądanie 400, ponieważ nie można przekonwertować
abc
powiązania modelu na liczbę całkowitą.[HttpGet("int2/{id}")] // GET /api/test2/int2/3 public IActionResult GetInt2Product(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
Routing atrybutów może używać HttpMethodAttribute atrybutów, takich jak HttpPostAttribute, HttpPutAttributei HttpDeleteAttribute. Wszystkie atrybuty czasownika HTTP akceptują szablon trasy. W poniższym przykładzie przedstawiono dwie akcje pasujące do tego samego szablonu trasy:
[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);
}
}
Za pomocą ścieżki /products3
adresu URL:
- Akcja
MyProductsController.ListProducts
jest uruchamiana, gdy czasownik HTTP toGET
. - Akcja
MyProductsController.CreateProduct
jest uruchamiana, gdy czasownik HTTP toPOST
.
Podczas tworzenia interfejsu REST API rzadko trzeba użyć [Route(...)]
metody akcji, ponieważ akcja akceptuje wszystkie metody HTTP. Lepiej użyć bardziej szczegółowego atrybutu czasownika HTTP, aby dokładnie określić, co obsługuje interfejs API. Oczekuje się, że klienci interfejsów REST API będą wiedzieć, jakie ścieżki i czasowniki HTTP mapują na określone operacje logiczne.
REST Interfejsy API powinny używać routingu atrybutów do modelowania funkcjonalności aplikacji jako zestawu zasobów, w których operacje są reprezentowane przez czasowniki HTTP. Oznacza to, że wiele operacji, na przykład GET i POST w tym samym zasobie logicznym, używa tego samego adresu URL. Routing atrybutów zapewnia poziom kontroli, który jest potrzebny do dokładnego zaprojektowania publicznego układu punktu końcowego interfejsu API.
Ponieważ trasa atrybutu ma zastosowanie do określonej akcji, łatwo jest ustawić parametry wymagane w ramach definicji szablonu trasy. W poniższym przykładzie id
jest wymagany jako część ścieżki adresu URL:
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Akcja Products2ApiController.GetProduct(int)
:
- Jest uruchamiany ze ścieżką adresu URL, na przykład
/products2/3
- Nie jest uruchamiany przy użyciu ścieżki
/products2
adresu URL .
Atrybut [Consumes] umożliwia akcji ograniczenie obsługiwanych typów zawartości żądania. Aby uzyskać więcej informacji, zobacz Definiowanie obsługiwanych typów zawartości żądań za pomocą atrybutu Consumes.
Zobacz Routing , aby uzyskać pełny opis szablonów tras i powiązanych opcji.
Aby uzyskać więcej informacji na temat [ApiController]
programu , zobacz Atrybut ApiController.
Nazwa trasy
Poniższy kod definiuje nazwę trasy :Products_List
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Nazwy tras mogą służyć do generowania adresu URL na podstawie określonej trasy. Nazwy tras:
- Nie ma wpływu na zachowanie dopasowywania adresów URL routingu.
- Są używane tylko do generowania adresów URL.
Nazwy tras muszą być unikatowe dla całej aplikacji.
Porównaj powyższy kod z tradycyjną trasą domyślną, która definiuje id
parametr jako opcjonalny ({id?}
). Możliwość dokładnego określenia interfejsów API ma zalety, takie jak umożliwienie /products
i /products/5
wysłanie ich do różnych akcji.
Łączenie tras atrybutów
Aby routing atrybutów był mniej powtarzalny, atrybuty trasy na kontrolerze są łączone z atrybutami trasy w poszczególnych akcjach. Wszystkie szablony tras zdefiniowane na kontrolerze są dołączane do szablonów tras w akcjach. Umieszczenie atrybutu trasy na kontrolerze sprawia, że wszystkie akcje w kontrolerze używają routingu atrybutów.
[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);
}
}
W powyższym przykładzie:
- Ścieżka
/products
adresu URL może być zgodnaProductsApi.ListProducts
- Ścieżka
/products/5
adresu URL może być zgodna zProductsApi.GetProduct(int)
.
Obie te akcje są zgodne tylko z protokołem HTTP GET
, ponieważ są one oznaczone atrybutem [HttpGet]
.
Szablony tras stosowane do akcji rozpoczynającej się /
od lub ~/
nie są łączone z szablonami tras zastosowanymi do kontrolera. Poniższy przykład pasuje do zestawu ścieżek URL podobnych do trasy domyślnej.
[Route("Home")]
public class HomeController : Controller
{
[Route("")]
[Route("Index")]
[Route("/")]
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
[Route("About")]
public IActionResult About()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
W poniższej tabeli opisano [Route]
atrybuty w poprzednim kodzie:
Atrybut | Łączy się z [Route("Home")] |
Definiuje szablon trasy |
---|---|---|
[Route("")] |
Tak | "Home" |
[Route("Index")] |
Tak | "Home/Index" |
[Route("/")] |
Nie | "" |
[Route("About")] |
Tak | "Home/About" |
Kolejność tras atrybutów
Routing tworzy drzewo i pasuje do wszystkich punktów końcowych jednocześnie:
- Wpisy trasy zachowują się tak, jakby zostały umieszczone w idealnej kolejności.
- Najbardziej konkretne trasy mają szansę wykonać przed bardziej ogólnymi trasami.
Na przykład trasa atrybutu, na blog/search/{topic}
przykład, jest bardziej specyficzna niż trasa atrybutu, na przykład blog/{*article}
. Trasa blog/search/{topic}
ma domyślnie wyższy priorytet, ponieważ jest bardziej szczegółowa. Korzystając z routingu konwencjonalnego, deweloper jest odpowiedzialny za umieszczanie tras w żądanej kolejności.
Trasy atrybutów mogą konfigurować kolejność przy użyciu Order właściwości . Wszystkie udostępnione atrybuty Order
trasy obejmują . Trasy są przetwarzane zgodnie z rosnącą właściwością Order
. Domyślna kolejność to 0
. Ustawianie trasy przy użyciu Order = -1
przebiegów przed trasami, które nie ustawiają kolejności. Ustawianie trasy przy użyciu Order = 1
przebiegów po domyślnym porządkowaniu tras.
Unikaj w zależności od Order
. Jeśli miejsce w adresie URL aplikacji wymaga jawnych wartości zamówień w celu poprawnego kierowania, prawdopodobnie jest to również mylące dla klientów. Ogólnie rzecz biorąc, routing atrybutów wybiera poprawną trasę z dopasowaniem adresu URL. Jeśli domyślna kolejność używana na potrzeby generowania adresów URL nie działa, użycie nazwy trasy jako zastąpienia jest zwykle prostsze niż zastosowanie Order
właściwości.
Rozważmy dwa następujące kontrolery, które definiują dopasowanie /home
trasy:
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);
}
}
/home
Żądanie z powyższym kodem zgłasza wyjątek podobny do następującego:
AmbiguousMatchException: The request matched multiple endpoints. Matches:
WebMvcRouting.Controllers.HomeController.Index
WebMvcRouting.Controllers.MyDemoController.MyIndex
Dodanie Order
do jednego z atrybutów trasy rozwiązuje niejednoznaczność:
[Route("")]
[Route("Home", Order = 2)]
[Route("Home/MyIndex")]
public IActionResult MyIndex()
{
return ControllerContext.MyDisplayRouteInfo();
}
W poprzednim kodzie /home
uruchamia HomeController.Index
punkt końcowy. Aby uzyskać dostęp do MyDemoController.MyIndex
elementu , żądanie /home/MyIndex
. Uwaga:
- Powyższy kod jest przykładem lub słabym projektem routingu. Użyto go do zilustrowania
Order
właściwości. - Właściwość
Order
rozwiązuje tylko niejednoznaczność, że nie można dopasować tego szablonu. Lepiej byłoby usunąć[Route("Home")]
szablon.
Zobacz Razor Konwencje tras stron i aplikacji: Kolejność tras, aby uzyskać informacje na temat kolejności tras za pomocą Razor stron.
W niektórych przypadkach zwracany jest błąd HTTP 500 z niejednoznacznymi trasami. Użyj rejestrowania , aby zobaczyć, które punkty końcowe spowodowały AmbiguousMatchException
.
Zamiana tokenu w szablonach tras [kontroler], [action], [area]
Dla wygody trasy atrybutów obsługują zamianę tokenu przez dołączenie tokenu w nawiasy kwadratowe ([
, ]
). Tokeny [action]
, [area]
i [controller]
są zastępowane wartościami nazwy akcji, nazwy obszaru i nazwy kontrolera z akcji, w której zdefiniowano trasę:
[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);
}
}
Powyższy kod:
[HttpGet]
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
- Pasuje
/Products0/List
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
- Pasuje
/Products0/Edit/{id}
Zastąpienie tokenu odbywa się w ostatnim kroku tworzenia tras atrybutów. Powyższy przykład zachowuje się tak samo jak w poniższym kodzie:
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);
}
}
Jeśli czytasz to w języku innym niż angielski, daj nam znać w tym problemie z dyskusją w usłudze GitHub, jeśli chcesz zobaczyć komentarze kodu w języku natywnym.
Trasy atrybutów można również łączyć z dziedziczeniem. Jest to zaawansowane połączenie z zastąpieniem tokenu. Zamiana tokenu ma również zastosowanie do nazw tras zdefiniowanych przez trasy atrybutów.
[Route("[controller]/[action]", Name="[controller]_[action]")]
generuje unikatową nazwę trasy dla każdej akcji:
[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);
}
}
Aby dopasować ogranicznik [
zastępczy tokenu literału lub ]
, należy go uruchomić, powtarzając znak ([[
lub ]]
).
Dostosowywanie zamiany tokenu za pomocą funkcji przekształcania parametrów
Zamiana tokenu można dostosować przy użyciu funkcji przekształcania parametrów. Transformator parametrów implementuje IOutboundParameterTransformer i przekształca wartość parametrów. Na przykład funkcja przekształcania parametrów niestandardowych SlugifyParameterTransformer
zmienia SubscriptionManagement
wartość trasy na 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();
}
}
Jest RouteTokenTransformerConvention to konwencja modelu aplikacji, która:
- Stosuje transformator parametru do wszystkich tras atrybutów w aplikacji.
- Dostosowuje wartości tokenu trasy atrybutu podczas ich zastępowania.
public class SubscriptionManagementController : Controller
{
[HttpGet("[controller]/[action]")]
public IActionResult ListAll()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Poprzednia metoda odpowiada /subscription-management/list-all
metodzie ListAll
.
Element RouteTokenTransformerConvention
jest zarejestrowany jako opcja:
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();
Aby uzyskać definicję usługi Slug, zobacz dokumentację internetową usługi MDN w usłudze Slug .
Ostrzeżenie
W przypadku używania System.Text.RegularExpressions metody do przetwarzania niezaufanych danych wejściowych należy przekazać limit czasu. Złośliwy użytkownik może podać dane wejściowe, aby spowodować RegularExpressions
atak typu "odmowa usługi". ASP.NET podstawowe interfejsy API platformy, które używają RegularExpressions
przekroczenia limitu czasu.
Wiele tras atrybutów
Routing atrybutów obsługuje definiowanie wielu tras, które docierają do tej samej akcji. Najczęstszym zastosowaniem tej metody jest naśladowanie zachowania domyślnej konwencjonalnej trasy, jak pokazano w poniższym przykładzie:
[Route("[controller]")]
public class Products13Controller : Controller
{
[Route("")] // Matches 'Products13'
[Route("Index")] // Matches 'Products13/Index'
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
Umieszczenie wielu atrybutów trasy na kontrolerze oznacza, że każdy z nich łączy się z każdym z atrybutów trasy w metodach akcji:
[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();
}
}
Wszystkie ograniczenia trasy czasownika HTTP implementują IActionConstraint
.
Gdy wiele IActionConstraint atrybutów trasy implementujących jest umieszczanych w akcji:
- Każde ograniczenie akcji łączy się z szablonem trasy zastosowanym do kontrolera.
[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();
}
}
Używanie wielu tras w akcjach może wydawać się przydatne i zaawansowane, lepiej zachować podstawową i dobrze zdefiniowaną przestrzeń adresów URL aplikacji. Użyj wielu tras w akcjach tylko w razie potrzeby, aby obsługiwać istniejących klientów.
Określanie opcjonalnych parametrów trasy atrybutu, wartości domyślnych i ograniczeń
Trasy atrybutów obsługują tę samą składnię śródliniową co konwencjonalne trasy w celu określenia opcjonalnych parametrów, wartości domyślnych i ograniczeń.
public class Products14Controller : Controller
{
[HttpPost("product14/{id:int}")]
public IActionResult ShowProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
W poprzednim kodzie [HttpPost("product14/{id:int}")]
stosuje ograniczenie trasy. Akcja Products14Controller.ShowProduct
jest dopasowywana tylko przez ścieżki adresu URL, takie jak /product14/3
. Część szablonu {id:int}
trasy ogranicza ten segment tylko do liczb całkowitych.
Zobacz Dokumentacja szablonu trasy, aby uzyskać szczegółowy opis składni szablonu trasy.
Niestandardowe atrybuty trasy przy użyciu elementu IRouteTemplateProvider
Wszystkie atrybuty trasy implementują IRouteTemplateProviderelement . Środowisko uruchomieniowe ASP.NET Core:
- Szuka atrybutów w klasach kontrolerów i metodach akcji podczas uruchamiania aplikacji.
- Używa atrybutów implementujących
IRouteTemplateProvider
do tworzenia początkowego zestawu tras.
Zaimplementuj IRouteTemplateProvider
, aby zdefiniować niestandardowe atrybuty trasy. Każda IRouteTemplateProvider
z nich umożliwia zdefiniowanie pojedynczej trasy przy użyciu niestandardowego szablonu trasy, kolejności i nazwy:
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();
}
}
Poprzednia Get
metoda zwraca wartość Order = 2, Template = api/MyTestApi
.
Dostosowywanie tras atrybutów za pomocą modelu aplikacji
Model aplikacji:
- Jest modelem obiektów utworzonym podczas uruchamiania w programie
Program.cs
. - Zawiera wszystkie metadane używane przez ASP.NET Core do kierowania i wykonywania akcji w aplikacji.
Model aplikacji zawiera wszystkie dane zebrane z atrybutów trasy. Dane z atrybutów trasy są dostarczane przez implementację IRouteTemplateProvider
. Konwencje:
- Można zapisać w celu zmodyfikowania modelu aplikacji, aby dostosować sposób działania routingu.
- Są odczytywane podczas uruchamiania aplikacji.
W tej sekcji przedstawiono podstawowy przykład dostosowywania routingu przy użyciu modelu aplikacji. Poniższy kod sprawia, że trasy są w przybliżeniu zgodne ze strukturą folderów projektu.
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()
};
}
}
}
Poniższy kod uniemożliwia namespace
zastosowanie konwencji do kontrolerów, które są kierowane atrybutami:
public void Apply(ControllerModel controller)
{
var hasRouteAttributes = controller.Selectors.Any(selector =>
selector.AttributeRouteModel != null);
if (hasRouteAttributes)
{
return;
}
Na przykład następujący kontroler nie używa polecenia 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}");
}
}
Metoda NamespaceRoutingConvention.Apply
:
- Nic nie robi, jeśli kontroler jest kierowany atrybut.
- Ustawia szablon kontrolerów na
namespace
podstawie elementu z usuniętą baząnamespace
.
Element NamespaceRoutingConvention
można zastosować w pliku 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();
Rozważmy na przykład następujący kontroler:
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}");
}
}
}
Powyższy kod:
- Podstawą
namespace
jestMy.Application
. - Pełna nazwa poprzedniego kontrolera to
My.Application.Admin.Controllers.UsersController
. - Parametr
NamespaceRoutingConvention
ustawia szablon kontrolerów naAdmin/Controllers/Users/[action]/{id?
wartość .
Można NamespaceRoutingConvention
go również zastosować jako atrybut na kontrolerze:
[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 mieszany: routing atrybutów a routing konwencjonalny
aplikacje ASP.NET Core mogą łączyć użycie konwencjonalnego routingu i routingu atrybutów. Typowe jest używanie konwencjonalnych tras dla kontrolerów obsługujących strony HTML dla przeglądarek i routing atrybutów dla kontrolerów obsługujących REST interfejsy API.
Akcje są zwykle kierowane lub kierowane atrybuty. Umieszczenie trasy na kontrolerze lub akcji sprawia, że atrybut jest kierowany. Akcje definiujące trasy atrybutów nie mogą być osiągane za pośrednictwem konwencjonalnych tras i odwrotnie. Każdy atrybut trasy na kontrolerze sprawia, że wszystkie akcje w atrybucie kontrolera są kierowane.
Routing atrybutów i routing konwencjonalny używają tego samego aparatu routingu.
Routing z znakami specjalnymi
Routing ze znakami specjalnymi może prowadzić do nieoczekiwanych wyników. Rozważmy na przykład kontroler z następującą metodą akcji:
[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;
}
Jeśli string id
zawiera następujące zakodowane wartości, mogą wystąpić nieoczekiwane wyniki:
ASCII | Encoded |
---|---|
/ |
%2F |
|
+ |
Parametry trasy nie zawsze są dekodowane za pomocą adresu URL. Ten problem może zostać rozwiązany w przyszłości. Aby uzyskać więcej informacji, zobacz ten problem z usługą GitHub;
Generowanie adresów URL i wartości otoczenia
Aplikacje mogą używać funkcji generowania adresów URL routingu do generowania linków adresów URL do akcji. Generowanie adresów URL eliminuje stałe adresy URL kodowania , dzięki czemu kod jest bardziej niezawodny i możliwy do utrzymania. Ta sekcja koncentruje się na funkcjach generowania adresów URL udostępnianych przez mvC i omówiono tylko podstawowe informacje na temat sposobu działania generowania adresów URL. Zobacz Routing , aby uzyskać szczegółowy opis generowania adresu URL.
Interfejs IUrlHelper jest podstawowym elementem infrastruktury między MVC i routingiem na potrzeby generowania adresów URL. Wystąpienie IUrlHelper
klasy jest dostępne za pośrednictwem Url
właściwości w kontrolerach, widokach i składnikach widoku.
W poniższym przykładzie IUrlHelper
interfejs jest używany za pośrednictwem Controller.Url
właściwości w celu wygenerowania adresu URL do innej akcji.
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();
}
}
Jeśli aplikacja używa domyślnej konwencjonalnej trasy, wartość url
zmiennej to ciąg /UrlGeneration/Destination
ścieżki adresu URL . Ta ścieżka adresu URL jest tworzona przez routing przez połączenie:
- Wartości trasy z bieżącego żądania, które są nazywane wartościami otoczenia.
- Wartości przekazane do
Url.Action
i podstawiając te wartości do szablonu trasy:
ambient values: { controller = "UrlGeneration", action = "Source" }
values passed to Url.Action: { controller = "UrlGeneration", action = "Destination" }
route template: {controller}/{action}/{id?}
result: /UrlGeneration/Destination
Każdy parametr trasy w szablonie trasy ma swoją wartość podstawioną przez dopasowanie nazw wartościami i wartościami otoczenia. Parametr trasy, który nie ma wartości, może:
- Użyj wartości domyślnej, jeśli ma ją.
- Jeśli jest to opcjonalne, należy pominąć. Na przykład element
id
z szablonu{controller}/{action}/{id?}
trasy .
Generowanie adresu URL kończy się niepowodzeniem, jeśli jakikolwiek wymagany parametr trasy nie ma odpowiedniej wartości. Jeśli generowanie adresu URL zakończy się niepowodzeniem dla trasy, kolejna trasa zostanie podjęta do momentu wypróbowanej wszystkich tras lub znalezienia dopasowania.
W poprzednim przykładzie przyjęto Url.Action
założenie, że routing konwencjonalny. Generowanie adresów URL działa podobnie z routingiem atrybutów, chociaż koncepcje są różne. Z konwencjonalnym routingiem:
- Wartości tras służą do rozwijania szablonu.
- Wartości tras dla
controller
iaction
zwykle są wyświetlane w tym szablonie. Działa to, ponieważ adresy URL dopasowane przez routing są zgodne z konwencją.
W poniższym przykładzie użyto routingu atrybutów:
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();
}
}
Akcja Source
w poprzednim kodzie generuje custom/url/to/destination
element .
LinkGenerator dodano w ASP.NET Core 3.0 jako alternatywę dla IUrlHelper
. LinkGenerator
oferuje podobne, ale bardziej elastyczne funkcje. Każda metoda w systemie IUrlHelper
ma również odpowiednią rodzinę metod LinkGenerator
.
Generowanie adresów URL według nazwy akcji
Url.Action, LinkGenerator.GetPathByAction i wszystkie powiązane przeciążenia są przeznaczone do generowania docelowego punktu końcowego przez określenie nazwy kontrolera i nazwy akcji.
W przypadku używania parametru Url.Action
bieżące wartości tras i controller
action
są udostępniane przez środowisko uruchomieniowe:
- Wartość
controller
iaction
są częścią wartości otoczenia i wartości. MetodaUrl.Action
zawsze używa bieżącychaction
wartości icontroller
generuje ścieżkę adresu URL, która kieruje do bieżącej akcji.
Routing próbuje użyć wartości w wartościach otoczenia, aby wypełnić informacje, które nie zostały podane podczas generowania adresu URL. Rozważ trasę podobną {a}/{b}/{c}/{d}
do wartości otoczenia { a = Alice, b = Bob, c = Carol, d = David }
:
- Routing ma wystarczającą ilość informacji, aby wygenerować adres URL bez żadnych dodatkowych wartości.
- Routing ma wystarczającą ilość informacji, ponieważ wszystkie parametry trasy mają wartość.
Jeśli wartość { d = Donovan }
zostanie dodana:
- Wartość
{ d = David }
jest ignorowana. - Wygenerowana ścieżka adresu URL to
Alice/Bob/Carol/Donovan
.
Ostrzeżenie: ścieżki adresów URL są hierarchiczne. W poprzednim przykładzie, jeśli wartość { c = Cheryl }
jest dodawana:
- Obie wartości
{ c = Carol, d = David }
są ignorowane. - Generowanie adresu URL nie jest już wartością
d
, a generowanie adresu URL kończy się niepowodzeniem. - Wymagane wartości
c
id
należy określić, aby wygenerować adres URL.
Możesz spodziewać się wystąpienia tego problemu z trasą {controller}/{action}/{id?}
domyślną . Ten problem jest rzadki w praktyce, ponieważ Url.Action
zawsze jawnie określa wartość controller
i action
.
Kilka przeciążeń adresu URL.Action wykonuje obiekt wartości trasy, aby podać wartości parametrów trasy innych niż controller
i action
. Obiekt wartości trasy jest często używany z elementem id
. Na przykład Url.Action("Buy", "Products", new { id = 17 })
. Obiekt wartości trasy:
- Zgodnie z konwencją jest zwykle obiektem typu anonimowego.
- Może to być
IDictionary<>
element lub POCO).
Wszelkie dodatkowe wartości tras, które nie pasują do parametrów trasy, są umieszczane w ciągu zapytania.
public IActionResult Index()
{
var url = Url.Action("Buy", "Products", new { id = 17, color = "red" });
return Content(url!);
}
Powyższy kod generuje /Products/Buy/17?color=red
element .
Poniższy kod generuje bezwzględny adres URL:
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!);
}
Aby utworzyć bezwzględny adres URL, użyj jednego z następujących elementów:
- Przeciążenie, które akceptuje element
protocol
. Na przykład powyższy kod. - LinkGenerator.GetUriByAction, który domyślnie generuje bezwzględne identyfikatory URI.
Generowanie adresów URL według trasy
Powyższy kod demonstrował generowanie adresu URL przez przekazanie kontrolera i nazwy akcji. IUrlHelper
Udostępnia również rodzinę metod Url.RouteUrl . Te metody są podobne do url.Action, ale nie kopiują bieżących action
wartości i controller
do wartości trasy. Najbardziej typowe użycie elementu Url.RouteUrl
:
- Określa nazwę trasy do wygenerowania adresu URL.
- Ogólnie rzecz biorąc, nie określa kontrolera ani nazwy akcji.
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();
}
Razor Poniższy plik generuje link HTML do pliku Destination_Route
:
<h1>Test Links</h1>
<ul>
<li><a href="@Url.RouteUrl("Destination_Route")">Test Destination_Route</a></li>
</ul>
Generowanie adresów URL w kodzie HTML i Razor
IHtmlHelperudostępnia metody Html.BeginForm i Html.ActionLink do generowania <form>
elementów i <a>
.HtmlHelper Te metody używają metody Url.Action do wygenerowania adresu URL i akceptują podobne argumenty. Są Url.RouteUrl
one obsługiwane HtmlHelper
Html.BeginRouteForm
i Html.RouteLink
które mają podobne funkcje.
TagHelpers generują adresy URL za pośrednictwem form
klasy TagHelper i <a>
TagHelper. Oba te zastosowania są używane IUrlHelper
do ich implementacji. Aby uzyskać więcej informacji, zobacz Pomocnicy tagów w formularzach .
Wewnątrz widoków IUrlHelper
właściwość jest dostępna za pośrednictwem Url
właściwości dla dowolnego generowania adresów URL ad hoc, które nie są objęte powyższymi elementami.
Generowanie adresu URL w wynikach akcji
W poprzednich przykładach pokazano użycie IUrlHelper
w kontrolerze. Najczęstszym użyciem kontrolera jest wygenerowanie adresu URL w ramach wyniku akcji.
Klasy ControllerBase i Controller zapewniają wygodne metody dla wyników akcji odwołujących się do innej akcji. Jednym z typowych użycia jest przekierowanie po zaakceptowaniu danych wejściowych użytkownika:
[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);
}
Akcja powoduje, że metody fabryki, takie jak RedirectToAction i CreatedAtAction , są zgodne ze wzorcem podobnym do metod w systemie IUrlHelper
.
Szczególny przypadek dla dedykowanych tras konwencjonalnych
Routing konwencjonalny może używać specjalnego rodzaju definicji trasy nazywanej dedykowaną trasą konwencjonalną. W poniższym przykładzie trasa o nazwie blog
jest dedykowaną trasą konwencjonalną:
app.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Korzystając z powyższych definicji tras, Url.Action("Index", "Home")
wygeneruje ścieżkę /
adresu URL przy użyciu default
trasy, ale dlaczego? Może się okazać, że wartości { controller = Home, action = Index }
trasy będą wystarczające, aby wygenerować adres URL przy użyciu metody blog
, a wynikiem będzie /blog?action=Index&controller=Home
.
Dedykowane trasy konwencjonalne polegają na specjalnym zachowaniu wartości domyślnych, które nie mają odpowiedniego parametru trasy, który uniemożliwia zbyt chciwość trasy generowania adresów URL. W takim przypadku wartości domyślne to { controller = Blog, action = Article }
, ani ani controller
nie action
są wyświetlane jako parametr trasy. Gdy routing wykonuje generowanie adresów URL, podane wartości muszą być zgodne z wartościami domyślnymi. Generowanie adresu URL przy użyciu metody kończy się niepowodzeniem blog
, ponieważ wartości { controller = Home, action = Index }
nie są zgodne { controller = Blog, action = Article }
z . Następnie routing wraca do próby default
, co powiedzie się.
Obszary
Obszary są funkcją MVC używaną do organizowania powiązanych funkcji w grupie jako oddzielnej:
- Routing przestrzeni nazw dla akcji kontrolera.
- Struktura folderów dla widoków.
Używanie obszarów umożliwia aplikacji posiadanie wielu kontrolerów o tej samej nazwie, o ile mają różne obszary. Użycie obszarów tworzy hierarchię na potrzeby routingu przez dodanie innego parametru area
trasy do controller
i action
. W tej sekcji omówiono sposób interakcji routingu z obszarami. Zobacz Obszary , aby uzyskać szczegółowe informacje na temat sposobu użycia obszarów z widokami.
Poniższy przykład umożliwia skonfigurowanie wzorca MVC do używania domyślnej trasy konwencjonalnej i area
trasy dla nazwanego area
Blog
elementu :
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();
W poprzednim kodzie jest wywoływany w MapAreaControllerRoute celu utworzenia elementu "blog_route"
. Drugi parametr , "Blog"
to nazwa obszaru.
Podczas dopasowywania ścieżki adresu URL, takiej jak /Manage/Users/AddUser
, "blog_route"
trasa generuje wartości { area = Blog, controller = Users, action = AddUser }
trasy . Wartość area
trasy jest generowany przez wartość domyślną dla area
elementu . Trasa utworzona przez MapAreaControllerRoute
jest równoważna następującym:
app.MapControllerRoute("blog_route", "Manage/{controller}/{action}/{id?}",
defaults: new { area = "Blog" }, constraints: new { area = "Blog" });
app.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
MapAreaControllerRoute
Tworzy trasę przy użyciu zarówno wartości domyślnej, jak i ograniczenia dla area
używania podanej nazwy obszaru, w tym przypadku Blog
. Wartość domyślna gwarantuje, że trasa zawsze generuje { area = Blog, ... }
wartość , ograniczenie wymaga wartości { area = Blog, ... }
generowania adresu URL.
Routing konwencjonalny jest zależny od kolejności. Ogólnie rzecz biorąc, trasy z obszarami powinny być umieszczane wcześniej, ponieważ są bardziej szczegółowe niż trasy bez obszaru.
Korzystając z powyższego przykładu, wartości { area = Blog, controller = Users, action = AddUser }
tras są zgodne z następującą akcją:
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}");
}
}
}
Atrybut [Area] określa kontroler w ramach obszaru. Ten kontroler znajduje się w Blog
obszarze. Kontrolery bez atrybutu [Area]
nie są elementami członkowskimi żadnego obszaru i nie są zgodne, gdy area
wartość trasy jest dostarczana przez routing. W poniższym przykładzie tylko pierwszy kontroler wymieniony może odpowiadać wartościom { area = Blog, controller = Users, action = AddUser }
trasy.
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}");
}
}
}
Przestrzeń nazw każdego kontrolera jest wyświetlana tutaj w celu uzyskania kompletności. Jeśli poprzednie kontrolery używały tej samej przestrzeni nazw, zostanie wygenerowany błąd kompilatora. Przestrzenie nazw klas nie mają wpływu na routing MVC.
Dwa pierwsze kontrolery są członkami obszarów i są zgodne tylko wtedy, gdy ich nazwa obszaru jest podana area
przez wartość trasy. Trzeci kontroler nie jest członkiem żadnego obszaru i może być zgodny tylko wtedy, gdy żadna wartość area
nie jest dostarczana przez routing.
Jeśli chodzi o dopasowanie żadnej wartości, brak area
wartości jest taki sam, jak w przypadku wartości area
null lub pustego ciągu.
Podczas wykonywania akcji wewnątrz obszaru wartość trasy jest dostępna jako wartość area
otoczenia dla routingu do użycia na potrzeby generowania adresów URL. Oznacza to, że domyślnie obszary działają lepkie dla generowania adresów URL, jak pokazano w poniższym przykładzie.
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);
}
}
}
Poniższy kod generuje adres URL do /Zebra/Users/AddUser
:
public class HomeController : Controller
{
public IActionResult About()
{
var url = Url.Action("AddUser", "Users", new { Area = "Zebra" });
return Content($"URL: {url}");
}
Definicja akcji
Metody publiczne na kontrolerze, z wyjątkiem tych z atrybutem NonAction , są akcjami.
Przykładowy kod
- MyDisplayRouteInfo jest dostarczany przez pakiet NuGet Rick.Docs.Samples.RouteInfo i wyświetla informacje o trasie.
- Wyświetl lub pobierz przykładowy kod (jak pobrać)
Diagnostyka debugowania
Aby uzyskać szczegółowe dane wyjściowe diagnostyki routingu, ustaw wartość Logging:LogLevel:Microsoft
Debug
. W środowisku deweloperów ustaw poziom dziennika w pliku appsettings.Development.json
:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Debug",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
kontrolery ASP.NET Core używają oprogramowania pośredniczącego routingu, aby dopasować adresy URL żądań przychodzących i mapować je na akcje. Szablony tras:
- Są definiowane w kodzie startowym lub atrybutach.
- Opisz, jak ścieżki adresów URL są dopasowywane do akcji.
- Są używane do generowania adresów URL łączy. Wygenerowane linki są zwykle zwracane w odpowiedziach.
Akcje są tradycyjnie kierowane lub kierowane przez atrybuty. Umieszczenie trasy na kontrolerze lub akcji sprawia, że jest kierowany atrybutem. Aby uzyskać więcej informacji, zobacz Routing mieszany .
Ten dokument:
- Objaśnia interakcje między mvc i routingiem:
- Jak typowe aplikacje MVC korzystają z funkcji routingu.
- Obejmuje oba:
- Konwencjonalny routing zwykle używany z kontrolerami i widokami.
- Routing atrybutów używany z REST interfejsami API. Jeśli interesuje Cię przede wszystkim routing dla REST interfejsów API, przejdź do sekcji Routing atrybutów dla REST interfejsów API.
- Zobacz Routing , aby uzyskać szczegółowe informacje na temat routingu zaawansowanego.
- Odwołuje się do domyślnego systemu routingu dodanego w ASP.NET Core 3.0, nazywanego routingiem punktów końcowych. Do celów zgodności można używać kontrolerów z poprzednią wersją routingu. Aby uzyskać instrukcje, zobacz przewodnik migracji 2.2-3.0. Zapoznaj się z wersją 2.2 tego dokumentu , aby uzyskać informacje referencyjne dotyczące starszego systemu routingu.
Konfigurowanie konwencjonalnej trasy
Startup.Configure
Zazwyczaj kod jest podobny do następującego w przypadku korzystania z routingu konwencjonalnego:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
Wewnątrz wywołania metody UseEndpointsMapControllerRoute jest używana do tworzenia pojedynczej trasy. Pojedyncza trasa nosi nazwę default
route. Większość aplikacji z kontrolerami i widokami używa szablonu trasy podobnego do default
trasy. REST Interfejsy API powinny używać routingu atrybutów.
Szablon "{controller=Home}/{action=Index}/{id?}"
trasy:
Pasuje do ścieżki adresu URL, takiej jak
/Products/Details/5
Wyodrębnia wartości
{ controller = Products, action = Details, id = 5 }
tras, tokenizując ścieżkę. Wyodrębnianie wartości tras powoduje dopasowanie, jeśli aplikacja ma kontroler o nazwieProductsController
iDetails
akcję:public class ProductsController : Controller { public IActionResult Details(int id) { return ControllerContext.MyDisplayRouteInfo(id); } }
MyDisplayRouteInfo jest dostarczany przez pakiet NuGet Rick.Docs.Samples.RouteInfo i wyświetla informacje o trasie.
/Products/Details/5
model wiąże wartość parametruid = 5
z wartościąid
, aby ustawić parametr na5
. Aby uzyskać więcej informacji, zobacz Powiązanie modelu.{controller=Home}
definiujeHome
jako wartość domyślnącontroller
.{action=Index}
definiujeIndex
jako wartość domyślnąaction
.Znak
?
w pliku{id?}
definiujeid
się jako opcjonalny.Domyślne i opcjonalne parametry trasy nie muszą być obecne w ścieżce adresu URL dla dopasowania. Zobacz Dokumentacja szablonu trasy, aby uzyskać szczegółowy opis składni szablonu trasy.
Pasuje do ścieżki
/
adresu URL .Tworzy wartości
{ controller = Home, action = Index }
tras .
Wartości i controller
action
używają wartości domyślnych. id
nie generuje wartości, ponieważ w ścieżce adresu URL nie ma odpowiedniego segmentu. /
odpowiada tylko wtedy, gdy istnieje akcja HomeController
i Index
:
public class HomeController : Controller
{
public IActionResult Index() { ... }
}
Korzystając z powyższej definicji kontrolera i szablonu trasy, HomeController.Index
akcja jest uruchamiana dla następujących ścieżek URL:
/Home/Index/17
/Home/Index
/Home
/
Ścieżka adresu URL /
używa domyślnych Home
kontrolerów i Index
akcji szablonu trasy. Ścieżka adresu URL /Home
używa domyślnej Index
akcji szablonu trasy.
Metoda MapDefaultControllerRoutewygody:
endpoints.MapDefaultControllerRoute();
Zastępuje:
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
Ważne
Routing jest konfigurowany przy użyciu oprogramowania pośredniczącego UseRouting, MapControllerRoute
i MapAreaControllerRoute
. Aby użyć kontrolerów:
- Wywołaj element MapControllers wewnątrz
UseEndpoints
, aby mapować atrybuty kierowane kontrolery. - Wywołaj MapControllerRoute metodę lub MapAreaControllerRoute, aby mapować zarówno konwencjonalne kontrolery trasowane, jak i atrybuty kierowane kontrolery.
Routing konwencjonalny
Routing konwencjonalny jest używany z kontrolerami i widokami. Trasa default
:
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Powyższy jest przykładem konwencjonalnej trasy. Jest to nazywane routingiem konwencjonalnym, ponieważ ustanawia konwencję dla ścieżek adresów URL:
- Pierwszy segment ścieżki,
{controller=Home}
, mapuje na nazwę kontrolera. - Drugi segment,
{action=Index}
, mapuje na nazwę akcji . - Trzeci segment
{id?}
jest używany dla opcjonalnegoid
elementu . Element?
in{id?}
sprawia, że jest opcjonalny.id
służy do mapowania na jednostkę modelu.
Korzystając z tej default
trasy, ścieżka adresu URL:
/Products/List
mapuje naProductsController.List
akcję./Blog/Article/17
mapuje naBlogController.Article
i zazwyczaj model wiążeid
parametr z wartością 17.
To mapowanie:
- Jest oparty tylko na kontrolerach i nazwach akcji.
- Nie jest oparta na przestrzeniach nazw, lokalizacjach plików źródłowych ani parametrach metody.
Użycie konwencjonalnego routingu z trasą domyślną umożliwia utworzenie aplikacji bez konieczności tworzenia nowego wzorca adresu URL dla każdej akcji. W przypadku aplikacji z akcjami stylu CRUD spójność adresów URL między kontrolerami:
- Ułatwia uproszczenie kodu.
- Sprawia, że interfejs użytkownika jest bardziej przewidywalny.
Ostrzeżenie
Element id
w poprzednim kodzie jest definiowany jako opcjonalny przez szablon trasy. Akcje mogą być wykonywane bez opcjonalnego identyfikatora podanego jako część adresu URL. Ogólnie rzecz biorąc, w przypadku id
pominięcia z adresu URL:
id
parametr jest ustawiany na0
wartość według powiązania modelu.- W bazie danych pasującej
id == 0
nie można odnaleźć jednostki .
Routing atrybutów zapewnia szczegółową kontrolę, aby identyfikator był wymagany dla niektórych akcji, a nie dla innych. Zgodnie z konwencją dokumentacja zawiera opcjonalne parametry, takie jak id
wtedy, gdy prawdopodobnie pojawią się w poprawnym użyciu.
Większość aplikacji powinna wybrać podstawowy i opisowy schemat routingu, aby adresy URL mogły być czytelne i zrozumiałe. Domyślna trasa konwencjonalna {controller=Home}/{action=Index}/{id?}
:
- Obsługuje podstawowy i opisowy schemat routingu.
- Jest przydatnym punktem wyjścia dla aplikacji opartych na interfejsie użytkownika.
- Jest jedynym szablonem trasy wymaganym dla wielu aplikacji internetowych interfejsu użytkownika. W przypadku większych aplikacji interfejsu użytkownika sieci Web kolejna trasa korzystająca z obszarów jest często wymagana.
MapControllerRoute i MapAreaRoute :
- Automatycznie przypisz wartość zamówienia do swoich punktów końcowych na podstawie kolejności, w której są wywoływane.
Routing punktów końcowych w programie ASP.NET Core 3.0 lub nowszym:
- Nie ma pojęcia tras.
- Nie zapewnia gwarancji kolejności wykonywania rozszerzalności, wszystkie punkty końcowe są przetwarzane jednocześnie.
Włącz rejestrowanie , aby zobaczyć, jak wbudowane implementacje routingu, takie jak Route, pasują do żądań.
Routing atrybutów wyjaśniono w dalszej części tego dokumentu.
Wiele konwencjonalnych tras
W środku UseEndpoints
można dodać wiele konwencjonalnych tras, dodając więcej wywołań do MapControllerRoute i MapAreaControllerRoute. Umożliwia to zdefiniowanie wielu konwencji lub dodanie konwencjonalnych tras przeznaczonych do określonej akcji, takich jak:
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?}");
});
Trasa blog
w poprzednim kodzie jest dedykowaną trasą konwencjonalną. Nazywa się to dedykowaną tradycyjną trasą, ponieważ:
- Korzysta z konwencjonalnego routingu.
- Jest ona przeznaczona dla określonej akcji.
Ponieważ controller
i action
nie są wyświetlane w szablonie "blog/{*article}"
trasy jako parametry:
- Mogą mieć tylko wartości
{ controller = "Blog", action = "Article" }
domyślne . - Ta trasa zawsze mapuje na akcję
BlogController.Article
.
/Blog
, /Blog/Article
i /Blog/{any-string}
są jedynymi ścieżkami adresów URL pasujących do trasy blogu.
Powyższy przykład:
blog
trasa ma wyższy priorytet dla dopasowań niżdefault
trasa, ponieważ jest dodawana jako pierwsza.- To przykład routingu stylu Slug , w którym zazwyczaj jest używana nazwa artykułu jako część adresu URL.
Ostrzeżenie
W programie ASP.NET Core 3.0 lub nowszym routing nie jest:
- Definiowanie koncepcji nazywanej trasą.
UseRouting
Dodaje trasę zgodną z potokiem oprogramowania pośredniczącego. OprogramowanieUseRouting
pośredniczące analizuje zestaw punktów końcowych zdefiniowanych w aplikacji i wybiera najlepsze dopasowanie punktu końcowego na podstawie żądania. - Zapewnianie gwarancji dotyczących kolejności wykonywania rozszerzalności, takich jak IRouteConstraint lub IActionConstraint.
Zobacz Routing , aby uzyskać informacje referencyjne dotyczące routingu.
Tradycyjna kolejność routingu
Routing konwencjonalny pasuje tylko do kombinacji akcji i kontrolera, które są zdefiniowane przez aplikację. Ma to na celu uproszczenie przypadków, w których nakładają się konwencjonalne trasy.
Dodawanie tras przy użyciu metody MapControllerRoute, MapDefaultControllerRoutei MapAreaControllerRoute automatyczne przypisywanie wartości zamówienia do ich punktów końcowych na podstawie kolejności, w której są wywoływane. Dopasowania z wyświetlonej wcześniej trasy mają wyższy priorytet. Routing konwencjonalny jest zależny od kolejności. Ogólnie rzecz biorąc, trasy z obszarami powinny być umieszczane wcześniej, ponieważ są bardziej szczegółowe niż trasy bez obszaru. Dedykowane trasy konwencjonalne z parametrami trasy typu catch-all, takie jak {*article}
mogą sprawić, że trasa będzie zbyt chciwa, co oznacza, że pasuje do adresów URL, które mają być dopasowane przez inne trasy. Umieść chciwe trasy później w tabeli tras, aby zapobiec chciwym dopasowaniom.
Ostrzeżenie
Parametr catch-all może być niepoprawnie zgodny z trasami z powodu błędu w routingu. Aplikacje, których dotyczy ta usterka, mają następujące cechy:
- Trasa typu catch-all, na przykład
{**slug}"
- Trasa catch-all nie pasuje do żądań, które powinna być zgodna.
- Usunięcie innych tras sprawia, że trasa catch-all zacznie działać.
Zobacz Usterki usługi GitHub 18677 i 16579 , na przykład przypadki, w których wystąpiła ta usterka.
Poprawka zgody na tę usterkę jest zawarta w zestawie .NET Core 3.1.301 SDK i nowszych wersjach. Poniższy kod ustawia przełącznik wewnętrzny, który naprawia tę usterkę:
public static void Main(string[] args)
{
AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior",
true);
CreateHostBuilder(args).Build().Run();
}
// Remaining code removed for brevity.
Rozwiązywanie niejednoznacznych akcji
Gdy dwa punkty końcowe są zgodne z routingiem, routing musi wykonać jedną z następujących czynności:
- Wybierz najlepszego kandydata.
- Zgłaszanie wyjątku.
Na przykład:
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);
}
}
}
Poprzedni kontroler definiuje dwie akcje zgodne z następującymi akcjami:
- Ścieżka adresu URL
/Products33/Edit/17
- Kierowanie danych
{ controller = Products33, action = Edit, id = 17 }
.
Jest to typowy wzorzec dla kontrolerów MVC:
Edit(int)
wyświetla formularz do edycji produktu.Edit(int, Product)
przetwarza opublikowany formularz.
Aby rozwiązać prawidłową trasę:
Edit(int, Product)
jest wybierany, gdy żądanie jest httpPOST
.Edit(int)
jest wybierany, gdy czasownik HTTP jest czymkolwiek innym.Edit(int)
jest zwykle wywoływana za pośrednictwem .GET
[HttpPost]
Element HttpPostAttribute, jest dostarczany do routingu, aby można było wybrać go na podstawie metody HTTP żądania. Funkcja HttpPostAttribute
sprawia, że Edit(int, Product)
lepsze dopasowanie niż Edit(int)
.
Ważne jest, aby zrozumieć rolę atrybutów, takich jak HttpPostAttribute
. Podobne atrybuty są definiowane dla innych czasowników HTTP. W przypadku routingu konwencjonalnego akcje używają tej samej nazwy akcji, gdy są częścią formularza pokazu, przesyłaj przepływ pracy formularza. Na przykład zobacz Badanie dwóch metod akcji Edytuj.
Jeśli routing nie może wybrać najlepszego kandydata, AmbiguousMatchException zostanie zgłoszony element z listą wielu pasowanych punktów końcowych.
Konwencjonalne nazwy tras
Ciągi i "default"
w następujących przykładach "blog"
to konwencjonalne nazwy tras:
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?}");
});
Nazwy tras dają trasie nazwę logiczną. Nazwana trasa może służyć do generowania adresów URL. Użycie nazwanej trasy upraszcza tworzenie adresów URL, gdy kolejność tras może spowodować, że generowanie adresów URL jest skomplikowane. Nazwy tras muszą być unikatowe dla całej aplikacji.
Nazwy tras:
- Nie ma wpływu na dopasowywanie adresów URL ani obsługę żądań.
- Są używane tylko do generowania adresów URL.
Koncepcja nazwy trasy jest reprezentowana w routingu jako IEndpointNameMetadata. Nazwa trasy i nazwa punktu końcowego terminów:
- Są zamienne.
- Który z nich jest używany w dokumentacji i kodzie, zależy od opisanego interfejsu API.
Routing atrybutów dla REST interfejsów API
REST Interfejsy API powinny używać routingu atrybutów do modelowania funkcjonalności aplikacji jako zestawu zasobów, w których operacje są reprezentowane przez czasowniki HTTP.
Routing atrybutów używa zestawu atrybutów do mapowania akcji bezpośrednio na szablony tras. Poniższy StartUp.Configure
kod jest typowy dla interfejsu REST API i jest używany w następnym przykładzie:
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();
});
}
W poprzednim kodzie jest wywoływana wewnątrzUseEndpoints
, MapControllers aby mapować atrybuty kierowane kontrolery.
W poniższym przykładzie:
HomeController
pasuje do zestawu adresów URL podobnych do domyślnej konwencjonalnej trasy{controller=Home}/{action=Index}/{id?}
.
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);
}
}
Akcja HomeController.Index
jest uruchamiana dla dowolnej ścieżki /
adresu URL , /Home
, /Home/Index
lub /Home/Index/3
.
W tym przykładzie wyróżniono kluczową różnicę programową między routingiem atrybutów a routingiem konwencjonalnym. Routing atrybutów wymaga większej ilości danych wejściowych w celu określenia trasy. Tradycyjna trasa domyślna obsługuje trasy bardziej zwięźle. Jednak routing atrybutów zezwala i wymaga dokładnej kontroli, które szablony tras mają zastosowanie do każdej akcji.
W przypadku routingu atrybutów nazwy kontrolera i akcji nie odgrywają żadnej roli, w której akcja jest dopasowywana, chyba że jest używana zamiana tokenu. Poniższy przykład jest zgodny z tymi samymi adresami URL co w poprzednim przykładzie:
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);
}
}
Poniższy kod używa zamiany tokenu dla action
elementów i 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();
}
}
Następujący kod ma zastosowanie [Route("[controller]/[action]")]
do kontrolera:
[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();
}
}
W poprzednim kodzie Index
szablony metod muszą poprzedzać /
szablony tras lub ~/
do nich. Szablony tras stosowane do akcji rozpoczynającej się /
od lub ~/
nie są łączone z szablonami tras zastosowanymi do kontrolera.
Zobacz Pierwszeństwo szablonu trasy, aby uzyskać informacje na temat wyboru szablonu trasy.
Nazwy routingu zarezerwowanego
Następujące słowa kluczowe to zastrzeżone nazwy parametrów tras podczas korzystania z kontrolerów lub Razor stron:
action
area
controller
handler
page
Używanie page
jako parametru trasy z routingiem atrybutów jest typowym błędem. Powoduje to niespójne i mylące zachowanie podczas generowania adresów URL.
public class MyDemo2Controller : Controller
{
[Route("/articles/{page}")]
public IActionResult ListArticles(int page)
{
return ControllerContext.MyDisplayRouteInfo(page);
}
}
Nazwy parametrów specjalnych są używane przez generowanie adresów URL w celu określenia, czy operacja generowania adresu URL odnosi się do Razor strony lub kontrolera.
Następujące słowa kluczowe są zastrzeżone w kontekście Razor widoku lub Razor strony:
page
using
namespace
inject
section
inherits
model
addTagHelper
removeTagHelper
Te słowa kluczowe nie powinny być używane w przypadku generowania linków, parametrów powiązanych z modelem ani właściwości najwyższego poziomu.
Szablony czasowników HTTP
ASP.NET Core ma następujące szablony czasowników HTTP:
Szablony tras
ASP.NET Core ma następujące szablony tras:
Routing atrybutów z atrybutami czasownika Http
Rozważmy następujący kontroler:
[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);
}
}
Powyższy kod:
- Każda akcja zawiera
[HttpGet]
atrybut, który ogranicza dopasowanie tylko do żądań HTTP GET. - Akcja
GetProduct
zawiera"{id}"
szablon, dlategoid
jest dołączana do"api/[controller]"
szablonu na kontrolerze. Szablon metod to"api/[controller]/{id}"
. W związku z tym ta akcja odpowiada tylko żądaniom GET dla formularza/api/test2/xyz
,/api/test2/123
/api/test2/{any string}
itp.[HttpGet("{id}")] // GET /api/test2/xyz public IActionResult GetProduct(string id) { return ControllerContext.MyDisplayRouteInfo(id); }
- Akcja
GetIntProduct
zawiera"int/{id:int}"
szablon. Część:int
szablonu ograniczaid
wartości trasy do ciągów, które można przekonwertować na liczbę całkowitą. Żądanie GET do/api/test2/int/abc
:- Nie pasuje do tej akcji.
- Zwraca błąd 404 Nie znaleziono.
[HttpGet("int/{id:int}")] // GET /api/test2/int/3 public IActionResult GetIntProduct(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
- Akcja
GetInt2Product
zawiera{id}
element w szablonie, ale nie ograniczaid
się do wartości, które można przekonwertować na liczbę całkowitą. Żądanie GET do/api/test2/int2/abc
:- Pasuje do tej trasy.
- Nie można przekonwertować
abc
powiązania modelu na liczbę całkowitą. Parametrid
metody to liczba całkowita. - Zwraca nieprawidłowe żądanie 400, ponieważ nie można przekonwertować
abc
powiązania modelu na liczbę całkowitą.[HttpGet("int2/{id}")] // GET /api/test2/int2/3 public IActionResult GetInt2Product(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
Routing atrybutów może używać HttpMethodAttribute atrybutów, takich jak HttpPostAttribute, HttpPutAttributei HttpDeleteAttribute. Wszystkie atrybuty czasownika HTTP akceptują szablon trasy. W poniższym przykładzie przedstawiono dwie akcje pasujące do tego samego szablonu trasy:
[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);
}
}
Za pomocą ścieżki /products3
adresu URL:
- Akcja
MyProductsController.ListProducts
jest uruchamiana, gdy czasownik HTTP toGET
. - Akcja
MyProductsController.CreateProduct
jest uruchamiana, gdy czasownik HTTP toPOST
.
Podczas tworzenia interfejsu REST API rzadko trzeba użyć [Route(...)]
metody akcji, ponieważ akcja akceptuje wszystkie metody HTTP. Lepiej użyć bardziej szczegółowego atrybutu czasownika HTTP, aby dokładnie określić, co obsługuje interfejs API. Oczekuje się, że klienci interfejsów REST API będą wiedzieć, jakie ścieżki i czasowniki HTTP mapują na określone operacje logiczne.
REST Interfejsy API powinny używać routingu atrybutów do modelowania funkcjonalności aplikacji jako zestawu zasobów, w których operacje są reprezentowane przez czasowniki HTTP. Oznacza to, że wiele operacji, na przykład GET i POST w tym samym zasobie logicznym, używa tego samego adresu URL. Routing atrybutów zapewnia poziom kontroli, który jest potrzebny do dokładnego zaprojektowania publicznego układu punktu końcowego interfejsu API.
Ponieważ trasa atrybutu ma zastosowanie do określonej akcji, łatwo jest ustawić parametry wymagane w ramach definicji szablonu trasy. W poniższym przykładzie id
jest wymagany jako część ścieżki adresu URL:
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Akcja Products2ApiController.GetProduct(int)
:
- Jest uruchamiany ze ścieżką adresu URL, na przykład
/products2/3
- Nie jest uruchamiany przy użyciu ścieżki
/products2
adresu URL .
Atrybut [Consumes] umożliwia akcji ograniczenie obsługiwanych typów zawartości żądania. Aby uzyskać więcej informacji, zobacz Definiowanie obsługiwanych typów zawartości żądań za pomocą atrybutu Consumes.
Zobacz Routing , aby uzyskać pełny opis szablonów tras i powiązanych opcji.
Aby uzyskać więcej informacji na temat [ApiController]
programu , zobacz Atrybut ApiController.
Nazwa trasy
Poniższy kod definiuje nazwę trasy :Products_List
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Nazwy tras mogą służyć do generowania adresu URL na podstawie określonej trasy. Nazwy tras:
- Nie ma wpływu na zachowanie dopasowywania adresów URL routingu.
- Są używane tylko do generowania adresów URL.
Nazwy tras muszą być unikatowe dla całej aplikacji.
Porównaj powyższy kod z tradycyjną trasą domyślną, która definiuje id
parametr jako opcjonalny ({id?}
). Możliwość dokładnego określenia interfejsów API ma zalety, takie jak umożliwienie /products
i /products/5
wysłanie ich do różnych akcji.
Łączenie tras atrybutów
Aby routing atrybutów był mniej powtarzalny, atrybuty trasy na kontrolerze są łączone z atrybutami trasy w poszczególnych akcjach. Wszystkie szablony tras zdefiniowane na kontrolerze są dołączane do szablonów tras w akcjach. Umieszczenie atrybutu trasy na kontrolerze sprawia, że wszystkie akcje w kontrolerze używają routingu atrybutów.
[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);
}
}
W powyższym przykładzie:
- Ścieżka
/products
adresu URL może być zgodnaProductsApi.ListProducts
- Ścieżka
/products/5
adresu URL może być zgodna zProductsApi.GetProduct(int)
.
Obie te akcje są zgodne tylko z protokołem HTTP GET
, ponieważ są one oznaczone atrybutem [HttpGet]
.
Szablony tras stosowane do akcji rozpoczynającej się /
od lub ~/
nie są łączone z szablonami tras zastosowanymi do kontrolera. Poniższy przykład pasuje do zestawu ścieżek URL podobnych do trasy domyślnej.
[Route("Home")]
public class HomeController : Controller
{
[Route("")]
[Route("Index")]
[Route("/")]
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
[Route("About")]
public IActionResult About()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
W poniższej tabeli opisano [Route]
atrybuty w poprzednim kodzie:
Atrybut | Łączy się z [Route("Home")] |
Definiuje szablon trasy |
---|---|---|
[Route("")] |
Tak | "Home" |
[Route("Index")] |
Tak | "Home/Index" |
[Route("/")] |
Nie | "" |
[Route("About")] |
Tak | "Home/About" |
Kolejność tras atrybutów
Routing tworzy drzewo i pasuje do wszystkich punktów końcowych jednocześnie:
- Wpisy trasy zachowują się tak, jakby zostały umieszczone w idealnej kolejności.
- Najbardziej konkretne trasy mają szansę wykonać przed bardziej ogólnymi trasami.
Na przykład trasa atrybutu, na blog/search/{topic}
przykład, jest bardziej specyficzna niż trasa atrybutu, na przykład blog/{*article}
. Trasa blog/search/{topic}
ma domyślnie wyższy priorytet, ponieważ jest bardziej szczegółowa. Korzystając z routingu konwencjonalnego, deweloper jest odpowiedzialny za umieszczanie tras w żądanej kolejności.
Trasy atrybutów mogą konfigurować kolejność przy użyciu Order właściwości . Wszystkie udostępnione atrybuty Order
trasy obejmują . Trasy są przetwarzane zgodnie z rosnącą właściwością Order
. Domyślna kolejność to 0
. Ustawianie trasy przy użyciu Order = -1
przebiegów przed trasami, które nie ustawiają kolejności. Ustawianie trasy przy użyciu Order = 1
przebiegów po domyślnym porządkowaniu tras.
Unikaj w zależności od Order
. Jeśli miejsce w adresie URL aplikacji wymaga jawnych wartości zamówień w celu poprawnego kierowania, prawdopodobnie jest to również mylące dla klientów. Ogólnie rzecz biorąc, routing atrybutów wybiera poprawną trasę z dopasowaniem adresu URL. Jeśli domyślna kolejność używana na potrzeby generowania adresów URL nie działa, użycie nazwy trasy jako zastąpienia jest zwykle prostsze niż zastosowanie Order
właściwości.
Rozważmy dwa następujące kontrolery, które definiują dopasowanie /home
trasy:
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);
}
}
/home
Żądanie z powyższym kodem zgłasza wyjątek podobny do następującego:
AmbiguousMatchException: The request matched multiple endpoints. Matches:
WebMvcRouting.Controllers.HomeController.Index
WebMvcRouting.Controllers.MyDemoController.MyIndex
Dodanie Order
do jednego z atrybutów trasy rozwiązuje niejednoznaczność:
[Route("")]
[Route("Home", Order = 2)]
[Route("Home/MyIndex")]
public IActionResult MyIndex()
{
return ControllerContext.MyDisplayRouteInfo();
}
W poprzednim kodzie /home
uruchamia HomeController.Index
punkt końcowy. Aby uzyskać dostęp do MyDemoController.MyIndex
elementu , żądanie /home/MyIndex
. Uwaga:
- Powyższy kod jest przykładem lub słabym projektem routingu. Użyto go do zilustrowania
Order
właściwości. - Właściwość
Order
rozwiązuje tylko niejednoznaczność, że nie można dopasować tego szablonu. Lepiej byłoby usunąć[Route("Home")]
szablon.
Zobacz Razor Konwencje tras stron i aplikacji: Kolejność tras, aby uzyskać informacje na temat kolejności tras za pomocą Razor stron.
W niektórych przypadkach zwracany jest błąd HTTP 500 z niejednoznacznymi trasami. Użyj rejestrowania , aby zobaczyć, które punkty końcowe spowodowały AmbiguousMatchException
.
Zamiana tokenu w szablonach tras [kontroler], [action], [area]
Dla wygody trasy atrybutów obsługują zamianę tokenu przez dołączenie tokenu w nawiasy kwadratowe ([
, ]
). Tokeny [action]
, [area]
i [controller]
są zastępowane wartościami nazwy akcji, nazwy obszaru i nazwy kontrolera z akcji, w której zdefiniowano trasę:
[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);
}
}
Powyższy kod:
[HttpGet]
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
- Pasuje
/Products0/List
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
- Pasuje
/Products0/Edit/{id}
Zastąpienie tokenu odbywa się w ostatnim kroku tworzenia tras atrybutów. Powyższy przykład zachowuje się tak samo jak w poniższym kodzie:
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);
}
}
Jeśli czytasz to w języku innym niż angielski, daj nam znać w tym problemie z dyskusją w usłudze GitHub, jeśli chcesz zobaczyć komentarze kodu w języku natywnym.
Trasy atrybutów można również łączyć z dziedziczeniem. Jest to zaawansowane połączenie z zastąpieniem tokenu. Zamiana tokenu ma również zastosowanie do nazw tras zdefiniowanych przez trasy atrybutów.
[Route("[controller]/[action]", Name="[controller]_[action]")]
generuje unikatową nazwę trasy dla każdej akcji:
[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);
}
}
Aby dopasować ogranicznik [
zastępczy tokenu literału lub ]
, należy go uruchomić, powtarzając znak ([[
lub ]]
).
Dostosowywanie zamiany tokenu za pomocą funkcji przekształcania parametrów
Zamiana tokenu można dostosować przy użyciu funkcji przekształcania parametrów. Transformator parametrów implementuje IOutboundParameterTransformer i przekształca wartość parametrów. Na przykład funkcja przekształcania parametrów niestandardowych SlugifyParameterTransformer
zmienia SubscriptionManagement
wartość trasy na 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();
}
}
Jest RouteTokenTransformerConvention to konwencja modelu aplikacji, która:
- Stosuje transformator parametru do wszystkich tras atrybutów w aplikacji.
- Dostosowuje wartości tokenu trasy atrybutu podczas ich zastępowania.
public class SubscriptionManagementController : Controller
{
[HttpGet("[controller]/[action]")]
public IActionResult ListAll()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Poprzednia metoda odpowiada /subscription-management/list-all
metodzie ListAll
.
Element RouteTokenTransformerConvention
jest zarejestrowany jako opcja w ConfigureServices
pliku .
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Conventions.Add(new RouteTokenTransformerConvention(
new SlugifyParameterTransformer()));
});
}
Aby uzyskać definicję usługi Slug, zobacz dokumentację internetową usługi MDN w usłudze Slug .
Ostrzeżenie
W przypadku używania System.Text.RegularExpressions metody do przetwarzania niezaufanych danych wejściowych należy przekazać limit czasu. Złośliwy użytkownik może podać dane wejściowe, aby spowodować RegularExpressions
atak typu "odmowa usługi". ASP.NET podstawowe interfejsy API platformy, które używają RegularExpressions
przekroczenia limitu czasu.
Wiele tras atrybutów
Routing atrybutów obsługuje definiowanie wielu tras, które docierają do tej samej akcji. Najczęstszym zastosowaniem tej metody jest naśladowanie zachowania domyślnej konwencjonalnej trasy, jak pokazano w poniższym przykładzie:
[Route("[controller]")]
public class Products13Controller : Controller
{
[Route("")] // Matches 'Products13'
[Route("Index")] // Matches 'Products13/Index'
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
Umieszczenie wielu atrybutów trasy na kontrolerze oznacza, że każdy z nich łączy się z każdym z atrybutów trasy w metodach akcji:
[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();
}
}
Wszystkie ograniczenia trasy czasownika HTTP implementują IActionConstraint
.
Gdy wiele IActionConstraint atrybutów trasy implementujących jest umieszczanych w akcji:
- Każde ograniczenie akcji łączy się z szablonem trasy zastosowanym do kontrolera.
[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();
}
}
Używanie wielu tras w akcjach może wydawać się przydatne i zaawansowane, lepiej zachować podstawową i dobrze zdefiniowaną przestrzeń adresów URL aplikacji. Użyj wielu tras w akcjach tylko w razie potrzeby, aby obsługiwać istniejących klientów.
Określanie opcjonalnych parametrów trasy atrybutu, wartości domyślnych i ograniczeń
Trasy atrybutów obsługują tę samą składnię śródliniową co konwencjonalne trasy w celu określenia opcjonalnych parametrów, wartości domyślnych i ograniczeń.
public class Products14Controller : Controller
{
[HttpPost("product14/{id:int}")]
public IActionResult ShowProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
W poprzednim kodzie [HttpPost("product14/{id:int}")]
stosuje ograniczenie trasy. Akcja Products14Controller.ShowProduct
jest dopasowywana tylko przez ścieżki adresu URL, takie jak /product14/3
. Część szablonu {id:int}
trasy ogranicza ten segment tylko do liczb całkowitych.
Zobacz Dokumentacja szablonu trasy, aby uzyskać szczegółowy opis składni szablonu trasy.
Niestandardowe atrybuty trasy przy użyciu elementu IRouteTemplateProvider
Wszystkie atrybuty trasy implementują IRouteTemplateProviderelement . Środowisko uruchomieniowe ASP.NET Core:
- Szuka atrybutów w klasach kontrolerów i metodach akcji podczas uruchamiania aplikacji.
- Używa atrybutów implementujących
IRouteTemplateProvider
do tworzenia początkowego zestawu tras.
Zaimplementuj IRouteTemplateProvider
, aby zdefiniować niestandardowe atrybuty trasy. Każda IRouteTemplateProvider
z nich umożliwia zdefiniowanie pojedynczej trasy przy użyciu niestandardowego szablonu trasy, kolejności i nazwy:
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();
}
}
Poprzednia Get
metoda zwraca wartość Order = 2, Template = api/MyTestApi
.
Dostosowywanie tras atrybutów za pomocą modelu aplikacji
Model aplikacji:
- Jest modelem obiektów utworzonym podczas uruchamiania.
- Zawiera wszystkie metadane używane przez ASP.NET Core do kierowania i wykonywania akcji w aplikacji.
Model aplikacji zawiera wszystkie dane zebrane z atrybutów trasy. Dane z atrybutów trasy są dostarczane przez implementację IRouteTemplateProvider
. Konwencje:
- Można zapisać w celu zmodyfikowania modelu aplikacji, aby dostosować sposób działania routingu.
- Są odczytywane podczas uruchamiania aplikacji.
W tej sekcji przedstawiono podstawowy przykład dostosowywania routingu przy użyciu modelu aplikacji. Poniższy kod sprawia, że trasy są w przybliżeniu zgodne ze strukturą folderów projektu.
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()
};
}
}
}
Poniższy kod uniemożliwia namespace
zastosowanie konwencji do kontrolerów, które są kierowane atrybutami:
public void Apply(ControllerModel controller)
{
var hasRouteAttributes = controller.Selectors.Any(selector =>
selector.AttributeRouteModel != null);
if (hasRouteAttributes)
{
return;
}
Na przykład następujący kontroler nie używa polecenia 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}");
}
}
Metoda NamespaceRoutingConvention.Apply
:
- Nic nie robi, jeśli kontroler jest kierowany atrybut.
- Ustawia szablon kontrolerów na
namespace
podstawie elementu z usuniętą baząnamespace
.
Element NamespaceRoutingConvention
można zastosować w pliku 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.
Rozważmy na przykład następujący kontroler:
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}");
}
}
}
Powyższy kod:
- Podstawą
namespace
jestMy.Application
. - Pełna nazwa poprzedniego kontrolera to
My.Application.Admin.Controllers.UsersController
. - Parametr
NamespaceRoutingConvention
ustawia szablon kontrolerów naAdmin/Controllers/Users/[action]/{id?
wartość .
Można NamespaceRoutingConvention
go również zastosować jako atrybut na kontrolerze:
[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 mieszany: routing atrybutów a routing konwencjonalny
aplikacje ASP.NET Core mogą łączyć użycie konwencjonalnego routingu i routingu atrybutów. Typowe jest używanie konwencjonalnych tras dla kontrolerów obsługujących strony HTML dla przeglądarek i routing atrybutów dla kontrolerów obsługujących REST interfejsy API.
Akcje są zwykle kierowane lub kierowane atrybuty. Umieszczenie trasy na kontrolerze lub akcji sprawia, że atrybut jest kierowany. Akcje definiujące trasy atrybutów nie mogą być osiągane za pośrednictwem konwencjonalnych tras i odwrotnie. Każdy atrybut trasy na kontrolerze sprawia, że wszystkie akcje w atrybucie kontrolera są kierowane.
Routing atrybutów i routing konwencjonalny używają tego samego aparatu routingu.
Generowanie adresów URL i wartości otoczenia
Aplikacje mogą używać funkcji generowania adresów URL routingu do generowania linków adresów URL do akcji. Generowanie adresów URL eliminuje trwałe adresy URL, dzięki czemu kod jest bardziej niezawodny i możliwy do utrzymania. Ta sekcja koncentruje się na funkcjach generowania adresów URL udostępnianych przez mvC i omówiono tylko podstawowe informacje na temat sposobu działania generowania adresów URL. Zobacz Routing , aby uzyskać szczegółowy opis generowania adresu URL.
Interfejs IUrlHelper jest podstawowym elementem infrastruktury między MVC i routingiem na potrzeby generowania adresów URL. Wystąpienie IUrlHelper
klasy jest dostępne za pośrednictwem Url
właściwości w kontrolerach, widokach i składnikach widoku.
W poniższym przykładzie IUrlHelper
interfejs jest używany za pośrednictwem Controller.Url
właściwości w celu wygenerowania adresu URL do innej akcji.
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();
}
}
Jeśli aplikacja używa domyślnej konwencjonalnej trasy, wartość url
zmiennej to ciąg /UrlGeneration/Destination
ścieżki adresu URL . Ta ścieżka adresu URL jest tworzona przez routing przez połączenie:
- Wartości trasy z bieżącego żądania, które są nazywane wartościami otoczenia.
- Wartości przekazane do
Url.Action
i podstawiając te wartości do szablonu trasy:
ambient values: { controller = "UrlGeneration", action = "Source" }
values passed to Url.Action: { controller = "UrlGeneration", action = "Destination" }
route template: {controller}/{action}/{id?}
result: /UrlGeneration/Destination
Każdy parametr trasy w szablonie trasy ma swoją wartość podstawioną przez dopasowanie nazw wartościami i wartościami otoczenia. Parametr trasy, który nie ma wartości, może:
- Użyj wartości domyślnej, jeśli ma ją.
- Jeśli jest to opcjonalne, należy pominąć. Na przykład element
id
z szablonu{controller}/{action}/{id?}
trasy .
Generowanie adresu URL kończy się niepowodzeniem, jeśli jakikolwiek wymagany parametr trasy nie ma odpowiedniej wartości. Jeśli generowanie adresu URL zakończy się niepowodzeniem dla trasy, kolejna trasa zostanie podjęta do momentu wypróbowanej wszystkich tras lub znalezienia dopasowania.
W poprzednim przykładzie przyjęto Url.Action
założenie, że routing konwencjonalny. Generowanie adresów URL działa podobnie z routingiem atrybutów, chociaż koncepcje są różne. Z konwencjonalnym routingiem:
- Wartości tras służą do rozwijania szablonu.
- Wartości tras dla
controller
iaction
zwykle są wyświetlane w tym szablonie. Działa to, ponieważ adresy URL dopasowane przez routing są zgodne z konwencją.
W poniższym przykładzie użyto routingu atrybutów:
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();
}
}
Akcja Source
w poprzednim kodzie generuje custom/url/to/destination
element .
LinkGenerator dodano w ASP.NET Core 3.0 jako alternatywę dla IUrlHelper
. LinkGenerator
oferuje podobne, ale bardziej elastyczne funkcje. Każda metoda w systemie IUrlHelper
ma również odpowiednią rodzinę metod LinkGenerator
.
Generowanie adresów URL według nazwy akcji
Url.Action, LinkGenerator.GetPathByAction i wszystkie powiązane przeciążenia są przeznaczone do generowania docelowego punktu końcowego przez określenie nazwy kontrolera i nazwy akcji.
W przypadku używania parametru Url.Action
bieżące wartości tras i controller
action
są udostępniane przez środowisko uruchomieniowe:
- Wartość
controller
iaction
są częścią wartości otoczenia i wartości. MetodaUrl.Action
zawsze używa bieżącychaction
wartości icontroller
generuje ścieżkę adresu URL, która kieruje do bieżącej akcji.
Routing próbuje użyć wartości w wartościach otoczenia, aby wypełnić informacje, które nie zostały podane podczas generowania adresu URL. Rozważ trasę podobną {a}/{b}/{c}/{d}
do wartości otoczenia { a = Alice, b = Bob, c = Carol, d = David }
:
- Routing ma wystarczającą ilość informacji, aby wygenerować adres URL bez żadnych dodatkowych wartości.
- Routing ma wystarczającą ilość informacji, ponieważ wszystkie parametry trasy mają wartość.
Jeśli wartość { d = Donovan }
zostanie dodana:
- Wartość
{ d = David }
jest ignorowana. - Wygenerowana ścieżka adresu URL to
Alice/Bob/Carol/Donovan
.
Ostrzeżenie: ścieżki adresów URL są hierarchiczne. W poprzednim przykładzie, jeśli wartość { c = Cheryl }
jest dodawana:
- Obie wartości
{ c = Carol, d = David }
są ignorowane. - Generowanie adresu URL nie jest już wartością
d
, a generowanie adresu URL kończy się niepowodzeniem. - Wymagane wartości
c
id
należy określić, aby wygenerować adres URL.
Możesz spodziewać się wystąpienia tego problemu z trasą {controller}/{action}/{id?}
domyślną . Ten problem jest rzadki w praktyce, ponieważ Url.Action
zawsze jawnie określa wartość controller
i action
.
Kilka przeciążeń adresu URL.Action wykonuje obiekt wartości trasy, aby podać wartości parametrów trasy innych niż controller
i action
. Obiekt wartości trasy jest często używany z elementem id
. Na przykład Url.Action("Buy", "Products", new { id = 17 })
. Obiekt wartości trasy:
- Zgodnie z konwencją jest zwykle obiektem typu anonimowego.
- Może to być
IDictionary<>
element lub POCO).
Wszelkie dodatkowe wartości tras, które nie pasują do parametrów trasy, są umieszczane w ciągu zapytania.
public IActionResult Index()
{
var url = Url.Action("Buy", "Products", new { id = 17, color = "red" });
return Content(url);
}
Powyższy kod generuje /Products/Buy/17?color=red
element .
Poniższy kod generuje bezwzględny adres URL:
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);
}
Aby utworzyć bezwzględny adres URL, użyj jednego z następujących elementów:
- Przeciążenie, które akceptuje element
protocol
. Na przykład powyższy kod. - LinkGenerator.GetUriByAction, który domyślnie generuje bezwzględne identyfikatory URI.
Generowanie adresów URL według trasy
Powyższy kod demonstrował generowanie adresu URL przez przekazanie kontrolera i nazwy akcji. IUrlHelper
Udostępnia również rodzinę metod Url.RouteUrl . Te metody są podobne do url.Action, ale nie kopiują bieżących action
wartości i controller
do wartości trasy. Najbardziej typowe użycie elementu Url.RouteUrl
:
- Określa nazwę trasy do wygenerowania adresu URL.
- Ogólnie rzecz biorąc, nie określa kontrolera ani nazwy akcji.
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();
}
Razor Poniższy plik generuje link HTML do pliku Destination_Route
:
<h1>Test Links</h1>
<ul>
<li><a href="@Url.RouteUrl("Destination_Route")">Test Destination_Route</a></li>
</ul>
Generowanie adresów URL w kodzie HTML i Razor
IHtmlHelperudostępnia metody Html.BeginForm i Html.ActionLink do generowania <form>
elementów i <a>
.HtmlHelper Te metody używają metody Url.Action do wygenerowania adresu URL i akceptują podobne argumenty. Są Url.RouteUrl
one obsługiwane HtmlHelper
Html.BeginRouteForm
i Html.RouteLink
które mają podobne funkcje.
TagHelpers generują adresy URL za pośrednictwem form
klasy TagHelper i <a>
TagHelper. Oba te zastosowania są używane IUrlHelper
do ich implementacji. Aby uzyskać więcej informacji, zobacz Pomocnicy tagów w formularzach .
Wewnątrz widoków IUrlHelper
właściwość jest dostępna za pośrednictwem Url
właściwości dla dowolnego generowania adresów URL ad hoc, które nie są objęte powyższymi elementami.
Generowanie adresu URL w wynikach akcji
W poprzednich przykładach pokazano użycie IUrlHelper
w kontrolerze. Najczęstszym użyciem kontrolera jest wygenerowanie adresu URL w ramach wyniku akcji.
Klasy ControllerBase i Controller zapewniają wygodne metody dla wyników akcji odwołujących się do innej akcji. Jednym z typowych użycia jest przekierowanie po zaakceptowaniu danych wejściowych użytkownika:
[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);
}
Akcja powoduje, że metody fabryki, takie jak RedirectToAction i CreatedAtAction , są zgodne ze wzorcem podobnym do metod w systemie IUrlHelper
.
Szczególny przypadek dla dedykowanych tras konwencjonalnych
Routing konwencjonalny może używać specjalnego rodzaju definicji trasy nazywanej dedykowaną trasą konwencjonalną. W poniższym przykładzie trasa o nazwie blog
jest dedykowaną trasą konwencjonalną:
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?}");
});
Korzystając z powyższych definicji tras, Url.Action("Index", "Home")
wygeneruje ścieżkę /
adresu URL przy użyciu default
trasy, ale dlaczego? Może się okazać, że wartości { controller = Home, action = Index }
trasy będą wystarczające, aby wygenerować adres URL przy użyciu metody blog
, a wynikiem będzie /blog?action=Index&controller=Home
.
Dedykowane trasy konwencjonalne polegają na specjalnym zachowaniu wartości domyślnych, które nie mają odpowiedniego parametru trasy, który uniemożliwia zbyt chciwość trasy generowania adresów URL. W takim przypadku wartości domyślne to { controller = Blog, action = Article }
, ani ani controller
nie action
są wyświetlane jako parametr trasy. Gdy routing wykonuje generowanie adresów URL, podane wartości muszą być zgodne z wartościami domyślnymi. Generowanie adresu URL przy użyciu metody kończy się niepowodzeniem blog
, ponieważ wartości { controller = Home, action = Index }
nie są zgodne { controller = Blog, action = Article }
z . Następnie routing wraca do próby default
, co powiedzie się.
Obszary
Obszary są funkcją MVC używaną do organizowania powiązanych funkcji w grupie jako oddzielnej:
- Routing przestrzeni nazw dla akcji kontrolera.
- Struktura folderów dla widoków.
Używanie obszarów umożliwia aplikacji posiadanie wielu kontrolerów o tej samej nazwie, o ile mają różne obszary. Użycie obszarów tworzy hierarchię na potrzeby routingu przez dodanie innego parametru area
trasy do controller
i action
. W tej sekcji omówiono sposób interakcji routingu z obszarami. Zobacz Obszary , aby uzyskać szczegółowe informacje na temat sposobu użycia obszarów z widokami.
Poniższy przykład umożliwia skonfigurowanie wzorca MVC do używania domyślnej trasy konwencjonalnej i area
trasy dla nazwanego area
Blog
elementu :
app.UseEndpoints(endpoints =>
{
endpoints.MapAreaControllerRoute("blog_route", "Blog",
"Manage/{controller}/{action}/{id?}");
endpoints.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
});
W poprzednim kodzie jest wywoływany w MapAreaControllerRoute celu utworzenia elementu "blog_route"
. Drugi parametr , "Blog"
to nazwa obszaru.
Podczas dopasowywania ścieżki adresu URL, takiej jak /Manage/Users/AddUser
, "blog_route"
trasa generuje wartości { area = Blog, controller = Users, action = AddUser }
trasy . Wartość area
trasy jest generowany przez wartość domyślną dla area
elementu . Trasa utworzona przez MapAreaControllerRoute
jest równoważna następującym:
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
Tworzy trasę przy użyciu zarówno wartości domyślnej, jak i ograniczenia dla area
używania podanej nazwy obszaru, w tym przypadku Blog
. Wartość domyślna gwarantuje, że trasa zawsze generuje { area = Blog, ... }
wartość , ograniczenie wymaga wartości { area = Blog, ... }
generowania adresu URL.
Routing konwencjonalny jest zależny od kolejności. Ogólnie rzecz biorąc, trasy z obszarami powinny być umieszczane wcześniej, ponieważ są bardziej szczegółowe niż trasy bez obszaru.
Korzystając z powyższego przykładu, wartości { area = Blog, controller = Users, action = AddUser }
tras są zgodne z następującą akcją:
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}");
}
}
}
Atrybut [Area] określa kontroler w ramach obszaru. Ten kontroler znajduje się w Blog
obszarze. Kontrolery bez atrybutu [Area]
nie są elementami członkowskimi żadnego obszaru i nie są zgodne, gdy area
wartość trasy jest dostarczana przez routing. W poniższym przykładzie tylko pierwszy kontroler wymieniony może odpowiadać wartościom { area = Blog, controller = Users, action = AddUser }
trasy.
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}");
}
}
}
Przestrzeń nazw każdego kontrolera jest wyświetlana tutaj w celu uzyskania kompletności. Jeśli poprzednie kontrolery używały tej samej przestrzeni nazw, zostanie wygenerowany błąd kompilatora. Przestrzenie nazw klas nie mają wpływu na routing MVC.
Dwa pierwsze kontrolery są członkami obszarów i są zgodne tylko wtedy, gdy ich nazwa obszaru jest podana area
przez wartość trasy. Trzeci kontroler nie jest członkiem żadnego obszaru i może być zgodny tylko wtedy, gdy żadna wartość area
nie jest dostarczana przez routing.
Jeśli chodzi o dopasowanie żadnej wartości, brak area
wartości jest taki sam, jak w przypadku wartości area
null lub pustego ciągu.
Podczas wykonywania akcji wewnątrz obszaru wartość trasy jest dostępna jako wartość area
otoczenia dla routingu do użycia na potrzeby generowania adresów URL. Oznacza to, że domyślnie obszary działają lepkie dla generowania adresów URL, jak pokazano w poniższym przykładzie.
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);
}
}
}
Poniższy kod generuje adres URL do /Zebra/Users/AddUser
:
public class HomeController : Controller
{
public IActionResult About()
{
var url = Url.Action("AddUser", "Users", new { Area = "Zebra" });
return Content($"URL: {url}");
}
Definicja akcji
Metody publiczne na kontrolerze, z wyjątkiem tych z atrybutem NonAction , są akcjami.
Przykładowy kod
- MyDisplayRouteInfo jest dostarczany przez pakiet NuGet Rick.Docs.Samples.RouteInfo i wyświetla informacje o trasie.
- Wyświetl lub pobierz przykładowy kod (jak pobrać)
Diagnostyka debugowania
Aby uzyskać szczegółowe dane wyjściowe diagnostyki routingu, ustaw wartość Logging:LogLevel:Microsoft
Debug
. W środowisku deweloperów ustaw poziom dziennika w pliku appsettings.Development.json
:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Debug",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}