Zdarzenia
Power BI DataViz World Championships
14 lut, 16 - 31 mar, 16
Z 4 szans na wejście, można wygrać pakiet konferencji i zrobić go do LIVE Grand Finale w Las Vegas
Dowiedz się więcejTa przeglądarka nie jest już obsługiwana.
Przejdź na przeglądarkę Microsoft Edge, aby korzystać z najnowszych funkcji, aktualizacji zabezpieczeń i pomocy technicznej.
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.
Routing jest odpowiedzialny za dopasowywanie przychodzących żądań HTTP i wysyłanie tych żądań do punktów końcowych wykonywalnych aplikacji. Punkty końcowe to jednostki aplikacji kodu obsługi żądań wykonywalnych. Punkty końcowe są definiowane w aplikacji i konfigurowane podczas uruchamiania aplikacji. Proces dopasowywania punktu końcowego może wyodrębnić wartości z adresu URL żądania i podać te wartości do przetwarzania żądań. Korzystając z informacji o punkcie końcowym z aplikacji, routing jest również w stanie wygenerować adresy URL mapujące na punkty końcowe.
Aplikacje mogą konfigurować routing przy użyciu:
W tym artykule opisano szczegóły niskiego poziomu dotyczące routingu ASP.NET Core. Aby uzyskać informacje na temat konfigurowania routingu:
Poniższy kod przedstawia podstawowy przykład routingu:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Powyższy przykład zawiera pojedynczy punkt końcowy przy użyciu MapGet metody :
GET
do głównego adresu URL /
: Hello World!
jest zapisywany w odpowiedzi HTTP.GET
jest lub główny adres URL nie /
jest , nie jest zgodna trasa i zwracany jest http 404.Routing używa pary oprogramowania pośredniczącego zarejestrowanego przez UseRouting i UseEndpoints:
UseRouting
Dodaje trasę zgodną z potokiem oprogramowania pośredniczącego. To oprogramowanie pośredniczące analizuje zestaw punktów końcowych zdefiniowanych w aplikacji i wybiera najlepsze dopasowanie na podstawie żądania.UseEndpoints
Dodaje wykonywanie punktu końcowego do potoku oprogramowania pośredniczącego. Uruchamia delegata skojarzonego z wybranym punktem końcowym.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
. Jednak aplikacje mogą zmieniać kolejność, w jakiej UseRouting
i UseEndpoints
uruchamiać, wywołując te metody jawnie. Na przykład następujący kod wykonuje jawne wywołanie metody UseRouting
:
app.Use(async (context, next) =>
{
// ...
await next(context);
});
app.UseRouting();
app.MapGet("/", () => "Hello World!");
Powyższy kod:
app.Use
zarejestrowania niestandardowego oprogramowania pośredniczącego uruchamianego na początku potoku.UseRouting
skonfigurowania odpowiedniego oprogramowania pośredniczącego trasy do uruchomienia po niestandardowym oprogramowaniem pośredniczącym.MapGet
przy użyciu jest uruchamiany na końcu potoku.Jeśli poprzedni przykład nie zawierał wywołania metody UseRouting
, niestandardowe oprogramowanie pośredniczące zostanie uruchomione po dopasowaniu trasy oprogramowania pośredniczącego.
Uwaga: trasy dodane bezpośrednio do WebApplication wykonania na końcu potoku.
Metoda MapGet
służy do definiowania punktu końcowego. Punkt końcowy to coś, co może być następujące:
Punkty końcowe, które mogą być dopasowane i wykonywane przez aplikację, są konfigurowane w programie UseEndpoints
. Na przykład metody MapGet, MapPosti podobne łączą delegatów żądań z systemem routingu. Dodatkowe metody mogą służyć do łączenia funkcji platformy ASP.NET Core z systemem routingu:
W poniższym przykładzie przedstawiono routing z bardziej zaawansowanym szablonem trasy:
app.MapGet("/hello/{name:alpha}", (string name) => $"Hello {name}!");
Ciąg /hello/{name:alpha}
jest szablonem trasy. Szablon trasy służy do konfigurowania sposobu dopasowania punktu końcowego. W takim przypadku szablon jest zgodny z następującymi elementami:
/hello/Docs
/hello/
od sekwencji znaków alfabetycznych.
:alpha
stosuje ograniczenie trasy, które pasuje tylko do znaków alfabetycznych.
Ograniczenia tras zostały wyjaśnione w dalszej części tego artykułu.Drugi segment ścieżki adresu URL: {name:alpha}
name
.W poniższym przykładzie przedstawiono routing z kontrolą kondycji i autoryzacją:
app.UseAuthentication();
app.UseAuthorization();
app.MapHealthChecks("/healthz").RequireAuthorization();
app.MapGet("/", () => "Hello World!");
W poprzednim przykładzie pokazano, jak:
Wywołanie MapHealthChecks dodaje punkt końcowy kontroli kondycji. Łączenie RequireAuthorization łańcucha do tego wywołania powoduje dołączenie zasad autoryzacji do punktu końcowego.
Wywoływanie UseAuthentication i UseAuthorization dodawanie oprogramowania pośredniczącego uwierzytelniania i autoryzacji. Te oprogramowanie pośredniczące są umieszczane między elementami UseRouting i UseEndpoints
, dzięki czemu mogą:
UseRouting
element .W poprzednim przykładzie istnieją dwa punkty końcowe, ale tylko punkt końcowy kontroli kondycji ma dołączone zasady autoryzacji. Jeśli żądanie pasuje do punktu końcowego sprawdzania kondycji, /healthz
zostanie wykonane sprawdzanie autoryzacji. Pokazuje to, że punkty końcowe mogą mieć dołączone dodatkowe dane. Te dodatkowe dane są nazywane metadanymi punktu końcowego:
System routingu opiera się na potoku oprogramowania pośredniczącego, dodając zaawansowaną koncepcję punktu końcowego . Punkty końcowe reprezentują jednostki funkcjonalności aplikacji, które różnią się od siebie pod względem routingu, autoryzacji i dowolnej liczby systemów ASP.NET Core.
Punkt końcowy platformy ASP.NET Core to:
Poniższy kod pokazuje, jak pobrać i sprawdzić punkt końcowy pasujący do bieżącego żądania:
app.Use(async (context, next) =>
{
var currentEndpoint = context.GetEndpoint();
if (currentEndpoint is null)
{
await next(context);
return;
}
Console.WriteLine($"Endpoint: {currentEndpoint.DisplayName}");
if (currentEndpoint is RouteEndpoint routeEndpoint)
{
Console.WriteLine($" - Route Pattern: {routeEndpoint.RoutePattern}");
}
foreach (var endpointMetadata in currentEndpoint.Metadata)
{
Console.WriteLine($" - Metadata: {endpointMetadata}");
}
await next(context);
});
app.MapGet("/", () => "Inspect Endpoint.");
Punkt końcowy, jeśli został wybrany, można pobrać z pliku HttpContext
. Jego właściwości można sprawdzić. Obiekty punktu końcowego są niezmienne i nie można ich modyfikować po utworzeniu. Najczęstszym typem RouteEndpointpunktu końcowego jest .
RouteEndpoint
zawiera informacje, które umożliwiają wybranie go przez system routingu.
W poprzednim kodzie aplikacja. Użyj konfiguracji wbudowanego oprogramowania pośredniczącego.
Poniższy kod pokazuje, że w zależności od tego, gdzie app.Use
jest wywoływana w potoku, punkt końcowy może nie być:
// Location 1: before routing runs, endpoint is always null here.
app.Use(async (context, next) =>
{
Console.WriteLine($"1. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
await next(context);
});
app.UseRouting();
// Location 2: after routing runs, endpoint will be non-null if routing found a match.
app.Use(async (context, next) =>
{
Console.WriteLine($"2. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
await next(context);
});
// Location 3: runs when this endpoint matches
app.MapGet("/", (HttpContext context) =>
{
Console.WriteLine($"3. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
return "Hello World!";
}).WithDisplayName("Hello");
app.UseEndpoints(_ => { });
// Location 4: runs after UseEndpoints - will only run if there was no match.
app.Use(async (context, next) =>
{
Console.WriteLine($"4. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
await next(context);
});
Poprzedni przykład dodaje Console.WriteLine
instrukcje, które wyświetlają, czy wybrano punkt końcowy. Aby uzyskać czytelność, przykład przypisuje nazwę wyświetlaną do podanego /
punktu końcowego.
Powyższy przykład obejmuje również wywołania i UseRouting
UseEndpoints
sterowanie dokładnie tym, kiedy oprogramowanie pośredniczące działa w potoku.
Uruchomienie tego kodu z adresem URL wyświetlania /
:
1. Endpoint: (null)
2. Endpoint: Hello
3. Endpoint: Hello
Uruchomienie tego kodu przy użyciu dowolnego innego adresu URL powoduje wyświetlenie:
1. Endpoint: (null)
2. Endpoint: (null)
4. Endpoint: (null)
Te dane wyjściowe pokazują, że:
UseRouting
wywołaniem.UseRouting
i UseEndpoints.UseEndpoints
pośredniczące jest terminalem po znalezieniu dopasowania.
Oprogramowanie pośredniczące terminala jest zdefiniowane w dalszej części tego artykułu.UseEndpoints
wykonaniu tylko wtedy, gdy nie zostanie znalezione dopasowanie.Oprogramowanie UseRouting
pośredniczące używa SetEndpoint metody w celu dołączenia punktu końcowego do bieżącego kontekstu. Można zastąpić UseRouting
oprogramowanie pośredniczące logiką niestandardową i nadal korzystać z zalet korzystania z punktów końcowych. Punkty końcowe są typami pierwotnymi niskiego poziomu, takimi jak oprogramowanie pośredniczące, i nie są powiązane z implementacją routingu. Większość aplikacji nie musi zastępować UseRouting
logiką niestandardową.
Oprogramowanie UseEndpoints
pośredniczące jest przeznaczone do użycia w parze z oprogramowaniem pośredniczącym UseRouting
. Podstawowa logika do wykonania punktu końcowego nie jest skomplikowana. Użyj GetEndpoint polecenia , aby pobrać punkt końcowy, a następnie wywołać jego RequestDelegate właściwość.
Poniższy kod pokazuje, jak oprogramowanie pośredniczące może wpływać na routing lub reagować na nie:
app.UseHttpMethodOverride();
app.UseRouting();
app.Use(async (context, next) =>
{
if (context.GetEndpoint()?.Metadata.GetMetadata<RequiresAuditAttribute>() is not null)
{
Console.WriteLine($"ACCESS TO SENSITIVE DATA AT: {DateTime.UtcNow}");
}
await next(context);
});
app.MapGet("/", () => "Audit isn't required.");
app.MapGet("/sensitive", () => "Audit required for sensitive data.")
.WithMetadata(new RequiresAuditAttribute());
public class RequiresAuditAttribute : Attribute { }
W poprzednim przykładzie przedstawiono dwie ważne pojęcia:
UseRouting
zmodyfikowaniem danych, na których działa routing.
UseRouting
i UseEndpoints w celu przetworzenia wyników routingu przed wykonaniem punktu końcowego.
UseRouting
i UseEndpoints
: UseAuthorization
i UseCors
.Powyższy kod przedstawia przykład niestandardowego oprogramowania pośredniczącego obsługującego zasady poszczególnych punktów końcowych. Oprogramowanie pośredniczące zapisuje dziennik inspekcji dostępu do poufnych danych w konsoli. Oprogramowanie pośredniczące można skonfigurować do inspekcji punktu końcowego za RequiresAuditAttribute
pomocą metadanych. W tym przykładzie pokazano wzorzec zgody, w którym są poddawane inspekcji tylko punkty końcowe oznaczone jako poufne. Tę logikę można zdefiniować w odwrotnej kolejności, przeprowadzając inspekcję wszystkich elementów, które nie są oznaczone jako bezpieczne, na przykład. System metadanych punktu końcowego jest elastyczny. Ta logika może być zaprojektowana w dowolny sposób odpowiednio do przypadku użycia.
Powyższy przykładowy kod ma na celu zademonstrowanie podstawowych pojęć dotyczących punktów końcowych. Przykład nie jest przeznaczony do użytku produkcyjnego. Bardziej kompletna wersja oprogramowania pośredniczącego dziennika inspekcji:
Metadane RequiresAuditAttribute
zasad inspekcji są definiowane jako Attribute
łatwiejsze w użyciu z platformami opartymi na klasach, takimi jak kontrolery i SignalR. W przypadku używania trasy do kodu:
Najlepszym rozwiązaniem dla typów metadanych jest zdefiniowanie ich jako interfejsów lub atrybutów. Interfejsy i atrybuty umożliwiają ponowne użycie kodu. System metadanych jest elastyczny i nie nakłada żadnych ograniczeń.
W poniższym przykładzie pokazano zarówno oprogramowanie pośredniczące terminala, jak i routing:
// Approach 1: Terminal Middleware.
app.Use(async (context, next) =>
{
if (context.Request.Path == "/")
{
await context.Response.WriteAsync("Terminal Middleware.");
return;
}
await next(context);
});
app.UseRouting();
// Approach 2: Routing.
app.MapGet("/Routing", () => "Routing.");
Styl oprogramowania pośredniczącego pokazany za pomocą Approach 1:
programu to oprogramowanie pośredniczące terminalu. Jest to nazywane oprogramowaniem pośredniczącym terminalu, ponieważ wykonuje zgodną operację:
Path == "/"
oprogramowania pośredniczącego i Path == "/Routing"
routingu.next
.Jest to nazywane oprogramowaniem pośredniczącym terminalu, ponieważ kończy wyszukiwanie, wykonuje niektóre funkcje, a następnie zwraca.
Poniższa lista porównuje oprogramowanie pośredniczące terminala z routingiem:
next
UseAuthorization
i UseCors
.
UseAuthorization
oprogramowaniem pośredniczącym lub UseCors
wymaga ręcznego łączenia się z systemem autoryzacji.Punkt końcowy definiuje oba:
Oprogramowanie pośredniczące terminalu może być skutecznym narzędziem, ale może wymagać:
Przed napisaniem oprogramowania pośredniczącego terminalu rozważ integrację z routingiem.
Istniejące oprogramowanie pośredniczące terminalu integrujące się z usługą Map lub MapWhen zwykle może zostać przekształcone w punkt końcowy obsługujący routing. MapHealthChecks demonstruje wzorzec oprogramowania routera:
Map
i podaj nowy potok oprogramowania pośredniczącego.Map
metodę rozszerzenia.Poniższy kod przedstawia użycie narzędzi MapHealthChecks:
app.UseAuthentication();
app.UseAuthorization();
app.MapHealthChecks("/healthz").RequireAuthorization();
W poprzednim przykładzie pokazano, dlaczego zwracanie obiektu konstruktora jest ważne. Zwracanie obiektu konstruktora umożliwia deweloperowi aplikacji konfigurowanie zasad, takich jak autoryzacja dla punktu końcowego. W tym przykładzie oprogramowanie pośredniczące kontroli kondycji nie ma bezpośredniej integracji z systemem autoryzacji.
System metadanych został utworzony w odpowiedzi na problemy napotykane przez autorów rozszerzeń przy użyciu oprogramowania pośredniczącego terminalu. Dla każdego oprogramowania pośredniczącego problematyczne jest zaimplementowanie własnej integracji z systemem autoryzacji.
Po wykonaniu oprogramowania pośredniczącego routingu ustawia Endpoint
ona wartości i kieruje je do funkcji żądania z HttpContext bieżącego żądania:
HttpRequest.RouteValues
pobiera kolekcję wartości tras.Oprogramowanie pośredniczące uruchamiane po uruchomieniu oprogramowania pośredniczącego routingu może sprawdzić punkt końcowy i podjąć działania. Na przykład oprogramowanie pośredniczące autoryzacji może przesłuchić kolekcję metadanych punktu końcowego dla zasad autoryzacji. Po wykonaniu całego oprogramowania pośredniczącego w potoku przetwarzania żądań wywoływany jest delegat wybranego punktu końcowego.
System routingu w routingu punktów końcowych jest odpowiedzialny za wszystkie decyzje dotyczące wysyłania. Ponieważ oprogramowanie pośredniczące stosuje zasady na podstawie wybranego punktu końcowego, ważne jest, aby:
Ostrzeżenie
W przypadku zgodności z poprzednimi wersjami po wykonaniu delegata punktu końcowego kontroler lub Razor strony właściwości RouteContext.RouteData są ustawione na odpowiednie wartości na podstawie przetwarzania żądań wykonanego do tej pory.
Typ RouteContext
zostanie oznaczony jako przestarzały w przyszłej wersji:
RouteData.Values
do HttpRequest.RouteValues
.RouteData.DataTokens
w celu pobrania IDataTokensMetadata z metadanych punktu końcowego.Dopasowywanie adresów URL działa w konfigurowalnym zestawie faz. W każdej fazie dane wyjściowe są zestawem dopasowań. Zestaw dopasowań można zawęzić dalej przez następną fazę. Implementacja routingu nie gwarantuje kolejności przetwarzania pasujących punktów końcowych. Wszystkie możliwe dopasowania są przetwarzane jednocześnie. Fazy dopasowywania adresów URL są wykonywane w następującej kolejności. ASP.NET Core:
Lista punktów końcowych jest priorytetowa zgodnie z:
Wszystkie pasujące punkty końcowe są przetwarzane w każdej fazie do momentu EndpointSelector osiągnięcia. Jest EndpointSelector
to ostatnia faza. Wybiera punkt końcowy o najwyższym priorytcie z dopasowań jako najlepszy. Jeśli istnieją inne dopasowania z tym samym priorytetem co najlepsze dopasowanie, zgłaszany jest niejednoznaczny wyjątek dopasowania.
Pierwszeństwo trasy jest obliczane na podstawie bardziej szczegółowego szablonu trasy o wyższym priorytecie. Rozważmy na przykład szablony /hello
i /{message}
:
/hello
adresu URL ./hello
jest bardziej szczegółowy i dlatego wyższy priorytet.Ogólnie rzecz biorąc, pierwszeństwo trasy dobrze nadaje się do wyboru najlepszego dopasowania do rodzajów schematów adresów URL używanych w praktyce. Używaj Order tylko wtedy, gdy jest to konieczne, aby uniknąć niejednoznaczności.
Ze względu na rodzaj rozszerzalności zapewnianej przez routing nie jest możliwe, aby system routingu obliczał przed upływem czasu niejednoznaczne trasy. Rozważmy przykład, taki jak szablony /{message:alpha}
tras i /{message:int}
:
alpha
pasuje tylko do znaków alfabetycznych.int
pasuje tylko do liczb.Ostrzeżenie
Kolejność operacji wewnątrz UseEndpoints nie ma wpływu na zachowanie routingu z jednym wyjątkiem. MapControllerRoute i MapAreaRoute automatycznie przypisz wartość zamówienia do swoich punktów końcowych na podstawie kolejności, w której są wywoływane. Symuluje to długotrwałe zachowanie kontrolerów bez systemu routingu zapewniające takie same gwarancje jak starsze implementacje routingu.
Routing punktów końcowych w ASP.NET Core:
Pierwszeństwo szablonu trasy to system, który przypisuje każdemu szablonowi trasy wartość na podstawie jego specyfiki. Pierwszeństwo szablonu trasy:
Rozważmy na przykład szablony /Products/List
i /Products/{id}
. Uzasadnione byłoby założenie, że /Products/List
jest to lepsze dopasowanie niż /Products/{id}
dla ścieżki /Products/List
adresu URL . Działa to, ponieważ segment /List
literału jest uznawany za lepszy priorytet niż segment /{id}
parametrów .
Szczegółowe informacje o sposobie działania pierwszeństwa są powiązane z definiowaniem szablonów tras:
Generowanie adresu URL:
Routing punktów końcowych obejmuje LinkGenerator interfejs API.
LinkGenerator
jest pojedynczą usługą dostępną z di. Interfejs LinkGenerator
API może być używany poza kontekstem wykonywania żądania.
Mvc.IUrlHelper i scenariusze, które opierają się na IUrlHelperelementach , takich jak Pomocnicy tagów, Pomocnicy HTML i Wyniki akcji, używają interfejsu LinkGenerator
API wewnętrznie, aby zapewnić możliwości generowania linków.
Generator linków jest wspierany przez koncepcję schematów adresów i adresów. Schemat adresów to sposób określania punktów końcowych, które powinny być brane pod uwagę podczas generowania linków. Na przykład scenariusze nazw tras i wartości tras, które wielu użytkowników zna z kontrolerów i Razor stron, są implementowane jako schemat adresów.
Generator linków może łączyć się z kontrolerami i Razor stronami za pomocą następujących metod rozszerzeń:
Przeciążenia tych metod akceptują argumenty, które obejmują HttpContext
element . Te metody są funkcjonalnie równoważne url.Action i Url.Page, ale oferują dodatkową elastyczność i opcje.
Metody GetPath*
są najbardziej podobne do Url.Action
metod i Url.Page
, w których generują identyfikator URI zawierający ścieżkę bezwzględną. Metody GetUri*
zawsze generują bezwzględny identyfikator URI zawierający schemat i host. Metody, które akceptują HttpContext
generowanie identyfikatora URI w kontekście wykonywania żądania. Wartości trasy otoczenia , ścieżka podstawowa adresu URL, schemat i host z wykonywanego żądania są używane, chyba że zostaną zastąpione.
LinkGenerator jest wywoływana przy użyciu adresu. Generowanie identyfikatora URI odbywa się w dwóch krokach:
Metody udostępniane przez LinkGenerator obsługę standardowych możliwości generowania linków dla dowolnego typu adresu. Najwygodniejszym sposobem korzystania z generatora linków jest użycie metod rozszerzeń, które wykonują operacje dla określonego typu adresu:
Extension, metoda | opis |
---|---|
GetPathByAddress | Generuje identyfikator URI ze ścieżką bezwzględną na podstawie podanych wartości. |
GetUriByAddress | Generuje bezwzględny identyfikator URI na podstawie podanych wartości. |
Ostrzeżenie
Zwróć uwagę na następujące konsekwencje wywoływania LinkGenerator metod:
Użyj GetUri*
metod rozszerzeń z ostrożnością w konfiguracji aplikacji, która nie weryfikuje Host
nagłówka żądań przychodzących.
Host
Jeśli nagłówek żądań przychodzących nie jest weryfikowany, niezaufane dane wejściowe żądania mogą być wysyłane z powrotem do klienta w identyfikatorach URI w widoku lub na stronie. Zalecamy skonfigurowanie serwera przez wszystkie aplikacje produkcyjne w celu zweryfikowania nagłówka Host
pod kątem znanych prawidłowych wartości.
Należy używać LinkGenerator z ostrożnością w oprogramowania pośredniczącego w połączeniu z Map
lub MapWhen
.
Map*
zmienia ścieżkę podstawową wykonywania żądania, co wpływa na dane wyjściowe generowania linków.
LinkGenerator Wszystkie interfejsy API umożliwiają określenie ścieżki podstawowej. Określ pustą ścieżkę podstawową, aby cofnąć wpływ na generowanie linków Map*
.
W poniższym przykładzie oprogramowanie pośredniczące używa interfejsu LinkGenerator API do utworzenia linku do metody akcji zawierającej listę produktów. Użycie generatora linków przez wstrzyknięcie go do klasy i wywołanie GenerateLink
jest dostępne dla dowolnej klasy w aplikacji:
public class ProductsMiddleware
{
private readonly LinkGenerator _linkGenerator;
public ProductsMiddleware(RequestDelegate next, LinkGenerator linkGenerator) =>
_linkGenerator = linkGenerator;
public async Task InvokeAsync(HttpContext httpContext)
{
httpContext.Response.ContentType = MediaTypeNames.Text.Plain;
var productsPath = _linkGenerator.GetPathByAction("Products", "Store");
await httpContext.Response.WriteAsync(
$"Go to {productsPath} to see our products.");
}
}
Tokeny w ramach {}
definicji parametrów trasy, które są powiązane, jeśli trasa jest zgodna. W segmencie trasy można zdefiniować więcej niż jeden parametr trasy, ale parametry trasy muszą być rozdzielone wartością literału. Na przykład:
{controller=Home}{action=Index}
nie jest prawidłową trasą, ponieważ nie ma wartości literału między {controller}
i {action}
. Parametry trasy muszą mieć nazwę i mogą mieć określone dodatkowe atrybuty.
Tekst literału inny niż parametry trasy (na przykład {id}
) i separator /
ścieżki musi być zgodny z tekstem w adresie URL. Dopasowywanie tekstu jest bez uwzględniania wielkości liter i oparte na dekodowanej reprezentacji ścieżki adresu URL. Aby dopasować ogranicznik {
parametru trasy literału lub }
, należy uruchomić ogranicznik, powtarzając znak. Na przykład {{
lub }}
.
Gwiazdka *
lub podwójna gwiazdka **
:
blog/{**slug}
blog/
i ma dowolną wartość po niej.blog/
jest przypisywana do wartości trasy slug .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:
{**slug}"
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.
Parametry catch-all mogą być również zgodne z pustym ciągiem.
Parametr catch-all ucieczki odpowiednich znaków, gdy trasa jest używana do generowania adresu URL, w tym znaków separatora /
ścieżki. Na przykład trasa foo/{*path}
z wartościami { path = "my/path" }
tras generuje foo/my%2Fpath
wartość . Zwróć uwagę na unikniętą ukośnik. Aby użyć znaków separatora ścieżki dwukierunkowej, użyj prefiksu parametru **
trasy. Trasa foo/{**path}
z elementem { path = "my/path" }
generuje foo/my/path
wartość .
Wzorce adresów URL, które próbują przechwycić nazwę pliku z opcjonalnym rozszerzeniem pliku, mają dodatkowe uwagi. Rozważmy na przykład szablon files/{filename}.{ext?}
. Gdy wartości obu filename
i ext
istnieją, oba wartości są wypełniane. Jeśli w adresie URL istnieje tylko wartość filename
, trasa jest zgodna, ponieważ końcowy .
jest opcjonalny. Następujące adresy URL pasują do tej trasy:
/files/myFile.txt
/files/myFile
Parametry trasy mogą mieć wartości domyślne wyznaczone przez określenie wartości domyślnej po nazwie parametru oddzielonej znakiem równości (=
). Na przykład {controller=Home}
definiuje Home
jako wartość domyślną dla elementu controller
. Wartość domyślna jest używana, jeśli w adresie URL parametru nie ma żadnej wartości. Parametry trasy są opcjonalne, dołączając znak zapytania (?
) na końcu nazwy parametru. Na przykład id?
. Różnica między opcjonalnymi wartościami a domyślnymi parametrami trasy to:
Parametry trasy mogą mieć ograniczenia, które muszą być zgodne z wartością trasy powiązaną z adresem URL. Dodanie :
i ograniczenie nazwy po nazwie parametru trasy określa ograniczenie wbudowane w parametrze trasy. Jeśli ograniczenie wymaga argumentów, są one ujęte w nawiasy (...)
po nazwie ograniczenia. Można określić wiele ograniczeń wbudowanych przez dołączenie innej :
nazwy i ograniczenia.
Nazwa ograniczenia i argumenty są przekazywane do IInlineConstraintResolver usługi w celu utworzenia wystąpienia IRouteConstraint do użycia w przetwarzaniu adresów URL. Na przykład szablon blog/{article:minlength(10)}
trasy określa minlength
ograniczenie z argumentem 10
. Aby uzyskać więcej informacji na temat ograniczeń tras i listy ograniczeń udostępnianych przez platformę, zobacz sekcję Ograniczenia trasy.
Parametry trasy mogą również zawierać transformatory parametrów. Transformatory parametrów przekształcają wartość parametru podczas generowania łączy i pasujących akcji i stron do adresów URL. Podobnie jak ograniczenia, transformatory parametrów można dodać w tekście do parametru trasy przez dodanie :
nazwy parametru i transformatora po nazwie parametru trasy. Na przykład szablon blog/{article:slugify}
trasy określa slugify
transformator. Aby uzyskać więcej informacji na temat transformatorów parametrów, zobacz sekcję Transformatory parametrów .
W poniższej tabeli przedstawiono przykładowe szablony tras i ich zachowanie:
Szablon trasy | Przykładowy pasujący identyfikator URI | Identyfikator URI żądania... |
---|---|---|
hello |
/hello |
Pasuje tylko do pojedynczej ścieżki /hello . |
{Page=Home} |
/ |
Dopasuj i ustawia wartość Page Home . |
{Page=Home} |
/Contact |
Dopasuj i ustawia wartość Page Contact . |
{controller}/{action}/{id?} |
/Products/List |
Mapuje na Products kontroler i List akcję. |
{controller}/{action}/{id?} |
/Products/Details/123 |
Mapuje na Products kontroler i Details akcję z ustawioną wartościąid 123. |
{controller=Home}/{action=Index}/{id?} |
/ |
Mapuje na kontroler i Home metodęIndex . Właściwość id jest ignorowana. |
{controller=Home}/{action=Index}/{id?} |
/Products |
Mapuje na kontroler i Products metodęIndex . Właściwość id jest ignorowana. |
Użycie szablonu jest zwykle najprostszym podejściem do routingu. Ograniczenia i wartości domyślne można również określić poza szablonem trasy.
Złożone segmenty są przetwarzane przez dopasowanie ograniczników literału od prawej do lewej w sposób niechłanny . Na przykład [Route("/a{b}c{d}")]
jest to złożony segment.
Złożone segmenty działają w określony sposób, aby można było z nich korzystać pomyślnie. W przykładzie w tej sekcji pokazano, dlaczego złożone segmenty działają prawidłowo tylko wtedy, gdy tekst ogranicznika nie pojawia się wewnątrz wartości parametrów. Użycie wyrażenia regularnego, a następnie ręczne wyodrębnienie wartości jest wymagane w przypadku bardziej złożonych przypadków.
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.
Jest to podsumowanie kroków, które routing wykonuje przy użyciu szablonu /a{b}c{d}
i ścieżki /abcd
adresu URL . Służy |
do wizualizacji sposobu działania algorytmu:
c
. Dlatego /abcd
jest wyszukiwany z prawej strony i znajduje /ab|c|d
.d
) jest teraz dopasowane do parametru {d}
trasy .a
. Dlatego /ab|c|d
jest wyszukiwany, zaczynając od miejsca, w którym odeszliśmy, a następnie a
znajduje się /|a|b|c|d
.b
) jest teraz zgodna z parametrem {b}
trasy .Oto przykład negatywnego przypadku przy użyciu tego samego szablonu /a{b}c{d}
i ścieżki /aabcd
adresu URL . Służy |
do wizualizowania sposobu działania algorytmu. Ten przypadek nie jest zgodny, co zostało wyjaśnione przez ten sam algorytm:
c
. Dlatego /aabcd
jest wyszukiwany z prawej strony i znajduje /aab|c|d
.d
) jest teraz dopasowane do parametru {d}
trasy .a
. Dlatego /aab|c|d
jest wyszukiwany, zaczynając od miejsca, w którym odeszliśmy, a następnie a
znajduje się /a|a|b|c|d
.b
) jest teraz zgodna z parametrem {b}
trasy .a
, ale algorytm zabrakło szablonu trasy do przeanalizowania, więc nie jest to dopasowanie.Ponieważ pasujący algorytm nie jest chciwy:
Wyrażenia regularne zapewniają znacznie większą kontrolę nad ich zachowaniem dopasowania.
Chciwość dopasowania, znana również jako maksymalna próba dopasowania , aby znaleźć najdłuższe możliwe dopasowanie w tekście wejściowym, który spełnia wzorzec wyrażeń regularnych . Niechłanne dopasowanie, znane również jako leniwe dopasowywanie, szuka najkrótszego możliwego dopasowania w tekście wejściowym, który spełnia wzorzec wyrażeń regularnych.
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;
Ograniczenia trasy są wykonywane, gdy wystąpiło dopasowanie do przychodzącego adresu URL, a ścieżka adresu URL jest tokenizowana do wartości tras. Ograniczenia tras zazwyczaj sprawdzają wartość trasy skojarzoną za pośrednictwem szablonu trasy i podejmowania prawdziwej lub fałszywej decyzji o tym, czy wartość jest akceptowalna. Niektóre ograniczenia trasy używają danych poza wartością trasy, aby rozważyć, czy żądanie można kierować. Na przykład HttpMethodRouteConstraint może zaakceptować lub odrzucić żądanie na podstawie jego czasownika HTTP. Ograniczenia są używane w żądaniach routingu i generowaniu linków.
Ostrzeżenie
Nie używaj ograniczeń do walidacji danych wejściowych. Jeśli ograniczenia są używane do walidacji danych wejściowych, nieprawidłowe dane wejściowe będą skutkować odpowiedzią 404
Nie znaleziono. Nieprawidłowe dane wejściowe powinny spowodować wygenerowanie nieprawidłowego 400
żądania z odpowiednim komunikatem o błędzie. Ograniczenia trasy służą do uściślania podobnych tras, a nie sprawdzania poprawności danych wejściowych dla określonej trasy.
W poniższej tabeli przedstawiono przykładowe ograniczenia tras i ich oczekiwane zachowanie:
ograniczenie | Przykład | Przykładowe dopasowania | Uwagi |
---|---|---|---|
int |
{id:int} |
123456789 , -123456789 |
Pasuje do dowolnej liczby całkowitej |
bool |
{active:bool} |
true , FALSE |
Dopasuj lub true false . Bez uwzględniania wielkości liter |
datetime |
{dob:datetime} |
2016-12-31 , 2016-12-31 7:32pm |
Pasuje do prawidłowej DateTime wartości w niezmiennej kulturze. Zobacz poprzednie ostrzeżenie. |
decimal |
{price:decimal} |
49.99 , -1,000.01 |
Pasuje do prawidłowej decimal wartości w niezmiennej kulturze. Zobacz poprzednie ostrzeżenie. |
double |
{weight:double} |
1.234 , -1,001.01e8 |
Pasuje do prawidłowej double wartości w niezmiennej kulturze. Zobacz poprzednie ostrzeżenie. |
float |
{weight:float} |
1.234 , -1,001.01e8 |
Pasuje do prawidłowej float wartości w niezmiennej kulturze. Zobacz poprzednie ostrzeżenie. |
guid |
{id:guid} |
CD2C1638-1638-72D5-1638-DEADBEEF1638 |
Pasuje do prawidłowej Guid wartości |
long |
{ticks:long} |
123456789 , -123456789 |
Pasuje do prawidłowej long wartości |
minlength(value) |
{username:minlength(4)} |
Rick |
Ciąg musi mieć co najmniej 4 znaki |
maxlength(value) |
{filename:maxlength(8)} |
MyFile |
Ciąg nie może zawierać więcej niż 8 znaków |
length(length) |
{filename:length(12)} |
somefile.txt |
Ciąg musi mieć długość dokładnie 12 znaków |
length(min,max) |
{filename:length(8,16)} |
somefile.txt |
Ciąg musi mieć co najmniej 8 znaków i nie więcej niż 16 znaków |
min(value) |
{age:min(18)} |
19 |
Wartość całkowita musi być co najmniej 18 |
max(value) |
{age:max(120)} |
91 |
Wartość całkowita nie może być większa niż 120 |
range(min,max) |
{age:range(18,120)} |
91 |
Wartość całkowita musi być co najmniej 18, ale nie większa niż 120 |
alpha |
{name:alpha} |
Rick |
Ciąg musi składać się z co najmniej jednego znaku a -z alfabetycznego i bez uwzględniania wielkości liter. |
regex(expression) |
{ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)} |
123-45-6789 |
Ciąg musi być zgodny z wyrażeniem regularnym. Zobacz porady dotyczące definiowania wyrażenia regularnego. |
required |
{name:required} |
Rick |
Służy do wymuszania, że wartość nieparametrowa jest obecna podczas generowania adresu URL |
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 ograniczeń rozdzielonych dwukropkami można zastosować do jednego parametru. Na przykład następujące ograniczenie ogranicza parametr do wartości całkowitej 1 lub większej:
[Route("users/{id:int:min(1)}")]
public User GetUserById(int id) { }
Ostrzeżenie
Ograniczenia trasy, które weryfikują adres URL i są konwertowane na typ CLR, zawsze używają niezmiennej kultury. Na przykład konwersja na typ int
CLR lub DateTime
. Te ograniczenia zakładają, że adres URL nie jest lokalizowalny. Ograniczenia trasy dostarczone przez platformę nie modyfikują wartości przechowywanych w wartościach tras. Wszystkie wartości tras analizowane z adresu URL są przechowywane jako ciągi. Na przykład float
ograniczenie próbuje przekonwertować wartość trasy na zmiennoprzecinkowy, ale przekonwertowana wartość jest używana tylko do sprawdzania, czy można ją przekonwertować na zmiennoprzecinkowy.
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.
Wyrażenia regularne można określić jako ograniczenia wbudowane przy użyciu regex(...)
ograniczenia trasy. Metody w MapControllerRoute rodzinie akceptują również literał obiektów ograniczeń. Jeśli ten formularz jest używany, wartości ciągów są interpretowane jako wyrażenia regularne.
Poniższy kod używa wbudowanego ograniczenia wyrażeń regularnych:
app.MapGet("{message:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)}",
() => "Inline Regex Constraint Matched");
Poniższy kod używa literału obiektu do określenia ograniczenia wyrażenia regularnego:
app.MapControllerRoute(
name: "people",
pattern: "people/{ssn}",
constraints: new { ssn = "^\\d{3}-\\d{2}-\\d{4}$", },
defaults: new { controller = "People", action = "List" });
Struktura ASP.NET Core dodaje RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant
do konstruktora wyrażeń regularnych. Zobacz RegexOptions opis tych elementów członkowskich.
Wyrażenia regularne używają ograniczników i tokenów podobnych do tych używanych przez routing i język C#. Tokeny wyrażeń regularnych muszą zostać uniknięci. Aby użyć wyrażenia ^\d{3}-\d{2}-\d{4}$
regularnego w ograniczeniu wbudowanym, użyj jednego z następujących elementów:
\
znaki podane w ciągu jako \\
znaki w pliku źródłowym języka C#, aby uniknąć \
znaku ucieczki ciągu.Aby uniknąć znaków {
ogranicznika parametru routingu , , }
[
, ]
, podwaja znaki w wyrażeniu, na przykład {{
, , }}
, [[
, ]]
. W poniższej tabeli przedstawiono wyrażenie regularne i jego wersję unikniętą:
Regular expression | Wyrażenie regularne o wartości ucieczki |
---|---|
^\d{3}-\d{2}-\d{4}$ |
^\\d{{3}}-\\d{{2}}-\\d{{4}}$ |
^[a-z]{2}$ |
^[[a-z]]{{2}}$ |
Wyrażenia regularne używane w routingu często zaczynają się od ^
znaku i pasują do pozycji początkowej ciągu. Wyrażenia często kończą się znakiem $
i pasują do końca ciągu. Znaki ^
i $
zapewniają, że wyrażenie regularne jest zgodne z całą wartością parametru trasy.
^
Bez znaków i $
wyrażenie regularne pasuje do dowolnego podciągu w ciągu, co jest często niepożądane. W poniższej tabeli przedstawiono przykłady i wyjaśniono, dlaczego są one zgodne lub nie są zgodne:
Wyrażenie | String | Match | Komentarz |
---|---|---|---|
[a-z]{2} |
hello | Tak | Dopasowania podciągów |
[a-z]{2} |
123abc456 | Tak | Dopasowania podciągów |
[a-z]{2} |
mz | Tak | Dopasuj wyrażenie |
[a-z]{2} |
MZ | Tak | Nie uwzględnia wielkości liter |
^[a-z]{2}$ |
hello | Nie. | Zobacz ^ i $ powyżej |
^[a-z]{2}$ |
123abc456 | Nie. | Zobacz ^ i $ powyżej |
Aby uzyskać więcej informacji na temat składni wyrażeń regularnych, zobacz .NET Framework Regular Expressions (Wyrażenia regularne programu .NET Framework).
Aby ograniczyć parametr do znanego zestawu możliwych wartości, użyj wyrażenia regularnego. Na przykład {action:regex(^(list|get|create)$)}
pasuje action
tylko do wartości trasy do list
, get
lub create
. Jeśli zostanie przekazany do słownika ograniczeń, ciąg ^(list|get|create)$
jest równoważny. Ograniczenia przekazywane w słowniku ograniczeń, które nie są zgodne z jednym ze znanych ograniczeń, są również traktowane jako wyrażenia regularne. Ograniczenia przekazywane w szablonie, które nie pasują do jednego ze znanych ograniczeń, nie są traktowane jako wyrażenia regularne.
Ograniczenia trasy niestandardowej można utworzyć przez zaimplementowanie interfejsu IRouteConstraint . Interfejs IRouteConstraint
zawiera Matchwartość , która zwraca true
wartość, jeśli ograniczenie jest spełnione i false
w przeciwnym razie.
Ograniczenia trasy niestandardowej są rzadko potrzebne. Przed zaimplementowaniem ograniczenia trasy niestandardowej rozważ alternatywy, takie jak powiązanie modelu.
Folder ASP.NET Core Constraints zawiera dobre przykłady tworzenia ograniczeń. Na przykład GuidRouteConstraint.
Aby użyć niestandardowego IRouteConstraint
, typ ograniczenia trasy musi być zarejestrowany w aplikacji ConstraintMap w kontenerze usługi. Jest ConstraintMap
to słownik, który mapuje klucze ograniczeń trasy na IRouteConstraint
implementacje, które weryfikują te ograniczenia. Aplikację ConstraintMap
można zaktualizować w Program.cs
ramach AddRouting wywołania lub konfigurując bezpośrednio za pomocą RouteOptionspolecenia builder.Services.Configure<RouteOptions>
. Na przykład:
builder.Services.AddRouting(options =>
options.ConstraintMap.Add("noZeroes", typeof(NoZeroesRouteConstraint)));
Powyższe ograniczenie jest stosowane w następującym kodzie:
[ApiController]
[Route("api/[controller]")]
public class NoZeroesController : ControllerBase
{
[HttpGet("{id:noZeroes}")]
public IActionResult Get(string id) =>
Content(id);
}
Implementacja elementu NoZeroesRouteConstraint
uniemożliwia 0
korzystanie z parametru trasy:
public class NoZeroesRouteConstraint : IRouteConstraint
{
private static readonly Regex _regex = new(
@"^[1-9]*$",
RegexOptions.CultureInvariant | RegexOptions.IgnoreCase,
TimeSpan.FromMilliseconds(100));
public bool Match(
HttpContext? httpContext, IRouter? route, string routeKey,
RouteValueDictionary values, RouteDirection routeDirection)
{
if (!values.TryGetValue(routeKey, out var routeValue))
{
return false;
}
var routeValueString = Convert.ToString(routeValue, CultureInfo.InvariantCulture);
if (routeValueString is null)
{
return false;
}
return _regex.IsMatch(routeValueString);
}
}
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.
Powyższy kod ma następujące działanie:
0
w {id}
segmencie trasy.Poniższy kod jest lepszym podejściem do zapobiegania przetwarzaniu elementu zawierającego id
0
element:
[HttpGet("{id}")]
public IActionResult Get(string id)
{
if (id.Contains('0'))
{
return StatusCode(StatusCodes.Status406NotAcceptable);
}
return Content(id);
}
Powyższy kod ma następujące zalety w porównaniu z podejściem NoZeroesRouteConstraint
:
0
wartość .Transformatory parametrów:
Na przykład funkcja przekształcania parametrów niestandardowych slugify
we wzorcu blog\{article:slugify}
trasy z poleceniem Url.Action(new { article = "MyTestArticle" })
generuje blog\my-test-article
wartość .
Rozważmy następującą IOutboundParameterTransformer
implementację:
public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
public string? TransformOutbound(object? value)
{
if (value is null)
{
return null;
}
return Regex.Replace(
value.ToString()!,
"([a-z])([A-Z])",
"$1-$2",
RegexOptions.CultureInvariant,
TimeSpan.FromMilliseconds(100))
.ToLowerInvariant();
}
}
Aby użyć transformatora parametrów we wzorcu trasy, skonfiguruj go przy użyciu ConstraintMap polecenia w pliku Program.cs
:
builder.Services.AddRouting(options =>
options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer));
Struktura ASP.NET Core używa funkcji przekształcania parametrów w celu przekształcenia identyfikatora URI, w którym punkt końcowy jest rozpoznawany. Na przykład transformatory parametrów przekształcają wartości tras używane do dopasowania area
wartości , , controller
action
i page
:
app.MapControllerRoute(
name: "default",
pattern: "{controller:slugify=Home}/{action:slugify=Index}/{id?}");
W przypadku poprzedniego szablonu trasy akcja SubscriptionManagementController.GetAll
jest zgodna z identyfikatorem URI /subscription-management/get-all
. Funkcja przekształcania parametrów nie zmienia wartości tras używanych do generowania łącza. Na przykład Url.Action("GetAll", "SubscriptionManagement")
dane wyjściowe ./subscription-management/get-all
ASP.NET Core udostępnia konwencje interfejsu API dotyczące używania transformatorów parametrów z wygenerowanymi trasami:
Ta sekcja zawiera odwołanie do algorytmu zaimplementowanego przez generowanie adresów URL. W praktyce większość złożonych przykładów generowania adresów URL używa kontrolerów lub Razor stron. Aby uzyskać dodatkowe informacje, zobacz routing w kontrolerach .
Proces generowania adresu URL rozpoczyna się od wywołania metody LinkGenerator.GetPathByAddress lub podobnej metody. Metoda jest dostarczana z adresem, zestawem wartości tras i opcjonalnie informacjami o bieżącym żądaniu z .HttpContext
Pierwszym krokiem jest użycie adresu w celu rozpoznania zestawu punktów końcowych kandydata przy użyciu elementu zgodnego z typem IEndpointAddressScheme<TAddress> adresu.
Po znalezieniu zestawu kandydatów przez schemat adresów punkty końcowe są uporządkowane i przetwarzane iteracyjnie, dopóki operacja generowania adresu URL nie powiedzie się. Generowanie adresu URL nie sprawdza niejednoznaczności. Zwracany pierwszy wynik jest wynikiem końcowym.
Pierwszym krokiem generowania adresu URL rozwiązywania problemów jest ustawienie poziomu rejestrowania Microsoft.AspNetCore.Routing
na TRACE
.
LinkGenerator
rejestruje wiele szczegółów dotyczących jego przetwarzania, które mogą być przydatne do rozwiązywania problemów.
Aby uzyskać szczegółowe informacje na temat generowania adresów URL, zobacz Dokumentację generowania adresów URL.
Adresy to koncepcja generowania adresów URL używana do powiązania wywołania z generatorem linków z zestawem kandydatów punktów końcowych.
Adresy to rozszerzalna koncepcja, która domyślnie zawiera dwie implementacje:
string
) jako adresu: IUrlHelper
pomocników tagów, pomocników HTML, wyniki akcji itp.Rolą schematu adresów jest skojarzenie adresu i pasujących punktów końcowych według dowolnych kryteriów:
Z bieżącego żądania routing uzyskuje dostęp do wartości tras bieżącego żądania HttpContext.Request.RouteValues
. Wartości skojarzone z bieżącym żądaniem są określane jako wartości otoczenia. W celu jasności dokumentacja odnosi się do wartości tras przekazywanych do metod jako jawnych wartości.
W poniższym przykładzie przedstawiono wartości otoczenia i jawne wartości. Zapewnia ona wartości otoczenia z bieżącego żądania i jawnych wartości:
public class WidgetController : ControllerBase
{
private readonly LinkGenerator _linkGenerator;
public WidgetController(LinkGenerator linkGenerator) =>
_linkGenerator = linkGenerator;
public IActionResult Index()
{
var indexPath = _linkGenerator.GetPathByAction(
HttpContext, values: new { id = 17 })!;
return Content(indexPath);
}
// ...
Powyższy kod ma następujące działanie:
/Widget/Index/17
Poniższy kod zawiera tylko jawne wartości i nie ma wartości otoczenia:
var subscribePath = _linkGenerator.GetPathByAction(
"Subscribe", "Home", new { id = 17 })!;
Poprzednia metoda zwraca /Home/Subscribe/17
Poniższy kod w zwracaniu WidgetController
wartości /Widget/Subscribe/17
:
var subscribePath = _linkGenerator.GetPathByAction(
HttpContext, "Subscribe", null, new { id = 17 });
Poniższy kod udostępnia kontroler z wartości otoczenia w bieżącym żądaniu i jawnych wartościach:
public class GadgetController : ControllerBase
{
public IActionResult Index() =>
Content(Url.Action("Edit", new { id = 17 })!);
}
Powyższy kod:
/Gadget/Edit/17
jest zwracany.action
nazwę i route
wartości.Poniższy kod zawiera wartości otoczenia z bieżącego żądania i jawnych wartości:
public class IndexModel : PageModel
{
public void OnGet()
{
var editUrl = Url.Page("./Edit", new { id = 17 });
// ...
}
}
Powyższy kod ustawia wartość url
, /Edit/17
gdy strona edycji Razor zawiera następującą dyrektywę strony:
@page "{id:int}"
Jeśli strona Edycja nie zawiera szablonu "{id:int}"
trasy, url
to /Edit?id=17
.
Zachowanie wzorca MVC IUrlHelper dodaje warstwę złożoności oprócz reguł opisanych tutaj:
IUrlHelper
zawsze udostępnia wartości tras z bieżącego żądania jako wartości otoczenia.action
wartości i controller
trasy jako wartości jawne, chyba że zostanie zastąpione przez dewelopera.page
wartość trasy jako jawną wartość, chyba że zostanie zastąpiona. IUrlHelper.Page
zawsze zastępuje bieżącą handler
wartość trasy wartością null
jawną, chyba że zostanie zastąpiona.Użytkownicy są często zaskoczeni szczegółami zachowania wartości otoczenia, ponieważ MVC nie wydaje się przestrzegać własnych reguł. Ze względów historycznych i zgodności niektóre wartości tras, takie jak action
, controller
, page
i handler
mają własne zachowanie specjalne.
Równoważna funkcjonalność zapewniana przez LinkGenerator.GetPathByAction
program i LinkGenerator.GetPathByPage
duplikuje te anomalie w IUrlHelper
celu zapewnienia zgodności.
Po znalezieniu zestawu punktów końcowych kandydata algorytm generowania adresów URL:
Pierwszym krokiem w tym procesie jest unieważnienie wartości trasy. Unieważnienie wartości trasy to proces, za pomocą którego routing decyduje, które wartości tras z wartości otoczenia powinny być używane i które powinny być ignorowane. Każda wartość otoczenia jest uwzględniana i połączona z jawnymi wartościami lub ignorowana.
Najlepszym sposobem myślenia o roli wartości otoczenia jest to, że próbują zapisać deweloperów aplikacji wpisywanie, w niektórych typowych przypadkach. Tradycyjnie scenariusze, w których wartości otoczenia są przydatne, są związane z MVC:
Wywołania do LinkGenerator
lub IUrlHelper
tego zwracania null
są zwykle spowodowane przez niezrozumienie unieważnienia wartości trasy. Rozwiąż problemy z nieprawidłową wartością trasy, określając jawnie więcej wartości trasy, aby sprawdzić, czy rozwiązuje to problem.
Unieważnienie wartości trasy działa zgodnie z założeniem, że schemat adresu URL aplikacji jest hierarchiczny, z hierarchią utworzoną od lewej do prawej. Rozważmy podstawowy szablon {controller}/{action}/{id?}
trasy kontrolera, aby uzyskać intuicyjne zrozumienie sposobu działania tej metody w praktyce.
Zmiana wartości unieważnia wszystkie wartości trasy, które są wyświetlane po prawej stronie. Odzwierciedla to założenie dotyczące hierarchii. Jeśli aplikacja ma wartość otoczenia dla id
, a operacja określa inną wartość dla elementu controller
:
id
nie będzie ponownie używany, ponieważ {controller}
znajduje się po lewej {id?}
stronie .Kilka przykładów demonstrujących tę zasadę:
id
, wartość otoczenia dla id
elementu jest ignorowana. Wartości otoczenia i controller
action
mogą być używane.action
, każda wartość otoczenia dla action
elementu jest ignorowana. Można użyć wartości controller
otoczenia. Jeśli jawna wartość parametru action
jest inna niż wartość otoczenia dla action
wartości , id
wartość nie zostanie użyta. Jeśli jawna wartość parametru action
jest taka sama jak wartość otoczenia dla action
, id
można użyć wartości .controller
, każda wartość otoczenia dla controller
elementu jest ignorowana. Jeśli jawna wartość parametru controller
jest inna niż wartość otoczenia dla controller
, action
wartości i id
nie będą używane. Jeśli jawna wartość parametru controller
jest taka sama jak wartość otoczenia dla controller
, action
można użyć wartości i id
.Ten proces jest jeszcze bardziej skomplikowany przez istnienie tras atrybutów i dedykowanych tras konwencjonalnych. Kontroler konwencjonalnych tras, takich jak {controller}/{action}/{id?}
określanie hierarchii przy użyciu parametrów trasy. W przypadku dedykowanych tras konwencjonalnych i tras atrybutów do kontrolerów i Razor stron:
W takich przypadkach generowanie adresów URL definiuje koncepcję wymaganych wartości . Punkty końcowe utworzone przez kontrolery i Razor strony mają wymagane wartości określone, które umożliwiają działanie unieważnienia wartości trasy.
Algorytm unieważniania wartości trasy szczegółowo:
W tym momencie operacja generowania adresu URL jest gotowa do oceny ograniczeń trasy. Zestaw akceptowanych wartości jest połączony z wartościami domyślnymi parametrów, które są dostarczane do ograniczeń. Jeśli wszystkie ograniczenia przechodzą, operacja będzie kontynuowana.
Następnie akceptowane wartości mogą służyć do rozszerzania szablonu trasy. Szablon trasy jest przetwarzany:
Wartości jawnie podane, które nie pasują do segmentu trasy, są dodawane do ciągu zapytania. W poniższej tabeli przedstawiono wynik użycia szablonu {controller}/{action}/{id?}
trasy .
Wartości otoczenia | Jawne wartości | Result |
---|---|---|
controller = "Home" | action = "Informacje" | /Home/About |
controller = "Home" | controller = "Order", action = "About" | /Order/About |
controller = "Home", color = "Red" | action = "Informacje" | /Home/About |
controller = "Home" | action = "About", color = "Red" | /Home/About?color=Red |
Opcjonalne parametry trasy muszą pochodzić po wszystkich wymaganych parametrach i literałach trasy. W poniższym kodzie id
parametry i name
muszą pochodzić po parametrze color
:
using Microsoft.AspNetCore.Mvc;
namespace WebApplication1.Controllers;
[Route("api/[controller]")]
public class MyController : ControllerBase
{
// GET /api/my/red/2/joe
// GET /api/my/red/2
// GET /api/my
[HttpGet("{color}/{id:int?}/{name?}")]
public IActionResult GetByIdAndOptionalName(string color, int id = 1, string? name = null)
{
return Ok($"{color} {id} {name ?? ""}");
}
}
Poniższy kod przedstawia przykład schematu generowania adresów URL, który nie jest obsługiwany przez routing:
app.MapControllerRoute(
"default",
"{culture}/{controller=Home}/{action=Index}/{id?}");
app.MapControllerRoute(
"blog",
"{culture}/{**slug}",
new { controller = "Blog", action = "ReadPost" });
W poprzednim kodzie culture
parametr trasy jest używany do lokalizacji. Pragnieniem jest, culture
aby parametr zawsze był akceptowany jako wartość otoczenia.
culture
Jednak parametr nie jest akceptowany jako wartość otoczenia ze względu na sposób działania wymaganych wartości:
"default"
culture
trasy parametr trasy znajduje się po lewej stronie controller
elementu , więc zmiany controller
nie będą unieważniać culture
elementu ."blog"
culture
trasy parametr trasy jest uznawany za znajdujący się po prawej stronie controller
elementu , który jest wyświetlany w wymaganych wartościach.Klasa LinkParser dodaje obsługę analizowania ścieżki adresu URL do zestawu wartości tras. Metoda ParsePathByEndpointName przyjmuje nazwę punktu końcowego i ścieżkę adresu URL i zwraca zestaw wartości tras wyodrębnionych ze ścieżki adresu URL.
W poniższym przykładowym kontrolerze akcja GetProduct
używa szablonu api/Products/{id}
trasy i ma NameGetProduct
wartość :
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
[HttpGet("{id}", Name = nameof(GetProduct))]
public IActionResult GetProduct(string id)
{
// ...
W tej samej klasie AddRelatedProduct
kontrolera akcja oczekuje ścieżki adresu URL , pathToRelatedProduct
która może być podana jako parametr ciągu zapytania:
[HttpPost("{id}/Related")]
public IActionResult AddRelatedProduct(
string id, string pathToRelatedProduct, [FromServices] LinkParser linkParser)
{
var routeValues = linkParser.ParsePathByEndpointName(
nameof(GetProduct), pathToRelatedProduct);
var relatedProductId = routeValues?["id"];
// ...
W poprzednim przykładzie akcja AddRelatedProduct
wyodrębnia id
wartość trasy ze ścieżki adresu URL. Na przykład ze ścieżką /api/Products/1
relatedProductId
adresu URL wartości jest ustawiona wartość 1
. Takie podejście umożliwia klientom interfejsu API używanie ścieżek url podczas odwoływania się do zasobów bez konieczności znajomości struktury takiego adresu URL.
Poniższe linki zawierają informacje na temat konfigurowania metadanych punktu końcowego:
[MinimumAgeAuthorize]
RequireHost stosuje ograniczenie do trasy, która wymaga określonego hosta. Parametr RequireHost
lub [Host] może być:
www.domain.com
, pasuje www.domain.com
do dowolnego portu.*.domain.com
, pasuje www.domain.com
do , subdomain.domain.com
lub www.subdomain.domain.com
na dowolnym porcie.*:5000
, pasuje do portu 5000 z dowolnym hostem.www.domain.com:5000
lub *.domain.com:5000
, pasuje do hosta i portu.Można określić wiele parametrów przy użyciu polecenia RequireHost
lub [Host]
. Ograniczenie pasuje do hostów prawidłowych dla dowolnego z parametrów. Na przykład pasuje [Host("domain.com", "*.domain.com")]
do domain.com
, www.domain.com
i subdomain.domain.com
.
Poniższy kod używa RequireHost
metody , aby wymagać określonego hosta na trasie:
app.MapGet("/", () => "Contoso").RequireHost("contoso.com");
app.MapGet("/", () => "AdventureWorks").RequireHost("adventure-works.com");
app.MapHealthChecks("/healthz").RequireHost("*:8080");
Poniższy kod używa atrybutu [Host]
na kontrolerze, aby wymagać któregokolwiek z określonych hostów:
[Host("contoso.com", "adventure-works.com")]
public class HostsController : Controller
{
public IActionResult Index() =>
View();
[Host("example.com")]
public IActionResult Example() =>
View();
}
Po zastosowaniu atrybutu [Host]
zarówno do kontrolera, jak i metody akcji:
Ostrzeżenie
Interfejs API, który opiera się na nagłówku hosta, takim jak HttpRequest.Host i RequireHost, podlega potencjalnemu fałszowaniu przez klientów.
Aby zapobiec fałszowaniu hostów i portów, użyj jednej z następujących metod:
Metoda MapGroup rozszerzenia ułatwia organizowanie grup punktów końcowych za pomocą wspólnego prefiksu. Zmniejsza powtarzalny kod i umożliwia dostosowywanie całych grup punktów końcowych za pomocą jednego wywołania metod, takich jak RequireAuthorization i WithMetadata które dodają metadane punktu końcowego.
Na przykład poniższy kod tworzy dwie podobne grupy punktów końcowych:
app.MapGroup("/public/todos")
.MapTodosApi()
.WithTags("Public");
app.MapGroup("/private/todos")
.MapTodosApi()
.WithTags("Private")
.AddEndpointFilterFactory(QueryPrivateTodos)
.RequireAuthorization();
EndpointFilterDelegate QueryPrivateTodos(EndpointFilterFactoryContext factoryContext, EndpointFilterDelegate next)
{
var dbContextIndex = -1;
foreach (var argument in factoryContext.MethodInfo.GetParameters())
{
if (argument.ParameterType == typeof(TodoDb))
{
dbContextIndex = argument.Position;
break;
}
}
// Skip filter if the method doesn't have a TodoDb parameter.
if (dbContextIndex < 0)
{
return next;
}
return async invocationContext =>
{
var dbContext = invocationContext.GetArgument<TodoDb>(dbContextIndex);
dbContext.IsPrivate = true;
try
{
return await next(invocationContext);
}
finally
{
// This should only be relevant if you're pooling or otherwise reusing the DbContext instance.
dbContext.IsPrivate = false;
}
};
}
public static RouteGroupBuilder MapTodosApi(this RouteGroupBuilder group)
{
group.MapGet("/", GetAllTodos);
group.MapGet("/{id}", GetTodo);
group.MapPost("/", CreateTodo);
group.MapPut("/{id}", UpdateTodo);
group.MapDelete("/{id}", DeleteTodo);
return group;
}
W tym scenariuszu możesz użyć względnego adresu nagłówka Location
w 201 Created
wyniku:
public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
await database.AddAsync(todo);
await database.SaveChangesAsync();
return TypedResults.Created($"{todo.Id}", todo);
}
Pierwsza grupa punktów końcowych będzie pasowała tylko do żądań z prefiksem /public/todos
i jest dostępna bez żadnego uwierzytelniania. Druga grupa punktów końcowych będzie pasowała tylko do żądań poprzedzonych prefiksem /private/todos
i wymaga uwierzytelniania.
QueryPrivateTodos
Fabryka filtrów punktów końcowych to funkcja lokalna, która modyfikuje parametry programu obsługi TodoDb
tras, aby umożliwić dostęp do prywatnych danych zadań do wykonania i przechowywanie ich.
Grupy tras obsługują również grupy zagnieżdżone i złożone wzorce prefiksów z parametrami i ograniczeniami trasy. W poniższym przykładzie program obsługi tras zamapowany na grupę user
może przechwytywać {org}
parametry trasy i {group}
zdefiniowane w prefiksach grup zewnętrznych.
Prefiks może być również pusty. Może to być przydatne w przypadku dodawania metadanych lub filtrów punktu końcowego do grupy punktów końcowych bez zmiany wzorca trasy.
var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");
Dodanie filtrów lub metadanych do grupy działa tak samo jak dodawanie ich indywidualnie do każdego punktu końcowego przed dodaniem dodatkowych filtrów lub metadanych, które mogły zostać dodane do grupy wewnętrznej lub określonego punktu końcowego.
var outer = app.MapGroup("/outer");
var inner = outer.MapGroup("/inner");
inner.AddEndpointFilter((context, next) =>
{
app.Logger.LogInformation("/inner group filter");
return next(context);
});
outer.AddEndpointFilter((context, next) =>
{
app.Logger.LogInformation("/outer group filter");
return next(context);
});
inner.MapGet("/", () => "Hi!").AddEndpointFilter((context, next) =>
{
app.Logger.LogInformation("MapGet filter");
return next(context);
});
W powyższym przykładzie zewnętrzny filtr będzie rejestrować żądanie przychodzące przed filtrem wewnętrznym, mimo że został dodany w sekundzie. Ponieważ filtry zostały zastosowane do różnych grup, kolejność, którą zostały dodane względem siebie, nie ma znaczenia. Filtry kolejności są dodawane niezależnie od tego, czy są stosowane do tej samej grupy lub określonego punktu końcowego.
Żądanie, aby zarejestrować /outer/inner/
następujące elementy:
/outer group filter
/inner group filter
MapGet filter
Gdy aplikacja ma problemy z wydajnością, routing jest często podejrzewany jako problem. Podejrzewa się, że routing polega na tym, że struktury, takie jak kontrolery i Razor strony, zgłaszają ilość czasu spędzonego w strukturze w komunikatach rejestrowania. W przypadku znacznej różnicy między czasem zgłoszonym przez kontrolery a łącznym czasem żądania:
Routing jest testowany pod kątem wydajności przy użyciu tysięcy punktów końcowych. Jest mało prawdopodobne, aby typowa aplikacja napotkała problem z wydajnością, ponieważ jest zbyt duża. Najczęstszą główną przyczyną niskiej wydajności routingu jest zwykle nieprawidłowe zachowanie niestandardowego oprogramowania pośredniczącego.
W poniższym przykładzie kodu przedstawiono podstawową technikę zawężania źródła opóźnienia:
var logger = app.Services.GetRequiredService<ILogger<Program>>();
app.Use(async (context, next) =>
{
var stopwatch = Stopwatch.StartNew();
await next(context);
stopwatch.Stop();
logger.LogInformation("Time 1: {ElapsedMilliseconds}ms", stopwatch.ElapsedMilliseconds);
});
app.UseRouting();
app.Use(async (context, next) =>
{
var stopwatch = Stopwatch.StartNew();
await next(context);
stopwatch.Stop();
logger.LogInformation("Time 2: {ElapsedMilliseconds}ms", stopwatch.ElapsedMilliseconds);
});
app.UseAuthorization();
app.Use(async (context, next) =>
{
var stopwatch = Stopwatch.StartNew();
await next(context);
stopwatch.Stop();
logger.LogInformation("Time 3: {ElapsedMilliseconds}ms", stopwatch.ElapsedMilliseconds);
});
app.MapGet("/", () => "Timing Test.");
Routing czasowy:
Jest to podstawowy sposób zawężenia opóźnienia, gdy jest to znaczące, na przykład więcej niż 10ms
. Odejmowanie Time 2
z Time 1
raportów czasu spędzonego wewnątrz oprogramowania pośredniczącego UseRouting
.
Poniższy kod używa bardziej kompaktowego podejścia do poprzedniego kodu chronometrażu:
public sealed class AutoStopwatch : IDisposable
{
private readonly ILogger _logger;
private readonly string _message;
private readonly Stopwatch _stopwatch;
private bool _disposed;
public AutoStopwatch(ILogger logger, string message) =>
(_logger, _message, _stopwatch) = (logger, message, Stopwatch.StartNew());
public void Dispose()
{
if (_disposed)
{
return;
}
_logger.LogInformation("{Message}: {ElapsedMilliseconds}ms",
_message, _stopwatch.ElapsedMilliseconds);
_disposed = true;
}
}
var logger = app.Services.GetRequiredService<ILogger<Program>>();
var timerCount = 0;
app.Use(async (context, next) =>
{
using (new AutoStopwatch(logger, $"Time {++timerCount}"))
{
await next(context);
}
});
app.UseRouting();
app.Use(async (context, next) =>
{
using (new AutoStopwatch(logger, $"Time {++timerCount}"))
{
await next(context);
}
});
app.UseAuthorization();
app.Use(async (context, next) =>
{
using (new AutoStopwatch(logger, $"Time {++timerCount}"))
{
await next(context);
}
});
app.MapGet("/", () => "Timing Test.");
Poniższa lista zawiera wgląd w funkcje routingu, które są stosunkowo kosztowne w porównaniu z podstawowymi szablonami tras:
{x}-{y}-{z}
): Domyślnie ASP.NET Core używa algorytmu routingu, który wymienia pamięć na czas procesora CPU. Ma to miły wpływ, że czas dopasowywania trasy zależy tylko od długości ścieżki do dopasowania, a nie liczby tras. Jednak takie podejście może być potencjalnie problematyczne w niektórych przypadkach, gdy aplikacja ma dużą liczbę tras (w tysiącach) i istnieje duża ilość zmiennych prefiksów w trasach. Jeśli na przykład trasy mają parametry we wczesnych segmentach trasy, na przykład {parameter}/some/literal
.
Jest mało prawdopodobne, aby aplikacja napotkała sytuację, w której jest to problem, chyba że:
Microsoft.AspNetCore.Routing.Matching.DfaNode
wystąpień.Istnieje kilka technik i optymalizacji, które można zastosować do tras, które w dużej mierze usprawniają ten scenariusz:
{parameter:int}
, {parameter:guid}
, {parameter:regex(\\d+)}
itp., jeśli to możliwe.
MapDynamicControllerRoute
i MapDynamicPageRoute
.Kiedy routing odpowiada punktowi końcowemu, zazwyczaj pozwala na wykonanie reszty potoka middleware przed wywołaniem logiki punktu końcowego. Usługi mogą zmniejszyć użycie zasobów przez odfiltrowanie znanych żądań na początku potoku.
ShortCircuit Użyj metody rozszerzenia, aby natychmiast wywołać logikę punktu końcowego, a następnie zakończyć żądanie. Na przykład dana trasa może nie wymagać uwierzytelniania lub oprogramowania pośredniczącego CORS. Następujące przykładowe żądania zwarć zgodne z trasą /short-circuit
:
app.MapGet("/short-circuit", () => "Short circuiting!").ShortCircuit();
Metoda ShortCircuit(IEndpointConventionBuilder, Nullable<Int32>) może opcjonalnie przyjąć kod stanu.
MapShortCircuit Użyj metody , aby skonfigurować zwarcie dla wielu tras jednocześnie, przekazując do niej tablicę params prefiksów adresów URL. Na przykład przeglądarki i boty często sonduje serwery pod kątem dobrze znanych ścieżek, takich jak robots.txt
i favicon.ico
. Jeśli aplikacja nie ma tych plików, jeden wiersz kodu może skonfigurować obie trasy:
app.MapShortCircuit(404, "robots.txt", "favicon.ico");
MapShortCircuit
funkcja zwraca IEndpointConventionBuilder , aby można było dodać do niego dodatkowe ograniczenia tras, takie jak filtrowanie hostów.
Metody ShortCircuit
i MapShortCircuit
nie mają wpływu na oprogramowanie pośredniczące umieszczone przed UseRouting
. Próba użycia tych metod z punktami końcowymi, które mają również [Authorize]
metadane, [RequireCors]
spowoduje niepowodzenie żądań z elementem InvalidOperationException
. Te metadane są stosowane przez [Authorize]
atrybuty lub [EnableCors]
RequireCors metody lub metodyRequireAuthorization.
Aby zobaczyć efekt oprogramowania pośredniczącego zwarciowego, ustaw kategorię rejestrowania "Microsoft" na "Informacje" w pliku appsettings.Development.json
:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
Uruchom następujący kod:
var app = WebApplication.Create();
app.UseHttpLogging();
app.MapGet("/", () => "No short-circuiting!");
app.MapGet("/short-circuit", () => "Short circuiting!").ShortCircuit();
app.MapShortCircuit(404, "robots.txt", "favicon.ico");
app.Run();
Poniższy przykład pochodzi z dzienników konsoli utworzonych przez uruchomienie punktu końcowego /
. Zawiera dane wyjściowe oprogramowania pośredniczącego rejestrowania:
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint 'HTTP: GET /'
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Executed endpoint 'HTTP: GET /'
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[2]
Response:
StatusCode: 200
Content-Type: text/plain; charset=utf-8
Date: Wed, 03 May 2023 21:05:59 GMT
Server: Kestrel
Alt-Svc: h3=":5182"; ma=86400
Transfer-Encoding: chunked
Poniższy przykład pochodzi z uruchamiania punktu końcowego /short-circuit
. Nie ma nic z oprogramowania pośredniczącego rejestrowania, ponieważ oprogramowanie pośredniczące zostało zwarcie:
info: Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware[4]
The endpoint 'HTTP: GET /short-circuit' is being executed without running additional middleware.
info: Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware[5]
The endpoint 'HTTP: GET /short-circuit' has been executed without running additional middleware.
Ta sekcja zawiera wskazówki dotyczące autorów bibliotek opartych na routingu. Te szczegóły mają na celu zapewnienie, że deweloperzy aplikacji mają dobre środowisko korzystania z bibliotek i struktur rozszerzających routing.
Aby utworzyć strukturę korzystającą z routingu do dopasowywania adresów URL, zacznij od zdefiniowania środowiska użytkownika, które opiera się na systemie UseEndpoints.
Kompilacja DO na podstawie funkcji IEndpointRouteBuilder. Dzięki temu użytkownicy mogą tworzyć struktury przy użyciu innych funkcji ASP.NET Core bez pomyłek. Każdy szablon ASP.NET Core zawiera routing. Załóżmy, że routing jest obecny i znany użytkownikom.
// Your framework
app.MapMyFramework(...);
app.MapHealthChecks("/healthz");
NALEŻY zwrócić zapieczętowany typ betonowy z wywołania, do MapMyFramework(...)
którego implementuje IEndpointConventionBuilderelement . Większość metod struktury Map...
jest zgodne z tym wzorcem. Interfejs IEndpointConventionBuilder
:
Deklarowanie własnego typu umożliwia dodanie do konstruktora własnych funkcji specyficznych dla platformy. Można zawinąć konstruktora zadeklarowanego przez platformę i przekazać do niego wywołania.
// Your framework
app.MapMyFramework(...)
.RequireAuthorization()
.WithMyFrameworkFeature(awesome: true);
app.MapHealthChecks("/healthz");
ROZWAŻ napisanie własnego EndpointDataSourcepliku .
EndpointDataSource
jest elementem pierwotnym niskiego poziomu do deklarowania i aktualizowania kolekcji punktów końcowych.
EndpointDataSource
to zaawansowany interfejs API używany przez kontrolery i Razor strony. Aby uzyskać więcej informacji, zobacz Dynamiczny routing punktów końcowych.
Testy routingu mają podstawowy przykład nieakcyjnego źródła danych.
ROZWAŻ zaimplementowanie .GetGroupedEndpoints Zapewnia to pełną kontrolę nad uruchamianiem konwencji grupy i ostatnimi metadanymi w zgrupowanych punktach końcowych. Umożliwia to na przykład niestandardowe EndpointDataSource
implementacje uruchamiania filtrów punktów końcowych dodanych do grup.
NIE próbuj domyślnie rejestrować konta EndpointDataSource
. Wymagaj od użytkowników zarejestrowania struktury w programie UseEndpoints. Filozofią routingu jest to, że nic nie jest dołączane domyślnie i UseEndpoints
jest to miejsce do rejestrowania punktów końcowych.
ROZWAŻ zdefiniowanie typów metadanych jako interfejsu.
DO umożliwia używanie typów metadanych jako atrybutu w klasach i metodach.
public interface ICoolMetadata
{
bool IsCool { get; }
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CoolMetadataAttribute : Attribute, ICoolMetadata
{
public bool IsCool => true;
}
Struktury, takie jak kontrolery i Razor strony, obsługują stosowanie atrybutów metadanych do typów i metod. W przypadku deklarowania typów metadanych:
Deklarowanie typu metadanych jako interfejsu zwiększa kolejną warstwę elastyczności:
NIE umożliwia przesłaniania metadanych, jak pokazano w poniższym przykładzie:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class SuppressCoolMetadataAttribute : Attribute, ICoolMetadata
{
public bool IsCool => false;
}
[CoolMetadata]
public class MyController : Controller
{
public void MyCool() { }
[SuppressCoolMetadata]
public void Uncool() { }
}
Najlepszym sposobem przestrzegania tych wytycznych jest unikanie definiowania metadanych znaczników:
Kolekcja metadanych jest uporządkowana i obsługuje zastępowanie według priorytetu. W przypadku kontrolerów metadane metody akcji są najbardziej specyficzne.
DO sprawia, że oprogramowanie pośredniczące jest przydatne z routingiem i bez tego routingu:
app.UseAuthorization(new AuthorizationPolicy() { ... });
// Your framework
app.MapMyFramework(...).RequireAuthorization();
Jako przykład tej wskazówki należy wziąć pod uwagę UseAuthorization
oprogramowanie pośredniczące. Oprogramowanie pośredniczące autoryzacji umożliwia przekazanie zasad rezerwowych.
Zasady rezerwowe, jeśli zostały określone, mają zastosowanie do obu następujących elementów:
Dzięki temu oprogramowanie pośredniczące autoryzacji jest przydatne poza kontekstem routingu. Oprogramowanie pośredniczące autoryzacji może służyć do tradycyjnego programowania oprogramowania pośredniczącego.
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"
}
}
}
Routing jest odpowiedzialny za dopasowywanie przychodzących żądań HTTP i wysyłanie tych żądań do punktów końcowych wykonywalnych aplikacji. Punkty końcowe to jednostki aplikacji kodu obsługi żądań wykonywalnych. Punkty końcowe są definiowane w aplikacji i konfigurowane podczas uruchamiania aplikacji. Proces dopasowywania punktu końcowego może wyodrębnić wartości z adresu URL żądania i podać te wartości do przetwarzania żądań. Korzystając z informacji o punkcie końcowym z aplikacji, routing jest również w stanie wygenerować adresy URL mapujące na punkty końcowe.
Aplikacje mogą konfigurować routing przy użyciu:
W tym artykule opisano szczegóły niskiego poziomu dotyczące routingu ASP.NET Core. Aby uzyskać informacje na temat konfigurowania routingu:
Poniższy kod przedstawia podstawowy przykład routingu:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Powyższy przykład zawiera pojedynczy punkt końcowy przy użyciu MapGet metody :
GET
do głównego adresu URL /
: Hello World!
jest zapisywany w odpowiedzi HTTP.GET
jest lub główny adres URL nie /
jest , nie jest zgodna trasa i zwracany jest http 404.Routing używa pary oprogramowania pośredniczącego zarejestrowanego przez UseRouting i UseEndpoints:
UseRouting
Dodaje trasę zgodną z potokiem oprogramowania pośredniczącego. To oprogramowanie pośredniczące analizuje zestaw punktów końcowych zdefiniowanych w aplikacji i wybiera najlepsze dopasowanie na podstawie żądania.UseEndpoints
Dodaje wykonywanie punktu końcowego do potoku oprogramowania pośredniczącego. Uruchamia delegata skojarzonego z wybranym punktem końcowym.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
. Jednak aplikacje mogą zmieniać kolejność, w jakiej UseRouting
i UseEndpoints
uruchamiać, wywołując te metody jawnie. Na przykład następujący kod wykonuje jawne wywołanie metody UseRouting
:
app.Use(async (context, next) =>
{
// ...
await next(context);
});
app.UseRouting();
app.MapGet("/", () => "Hello World!");
Powyższy kod:
app.Use
zarejestrowania niestandardowego oprogramowania pośredniczącego uruchamianego na początku potoku.UseRouting
skonfigurowania odpowiedniego oprogramowania pośredniczącego trasy do uruchomienia po niestandardowym oprogramowaniem pośredniczącym.MapGet
przy użyciu jest uruchamiany na końcu potoku.Jeśli poprzedni przykład nie zawierał wywołania metody UseRouting
, niestandardowe oprogramowanie pośredniczące zostanie uruchomione po dopasowaniu trasy oprogramowania pośredniczącego.
Metoda MapGet
służy do definiowania punktu końcowego. Punkt końcowy to coś, co może być następujące:
Punkty końcowe, które mogą być dopasowane i wykonywane przez aplikację, są konfigurowane w programie UseEndpoints
. Na przykład metody MapGet, MapPosti podobne łączą delegatów żądań z systemem routingu. Dodatkowe metody mogą służyć do łączenia funkcji platformy ASP.NET Core z systemem routingu:
W poniższym przykładzie przedstawiono routing z bardziej zaawansowanym szablonem trasy:
app.MapGet("/hello/{name:alpha}", (string name) => $"Hello {name}!");
Ciąg /hello/{name:alpha}
jest szablonem trasy. Szablon trasy służy do konfigurowania sposobu dopasowania punktu końcowego. W takim przypadku szablon jest zgodny z następującymi elementami:
/hello/Docs
/hello/
od sekwencji znaków alfabetycznych.
:alpha
stosuje ograniczenie trasy, które pasuje tylko do znaków alfabetycznych.
Ograniczenia tras zostały wyjaśnione w dalszej części tego artykułu.Drugi segment ścieżki adresu URL: {name:alpha}
name
.W poniższym przykładzie przedstawiono routing z kontrolą kondycji i autoryzacją:
app.UseAuthentication();
app.UseAuthorization();
app.MapHealthChecks("/healthz").RequireAuthorization();
app.MapGet("/", () => "Hello World!");
W poprzednim przykładzie pokazano, jak:
Wywołanie MapHealthChecks dodaje punkt końcowy kontroli kondycji. Łączenie RequireAuthorization łańcucha do tego wywołania powoduje dołączenie zasad autoryzacji do punktu końcowego.
Wywoływanie UseAuthentication i UseAuthorization dodawanie oprogramowania pośredniczącego uwierzytelniania i autoryzacji. Te oprogramowanie pośredniczące są umieszczane między elementami UseRouting i UseEndpoints
, dzięki czemu mogą:
UseRouting
element .W poprzednim przykładzie istnieją dwa punkty końcowe, ale tylko punkt końcowy kontroli kondycji ma dołączone zasady autoryzacji. Jeśli żądanie pasuje do punktu końcowego sprawdzania kondycji, /healthz
zostanie wykonane sprawdzanie autoryzacji. Pokazuje to, że punkty końcowe mogą mieć dołączone dodatkowe dane. Te dodatkowe dane są nazywane metadanymi punktu końcowego:
System routingu opiera się na potoku oprogramowania pośredniczącego, dodając zaawansowaną koncepcję punktu końcowego . Punkty końcowe reprezentują jednostki funkcjonalności aplikacji, które różnią się od siebie pod względem routingu, autoryzacji i dowolnej liczby systemów ASP.NET Core.
Punkt końcowy platformy ASP.NET Core to:
Poniższy kod pokazuje, jak pobrać i sprawdzić punkt końcowy pasujący do bieżącego żądania:
app.Use(async (context, next) =>
{
var currentEndpoint = context.GetEndpoint();
if (currentEndpoint is null)
{
await next(context);
return;
}
Console.WriteLine($"Endpoint: {currentEndpoint.DisplayName}");
if (currentEndpoint is RouteEndpoint routeEndpoint)
{
Console.WriteLine($" - Route Pattern: {routeEndpoint.RoutePattern}");
}
foreach (var endpointMetadata in currentEndpoint.Metadata)
{
Console.WriteLine($" - Metadata: {endpointMetadata}");
}
await next(context);
});
app.MapGet("/", () => "Inspect Endpoint.");
Punkt końcowy, jeśli został wybrany, można pobrać z pliku HttpContext
. Jego właściwości można sprawdzić. Obiekty punktu końcowego są niezmienne i nie można ich modyfikować po utworzeniu. Najczęstszym typem RouteEndpointpunktu końcowego jest .
RouteEndpoint
zawiera informacje, które umożliwiają wybranie go przez system routingu.
W poprzednim kodzie aplikacja. Użyj konfiguracji wbudowanego oprogramowania pośredniczącego.
Poniższy kod pokazuje, że w zależności od tego, gdzie app.Use
jest wywoływana w potoku, punkt końcowy może nie być:
// Location 1: before routing runs, endpoint is always null here.
app.Use(async (context, next) =>
{
Console.WriteLine($"1. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
await next(context);
});
app.UseRouting();
// Location 2: after routing runs, endpoint will be non-null if routing found a match.
app.Use(async (context, next) =>
{
Console.WriteLine($"2. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
await next(context);
});
// Location 3: runs when this endpoint matches
app.MapGet("/", (HttpContext context) =>
{
Console.WriteLine($"3. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
return "Hello World!";
}).WithDisplayName("Hello");
app.UseEndpoints(_ => { });
// Location 4: runs after UseEndpoints - will only run if there was no match.
app.Use(async (context, next) =>
{
Console.WriteLine($"4. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
await next(context);
});
Poprzedni przykład dodaje Console.WriteLine
instrukcje, które wyświetlają, czy wybrano punkt końcowy. Aby uzyskać czytelność, przykład przypisuje nazwę wyświetlaną do podanego /
punktu końcowego.
Powyższy przykład obejmuje również wywołania i UseRouting
UseEndpoints
sterowanie dokładnie tym, kiedy oprogramowanie pośredniczące działa w potoku.
Uruchomienie tego kodu z adresem URL wyświetlania /
:
1. Endpoint: (null)
2. Endpoint: Hello
3. Endpoint: Hello
Uruchomienie tego kodu przy użyciu dowolnego innego adresu URL powoduje wyświetlenie:
1. Endpoint: (null)
2. Endpoint: (null)
4. Endpoint: (null)
Te dane wyjściowe pokazują, że:
UseRouting
wywołaniem.UseRouting
i UseEndpoints.UseEndpoints
pośredniczące jest terminalem po znalezieniu dopasowania.
Oprogramowanie pośredniczące terminala jest zdefiniowane w dalszej części tego artykułu.UseEndpoints
wykonaniu tylko wtedy, gdy nie zostanie znalezione dopasowanie.Oprogramowanie UseRouting
pośredniczące używa SetEndpoint metody w celu dołączenia punktu końcowego do bieżącego kontekstu. Można zastąpić UseRouting
oprogramowanie pośredniczące logiką niestandardową i nadal korzystać z zalet korzystania z punktów końcowych. Punkty końcowe są typami pierwotnymi niskiego poziomu, takimi jak oprogramowanie pośredniczące, i nie są powiązane z implementacją routingu. Większość aplikacji nie musi zastępować UseRouting
logiką niestandardową.
Oprogramowanie UseEndpoints
pośredniczące jest przeznaczone do użycia w parze z oprogramowaniem pośredniczącym UseRouting
. Podstawowa logika do wykonania punktu końcowego nie jest skomplikowana. Użyj GetEndpoint polecenia , aby pobrać punkt końcowy, a następnie wywołać jego RequestDelegate właściwość.
Poniższy kod pokazuje, jak oprogramowanie pośredniczące może wpływać na routing lub reagować na nie:
app.UseHttpMethodOverride();
app.UseRouting();
app.Use(async (context, next) =>
{
if (context.GetEndpoint()?.Metadata.GetMetadata<RequiresAuditAttribute>() is not null)
{
Console.WriteLine($"ACCESS TO SENSITIVE DATA AT: {DateTime.UtcNow}");
}
await next(context);
});
app.MapGet("/", () => "Audit isn't required.");
app.MapGet("/sensitive", () => "Audit required for sensitive data.")
.WithMetadata(new RequiresAuditAttribute());
public class RequiresAuditAttribute : Attribute { }
W poprzednim przykładzie przedstawiono dwie ważne pojęcia:
UseRouting
zmodyfikowaniem danych, na których działa routing.
UseRouting
i UseEndpoints w celu przetworzenia wyników routingu przed wykonaniem punktu końcowego.
UseRouting
i UseEndpoints
: UseAuthorization
i UseCors
.Powyższy kod przedstawia przykład niestandardowego oprogramowania pośredniczącego obsługującego zasady poszczególnych punktów końcowych. Oprogramowanie pośredniczące zapisuje dziennik inspekcji dostępu do poufnych danych w konsoli. Oprogramowanie pośredniczące można skonfigurować do inspekcji punktu końcowego za RequiresAuditAttribute
pomocą metadanych. W tym przykładzie pokazano wzorzec zgody, w którym są poddawane inspekcji tylko punkty końcowe oznaczone jako poufne. Tę logikę można zdefiniować w odwrotnej kolejności, przeprowadzając inspekcję wszystkich elementów, które nie są oznaczone jako bezpieczne, na przykład. System metadanych punktu końcowego jest elastyczny. Ta logika może być zaprojektowana w dowolny sposób odpowiednio do przypadku użycia.
Powyższy przykładowy kod ma na celu zademonstrowanie podstawowych pojęć dotyczących punktów końcowych. Przykład nie jest przeznaczony do użytku produkcyjnego. Bardziej kompletna wersja oprogramowania pośredniczącego dziennika inspekcji:
Metadane RequiresAuditAttribute
zasad inspekcji są definiowane jako Attribute
łatwiejsze w użyciu z platformami opartymi na klasach, takimi jak kontrolery i SignalR. W przypadku używania trasy do kodu:
Najlepszym rozwiązaniem dla typów metadanych jest zdefiniowanie ich jako interfejsów lub atrybutów. Interfejsy i atrybuty umożliwiają ponowne użycie kodu. System metadanych jest elastyczny i nie nakłada żadnych ograniczeń.
W poniższym przykładzie pokazano zarówno oprogramowanie pośredniczące terminala, jak i routing:
// Approach 1: Terminal Middleware.
app.Use(async (context, next) =>
{
if (context.Request.Path == "/")
{
await context.Response.WriteAsync("Terminal Middleware.");
return;
}
await next(context);
});
app.UseRouting();
// Approach 2: Routing.
app.MapGet("/Routing", () => "Routing.");
Styl oprogramowania pośredniczącego pokazany za pomocą Approach 1:
programu to oprogramowanie pośredniczące terminalu. Jest to nazywane oprogramowaniem pośredniczącym terminalu, ponieważ wykonuje zgodną operację:
Path == "/"
oprogramowania pośredniczącego i Path == "/Routing"
routingu.next
.Jest to nazywane oprogramowaniem pośredniczącym terminalu, ponieważ kończy wyszukiwanie, wykonuje niektóre funkcje, a następnie zwraca.
Poniższa lista porównuje oprogramowanie pośredniczące terminala z routingiem:
next
UseAuthorization
i UseCors
.
UseAuthorization
oprogramowaniem pośredniczącym lub UseCors
wymaga ręcznego łączenia się z systemem autoryzacji.Punkt końcowy definiuje oba:
Oprogramowanie pośredniczące terminalu może być skutecznym narzędziem, ale może wymagać:
Przed napisaniem oprogramowania pośredniczącego terminalu rozważ integrację z routingiem.
Istniejące oprogramowanie pośredniczące terminalu integrujące się z usługą Map lub MapWhen zwykle może zostać przekształcone w punkt końcowy obsługujący routing. MapHealthChecks demonstruje wzorzec oprogramowania routera:
Map
i podaj nowy potok oprogramowania pośredniczącego.Map
metodę rozszerzenia.Poniższy kod przedstawia użycie narzędzi MapHealthChecks:
app.UseAuthentication();
app.UseAuthorization();
app.MapHealthChecks("/healthz").RequireAuthorization();
W poprzednim przykładzie pokazano, dlaczego zwracanie obiektu konstruktora jest ważne. Zwracanie obiektu konstruktora umożliwia deweloperowi aplikacji konfigurowanie zasad, takich jak autoryzacja dla punktu końcowego. W tym przykładzie oprogramowanie pośredniczące kontroli kondycji nie ma bezpośredniej integracji z systemem autoryzacji.
System metadanych został utworzony w odpowiedzi na problemy napotykane przez autorów rozszerzeń przy użyciu oprogramowania pośredniczącego terminalu. Dla każdego oprogramowania pośredniczącego problematyczne jest zaimplementowanie własnej integracji z systemem autoryzacji.
Po wykonaniu oprogramowania pośredniczącego routingu ustawia Endpoint
ona wartości i kieruje je do funkcji żądania z HttpContext bieżącego żądania:
HttpRequest.RouteValues
pobiera kolekcję wartości tras.Oprogramowanie pośredniczące jest uruchamiane po tym, jak oprogramowanie pośredniczące routingu może sprawdzić punkt końcowy i podjąć działania. Na przykład oprogramowanie pośredniczące autoryzacji może przesłuchić kolekcję metadanych punktu końcowego dla zasad autoryzacji. Po wykonaniu całego oprogramowania pośredniczącego w potoku przetwarzania żądań wywoływany jest delegat wybranego punktu końcowego.
System routingu w routingu punktów końcowych jest odpowiedzialny za wszystkie decyzje dotyczące wysyłania. Ponieważ oprogramowanie pośredniczące stosuje zasady na podstawie wybranego punktu końcowego, ważne jest, aby:
Ostrzeżenie
W przypadku zgodności z poprzednimi wersjami po wykonaniu delegata punktu końcowego kontroler lub Razor strony właściwości RouteContext.RouteData są ustawione na odpowiednie wartości na podstawie przetwarzania żądań wykonanego do tej pory.
Typ RouteContext
zostanie oznaczony jako przestarzały w przyszłej wersji:
RouteData.Values
do HttpRequest.RouteValues
.RouteData.DataTokens
w celu pobrania IDataTokensMetadata z metadanych punktu końcowego.Dopasowywanie adresów URL działa w konfigurowalnym zestawie faz. W każdej fazie dane wyjściowe są zestawem dopasowań. Zestaw dopasowań można zawęzić dalej przez następną fazę. Implementacja routingu nie gwarantuje kolejności przetwarzania pasujących punktów końcowych. Wszystkie możliwe dopasowania są przetwarzane jednocześnie. Fazy dopasowywania adresów URL są wykonywane w następującej kolejności. ASP.NET Core:
Lista punktów końcowych jest priorytetowa zgodnie z:
Wszystkie pasujące punkty końcowe są przetwarzane w każdej fazie do momentu EndpointSelector osiągnięcia. Jest EndpointSelector
to ostatnia faza. Wybiera punkt końcowy o najwyższym priorytcie z dopasowań jako najlepszy. Jeśli istnieją inne dopasowania z tym samym priorytetem co najlepsze dopasowanie, zgłaszany jest niejednoznaczny wyjątek dopasowania.
Pierwszeństwo trasy jest obliczane na podstawie bardziej szczegółowego szablonu trasy o wyższym priorytecie. Rozważmy na przykład szablony /hello
i /{message}
:
/hello
adresu URL ./hello
jest bardziej szczegółowy i dlatego wyższy priorytet.Ogólnie rzecz biorąc, pierwszeństwo trasy dobrze nadaje się do wyboru najlepszego dopasowania do rodzajów schematów adresów URL używanych w praktyce. Używaj Order tylko wtedy, gdy jest to konieczne, aby uniknąć niejednoznaczności.
Ze względu na rodzaj rozszerzalności zapewnianej przez routing nie jest możliwe, aby system routingu obliczał przed upływem czasu niejednoznaczne trasy. Rozważmy przykład, taki jak szablony /{message:alpha}
tras i /{message:int}
:
alpha
pasuje tylko do znaków alfabetycznych.int
pasuje tylko do liczb.Ostrzeżenie
Kolejność operacji wewnątrz UseEndpoints nie ma wpływu na zachowanie routingu z jednym wyjątkiem. MapControllerRoute i MapAreaRoute automatycznie przypisz wartość zamówienia do swoich punktów końcowych na podstawie kolejności, w której są wywoływane. Symuluje to długotrwałe zachowanie kontrolerów bez systemu routingu zapewniające takie same gwarancje jak starsze implementacje routingu.
Routing punktów końcowych w ASP.NET Core:
Pierwszeństwo szablonu trasy to system, który przypisuje każdemu szablonowi trasy wartość na podstawie jego specyfiki. Pierwszeństwo szablonu trasy:
Rozważmy na przykład szablony /Products/List
i /Products/{id}
. Uzasadnione byłoby założenie, że /Products/List
jest to lepsze dopasowanie niż /Products/{id}
dla ścieżki /Products/List
adresu URL . Działa to, ponieważ segment /List
literału jest uznawany za lepszy priorytet niż segment /{id}
parametrów .
Szczegółowe informacje o sposobie działania pierwszeństwa są powiązane z definiowaniem szablonów tras:
Generowanie adresu URL:
Routing punktów końcowych obejmuje LinkGenerator interfejs API.
LinkGenerator
jest pojedynczą usługą dostępną z di. Interfejs LinkGenerator
API może być używany poza kontekstem wykonywania żądania.
Mvc.IUrlHelper i scenariusze, które opierają się na IUrlHelperelementach , takich jak Pomocnicy tagów, Pomocnicy HTML i Wyniki akcji, używają interfejsu LinkGenerator
API wewnętrznie, aby zapewnić możliwości generowania linków.
Generator linków jest wspierany przez koncepcję schematów adresów i adresów. Schemat adresów to sposób określania punktów końcowych, które powinny być brane pod uwagę podczas generowania linków. Na przykład scenariusze nazw tras i wartości tras, które wielu użytkowników zna z kontrolerów i Razor stron, są implementowane jako schemat adresów.
Generator linków może łączyć się z kontrolerami i Razor stronami za pomocą następujących metod rozszerzeń:
Przeciążenia tych metod akceptują argumenty, które obejmują HttpContext
element . Te metody są funkcjonalnie równoważne url.Action i Url.Page, ale oferują dodatkową elastyczność i opcje.
Metody GetPath*
są najbardziej podobne do Url.Action
metod i Url.Page
, w których generują identyfikator URI zawierający ścieżkę bezwzględną. Metody GetUri*
zawsze generują bezwzględny identyfikator URI zawierający schemat i host. Metody, które akceptują HttpContext
generowanie identyfikatora URI w kontekście wykonywania żądania. Wartości trasy otoczenia , ścieżka podstawowa adresu URL, schemat i host z wykonywanego żądania są używane, chyba że zostaną zastąpione.
LinkGenerator jest wywoływana przy użyciu adresu. Generowanie identyfikatora URI odbywa się w dwóch krokach:
Metody udostępniane przez LinkGenerator obsługę standardowych możliwości generowania linków dla dowolnego typu adresu. Najwygodniejszym sposobem korzystania z generatora linków jest użycie metod rozszerzeń, które wykonują operacje dla określonego typu adresu:
Extension, metoda | opis |
---|---|
GetPathByAddress | Generuje identyfikator URI ze ścieżką bezwzględną na podstawie podanych wartości. |
GetUriByAddress | Generuje bezwzględny identyfikator URI na podstawie podanych wartości. |
Ostrzeżenie
Zwróć uwagę na następujące konsekwencje wywoływania LinkGenerator metod:
Użyj GetUri*
metod rozszerzeń z ostrożnością w konfiguracji aplikacji, która nie weryfikuje Host
nagłówka żądań przychodzących.
Host
Jeśli nagłówek żądań przychodzących nie jest weryfikowany, niezaufane dane wejściowe żądania mogą być wysyłane z powrotem do klienta w identyfikatorach URI w widoku lub na stronie. Zalecamy skonfigurowanie serwera przez wszystkie aplikacje produkcyjne w celu zweryfikowania nagłówka Host
pod kątem znanych prawidłowych wartości.
Należy używać LinkGenerator z ostrożnością w oprogramowania pośredniczącego w połączeniu z Map
lub MapWhen
.
Map*
zmienia ścieżkę podstawową wykonywania żądania, co wpływa na dane wyjściowe generowania linków.
LinkGenerator Wszystkie interfejsy API umożliwiają określenie ścieżki podstawowej. Określ pustą ścieżkę podstawową, aby cofnąć wpływ na generowanie linków Map*
.
W poniższym przykładzie oprogramowanie pośredniczące używa interfejsu LinkGenerator API do utworzenia linku do metody akcji zawierającej listę produktów. Użycie generatora linków przez wstrzyknięcie go do klasy i wywołanie GenerateLink
jest dostępne dla dowolnej klasy w aplikacji:
public class ProductsMiddleware
{
private readonly LinkGenerator _linkGenerator;
public ProductsMiddleware(RequestDelegate next, LinkGenerator linkGenerator) =>
_linkGenerator = linkGenerator;
public async Task InvokeAsync(HttpContext httpContext)
{
httpContext.Response.ContentType = MediaTypeNames.Text.Plain;
var productsPath = _linkGenerator.GetPathByAction("Products", "Store");
await httpContext.Response.WriteAsync(
$"Go to {productsPath} to see our products.");
}
}
Tokeny w ramach {}
definicji parametrów trasy, które są powiązane, jeśli trasa jest zgodna. W segmencie trasy można zdefiniować więcej niż jeden parametr trasy, ale parametry trasy muszą być rozdzielone wartością literału. Na przykład:
{controller=Home}{action=Index}
nie jest prawidłową trasą, ponieważ nie ma wartości literału między {controller}
i {action}
. Parametry trasy muszą mieć nazwę i mogą mieć określone dodatkowe atrybuty.
Tekst literału inny niż parametry trasy (na przykład {id}
) i separator /
ścieżki musi być zgodny z tekstem w adresie URL. Dopasowywanie tekstu jest bez uwzględniania wielkości liter i oparte na dekodowanej reprezentacji ścieżki adresu URL. Aby dopasować ogranicznik {
parametru trasy literału lub }
, należy uruchomić ogranicznik, powtarzając znak. Na przykład {{
lub }}
.
Gwiazdka *
lub podwójna gwiazdka **
:
blog/{**slug}
blog/
i ma dowolną wartość po niej.blog/
jest przypisywana do wartości trasy slug .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:
{**slug}"
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.
Parametry catch-all mogą być również zgodne z pustym ciągiem.
Parametr catch-all ucieczki odpowiednich znaków, gdy trasa jest używana do generowania adresu URL, w tym znaków separatora /
ścieżki. Na przykład trasa foo/{*path}
z wartościami { path = "my/path" }
tras generuje foo/my%2Fpath
wartość . Zwróć uwagę na unikniętą ukośnik. Aby użyć znaków separatora ścieżki dwukierunkowej, użyj prefiksu parametru **
trasy. Trasa foo/{**path}
z elementem { path = "my/path" }
generuje foo/my/path
wartość .
Wzorce adresów URL, które próbują przechwycić nazwę pliku z opcjonalnym rozszerzeniem pliku, mają dodatkowe uwagi. Rozważmy na przykład szablon files/{filename}.{ext?}
. Gdy wartości obu filename
i ext
istnieją, oba wartości są wypełniane. Jeśli w adresie URL istnieje tylko wartość filename
, trasa jest zgodna, ponieważ końcowy .
jest opcjonalny. Następujące adresy URL pasują do tej trasy:
/files/myFile.txt
/files/myFile
Parametry trasy mogą mieć wartości domyślne wyznaczone przez określenie wartości domyślnej po nazwie parametru oddzielonej znakiem równości (=
). Na przykład {controller=Home}
definiuje Home
jako wartość domyślną dla elementu controller
. Wartość domyślna jest używana, jeśli w adresie URL parametru nie ma żadnej wartości. Parametry trasy są opcjonalne, dołączając znak zapytania (?
) na końcu nazwy parametru. Na przykład id?
. Różnica między opcjonalnymi wartościami a domyślnymi parametrami trasy to:
Parametry trasy mogą mieć ograniczenia, które muszą być zgodne z wartością trasy powiązaną z adresem URL. Dodanie :
i ograniczenie nazwy po nazwie parametru trasy określa ograniczenie wbudowane w parametrze trasy. Jeśli ograniczenie wymaga argumentów, są one ujęte w nawiasy (...)
po nazwie ograniczenia. Można określić wiele ograniczeń wbudowanych przez dołączenie innej :
nazwy i ograniczenia.
Nazwa ograniczenia i argumenty są przekazywane do IInlineConstraintResolver usługi w celu utworzenia wystąpienia IRouteConstraint do użycia w przetwarzaniu adresów URL. Na przykład szablon blog/{article:minlength(10)}
trasy określa minlength
ograniczenie z argumentem 10
. Aby uzyskać więcej informacji na temat ograniczeń tras i listy ograniczeń udostępnianych przez platformę, zobacz sekcję Ograniczenia trasy.
Parametry trasy mogą również zawierać transformatory parametrów. Transformatory parametrów przekształcają wartość parametru podczas generowania łączy i pasujących akcji i stron do adresów URL. Podobnie jak ograniczenia, transformatory parametrów można dodać w tekście do parametru trasy przez dodanie :
nazwy parametru i transformatora po nazwie parametru trasy. Na przykład szablon blog/{article:slugify}
trasy określa slugify
transformator. Aby uzyskać więcej informacji na temat transformatorów parametrów, zobacz sekcję Transformatory parametrów .
W poniższej tabeli przedstawiono przykładowe szablony tras i ich zachowanie:
Szablon trasy | Przykładowy pasujący identyfikator URI | Identyfikator URI żądania... |
---|---|---|
hello |
/hello |
Pasuje tylko do pojedynczej ścieżki /hello . |
{Page=Home} |
/ |
Dopasuj i ustawia wartość Page Home . |
{Page=Home} |
/Contact |
Dopasuj i ustawia wartość Page Contact . |
{controller}/{action}/{id?} |
/Products/List |
Mapuje na Products kontroler i List akcję. |
{controller}/{action}/{id?} |
/Products/Details/123 |
Mapuje na Products kontroler i Details akcję z ustawioną wartościąid 123. |
{controller=Home}/{action=Index}/{id?} |
/ |
Mapuje na kontroler i Home metodęIndex . Właściwość id jest ignorowana. |
{controller=Home}/{action=Index}/{id?} |
/Products |
Mapuje na kontroler i Products metodęIndex . Właściwość id jest ignorowana. |
Użycie szablonu jest zwykle najprostszym podejściem do routingu. Ograniczenia i wartości domyślne można również określić poza szablonem trasy.
Złożone segmenty są przetwarzane przez dopasowanie ograniczników literału od prawej do lewej w sposób niechłanny . Na przykład [Route("/a{b}c{d}")]
jest to złożony segment.
Złożone segmenty działają w określony sposób, aby można było z nich korzystać pomyślnie. W przykładzie w tej sekcji pokazano, dlaczego złożone segmenty działają prawidłowo tylko wtedy, gdy tekst ogranicznika nie pojawia się wewnątrz wartości parametrów. Użycie wyrażenia regularnego, a następnie ręczne wyodrębnienie wartości jest wymagane w przypadku bardziej złożonych przypadków.
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.
Jest to podsumowanie kroków, które routing wykonuje przy użyciu szablonu /a{b}c{d}
i ścieżki /abcd
adresu URL . Służy |
do wizualizacji sposobu działania algorytmu:
c
. Dlatego /abcd
jest wyszukiwany z prawej strony i znajduje /ab|c|d
.d
) jest teraz dopasowane do parametru {d}
trasy .a
. Dlatego /ab|c|d
jest wyszukiwany, zaczynając od miejsca, w którym odeszliśmy, a następnie a
znajduje się /|a|b|c|d
.b
) jest teraz zgodna z parametrem {b}
trasy .Oto przykład negatywnego przypadku przy użyciu tego samego szablonu /a{b}c{d}
i ścieżki /aabcd
adresu URL . Służy |
do wizualizowania sposobu działania algorytmu. Ten przypadek nie jest zgodny, co zostało wyjaśnione przez ten sam algorytm:
c
. Dlatego /aabcd
jest wyszukiwany z prawej strony i znajduje /aab|c|d
.d
) jest teraz dopasowane do parametru {d}
trasy .a
. Dlatego /aab|c|d
jest wyszukiwany, zaczynając od miejsca, w którym odeszliśmy, a następnie a
znajduje się /a|a|b|c|d
.b
) jest teraz zgodna z parametrem {b}
trasy .a
, ale algorytm zabrakło szablonu trasy do przeanalizowania, więc nie jest to dopasowanie.Ponieważ pasujący algorytm nie jest chciwy:
Wyrażenia regularne zapewniają znacznie większą kontrolę nad ich zachowaniem dopasowania.
Zachłanne dopasowanie, znane również jako leniwe dopasowanie, pasuje do największego możliwego ciągu. Niechłanny pasuje do najmniejszego możliwego ciągu.
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;
Ograniczenia trasy są wykonywane, gdy wystąpiło dopasowanie do przychodzącego adresu URL, a ścieżka adresu URL jest tokenizowana do wartości tras. Ograniczenia tras zazwyczaj sprawdzają wartość trasy skojarzoną za pośrednictwem szablonu trasy i podejmowania prawdziwej lub fałszywej decyzji o tym, czy wartość jest akceptowalna. Niektóre ograniczenia trasy używają danych poza wartością trasy, aby rozważyć, czy żądanie można kierować. Na przykład HttpMethodRouteConstraint może zaakceptować lub odrzucić żądanie na podstawie jego czasownika HTTP. Ograniczenia są używane w żądaniach routingu i generowaniu linków.
Ostrzeżenie
Nie używaj ograniczeń do walidacji danych wejściowych. Jeśli ograniczenia są używane do walidacji danych wejściowych, nieprawidłowe dane wejściowe będą skutkować odpowiedzią 404
Nie znaleziono. Nieprawidłowe dane wejściowe powinny spowodować wygenerowanie nieprawidłowego 400
żądania z odpowiednim komunikatem o błędzie. Ograniczenia trasy służą do uściślania podobnych tras, a nie sprawdzania poprawności danych wejściowych dla określonej trasy.
W poniższej tabeli przedstawiono przykładowe ograniczenia tras i ich oczekiwane zachowanie:
ograniczenie | Przykład | Przykładowe dopasowania | Uwagi |
---|---|---|---|
int |
{id:int} |
123456789 , -123456789 |
Pasuje do dowolnej liczby całkowitej |
bool |
{active:bool} |
true , FALSE |
Dopasuj lub true false . Bez uwzględniania wielkości liter |
datetime |
{dob:datetime} |
2016-12-31 , 2016-12-31 7:32pm |
Pasuje do prawidłowej DateTime wartości w niezmiennej kulturze. Zobacz poprzednie ostrzeżenie. |
decimal |
{price:decimal} |
49.99 , -1,000.01 |
Pasuje do prawidłowej decimal wartości w niezmiennej kulturze. Zobacz poprzednie ostrzeżenie. |
double |
{weight:double} |
1.234 , -1,001.01e8 |
Pasuje do prawidłowej double wartości w niezmiennej kulturze. Zobacz poprzednie ostrzeżenie. |
float |
{weight:float} |
1.234 , -1,001.01e8 |
Pasuje do prawidłowej float wartości w niezmiennej kulturze. Zobacz poprzednie ostrzeżenie. |
guid |
{id:guid} |
CD2C1638-1638-72D5-1638-DEADBEEF1638 |
Pasuje do prawidłowej Guid wartości |
long |
{ticks:long} |
123456789 , -123456789 |
Pasuje do prawidłowej long wartości |
minlength(value) |
{username:minlength(4)} |
Rick |
Ciąg musi mieć co najmniej 4 znaki |
maxlength(value) |
{filename:maxlength(8)} |
MyFile |
Ciąg nie może zawierać więcej niż 8 znaków |
length(length) |
{filename:length(12)} |
somefile.txt |
Ciąg musi mieć długość dokładnie 12 znaków |
length(min,max) |
{filename:length(8,16)} |
somefile.txt |
Ciąg musi mieć co najmniej 8 znaków i nie więcej niż 16 znaków |
min(value) |
{age:min(18)} |
19 |
Wartość całkowita musi być co najmniej 18 |
max(value) |
{age:max(120)} |
91 |
Wartość całkowita nie może być większa niż 120 |
range(min,max) |
{age:range(18,120)} |
91 |
Wartość całkowita musi być co najmniej 18, ale nie większa niż 120 |
alpha |
{name:alpha} |
Rick |
Ciąg musi składać się z co najmniej jednego znaku a -z alfabetycznego i bez uwzględniania wielkości liter. |
regex(expression) |
{ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)} |
123-45-6789 |
Ciąg musi być zgodny z wyrażeniem regularnym. Zobacz porady dotyczące definiowania wyrażenia regularnego. |
required |
{name:required} |
Rick |
Służy do wymuszania, że wartość nieparametrowa jest obecna podczas generowania adresu URL |
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 ograniczeń rozdzielonych dwukropkami można zastosować do jednego parametru. Na przykład następujące ograniczenie ogranicza parametr do wartości całkowitej 1 lub większej:
[Route("users/{id:int:min(1)}")]
public User GetUserById(int id) { }
Ostrzeżenie
Ograniczenia trasy, które weryfikują adres URL i są konwertowane na typ CLR, zawsze używają niezmiennej kultury. Na przykład konwersja na typ int
CLR lub DateTime
. Te ograniczenia zakładają, że adres URL nie jest lokalizowalny. Ograniczenia trasy dostarczone przez platformę nie modyfikują wartości przechowywanych w wartościach tras. Wszystkie wartości tras analizowane z adresu URL są przechowywane jako ciągi. Na przykład float
ograniczenie próbuje przekonwertować wartość trasy na zmiennoprzecinkowy, ale przekonwertowana wartość jest używana tylko do sprawdzania, czy można ją przekonwertować na zmiennoprzecinkowy.
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.
Wyrażenia regularne można określić jako ograniczenia wbudowane przy użyciu regex(...)
ograniczenia trasy. Metody w MapControllerRoute rodzinie akceptują również literał obiektów ograniczeń. Jeśli ten formularz jest używany, wartości ciągów są interpretowane jako wyrażenia regularne.
Poniższy kod używa wbudowanego ograniczenia wyrażeń regularnych:
app.MapGet("{message:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)}",
() => "Inline Regex Constraint Matched");
Poniższy kod używa literału obiektu do określenia ograniczenia wyrażenia regularnego:
app.MapControllerRoute(
name: "people",
pattern: "people/{ssn}",
constraints: new { ssn = "^\\d{3}-\\d{2}-\\d{4}$", },
defaults: new { controller = "People", action = "List" });
Struktura ASP.NET Core dodaje RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant
do konstruktora wyrażeń regularnych. Zobacz RegexOptions opis tych elementów członkowskich.
Wyrażenia regularne używają ograniczników i tokenów podobnych do tych używanych przez routing i język C#. Tokeny wyrażeń regularnych muszą zostać uniknięci. Aby użyć wyrażenia ^\d{3}-\d{2}-\d{4}$
regularnego w ograniczeniu wbudowanym, użyj jednego z następujących elementów:
\
znaki podane w ciągu jako \\
znaki w pliku źródłowym języka C#, aby uniknąć \
znaku ucieczki ciągu.Aby uniknąć znaków {
ogranicznika parametru routingu , , }
[
, ]
, podwaja znaki w wyrażeniu, na przykład {{
, , }}
, [[
, ]]
. W poniższej tabeli przedstawiono wyrażenie regularne i jego wersję unikniętą:
Regular expression | Wyrażenie regularne o wartości ucieczki |
---|---|
^\d{3}-\d{2}-\d{4}$ |
^\\d{{3}}-\\d{{2}}-\\d{{4}}$ |
^[a-z]{2}$ |
^[[a-z]]{{2}}$ |
Wyrażenia regularne używane w routingu często zaczynają się od ^
znaku i pasują do pozycji początkowej ciągu. Wyrażenia często kończą się znakiem $
i pasują do końca ciągu. Znaki ^
i $
zapewniają, że wyrażenie regularne jest zgodne z całą wartością parametru trasy.
^
Bez znaków i $
wyrażenie regularne pasuje do dowolnego podciągu w ciągu, co jest często niepożądane. W poniższej tabeli przedstawiono przykłady i wyjaśniono, dlaczego są one zgodne lub nie są zgodne:
Wyrażenie | String | Match | Komentarz |
---|---|---|---|
[a-z]{2} |
hello | Tak | Dopasowania podciągów |
[a-z]{2} |
123abc456 | Tak | Dopasowania podciągów |
[a-z]{2} |
mz | Tak | Dopasuj wyrażenie |
[a-z]{2} |
MZ | Tak | Nie uwzględnia wielkości liter |
^[a-z]{2}$ |
hello | Nie. | Zobacz ^ i $ powyżej |
^[a-z]{2}$ |
123abc456 | Nie. | Zobacz ^ i $ powyżej |
Aby uzyskać więcej informacji na temat składni wyrażeń regularnych, zobacz .NET Framework Regular Expressions (Wyrażenia regularne programu .NET Framework).
Aby ograniczyć parametr do znanego zestawu możliwych wartości, użyj wyrażenia regularnego. Na przykład {action:regex(^(list|get|create)$)}
pasuje action
tylko do wartości trasy do list
, get
lub create
. Jeśli zostanie przekazany do słownika ograniczeń, ciąg ^(list|get|create)$
jest równoważny. Ograniczenia przekazywane w słowniku ograniczeń, które nie są zgodne z jednym ze znanych ograniczeń, są również traktowane jako wyrażenia regularne. Ograniczenia przekazywane w szablonie, które nie pasują do jednego ze znanych ograniczeń, nie są traktowane jako wyrażenia regularne.
Ograniczenia trasy niestandardowej można utworzyć przez zaimplementowanie interfejsu IRouteConstraint . Interfejs IRouteConstraint
zawiera Matchwartość , która zwraca true
wartość, jeśli ograniczenie jest spełnione i false
w przeciwnym razie.
Ograniczenia trasy niestandardowej są rzadko potrzebne. Przed zaimplementowaniem ograniczenia trasy niestandardowej rozważ alternatywy, takie jak powiązanie modelu.
Folder ASP.NET Core Constraints zawiera dobre przykłady tworzenia ograniczeń. Na przykład GuidRouteConstraint.
Aby użyć niestandardowego IRouteConstraint
, typ ograniczenia trasy musi być zarejestrowany w aplikacji ConstraintMap w kontenerze usługi. Jest ConstraintMap
to słownik, który mapuje klucze ograniczeń trasy na IRouteConstraint
implementacje, które weryfikują te ograniczenia. Aplikację ConstraintMap
można zaktualizować w Program.cs
ramach AddRouting wywołania lub konfigurując bezpośrednio za pomocą RouteOptionspolecenia builder.Services.Configure<RouteOptions>
. Na przykład:
builder.Services.AddRouting(options =>
options.ConstraintMap.Add("noZeroes", typeof(NoZeroesRouteConstraint)));
Powyższe ograniczenie jest stosowane w następującym kodzie:
[ApiController]
[Route("api/[controller]")]
public class NoZeroesController : ControllerBase
{
[HttpGet("{id:noZeroes}")]
public IActionResult Get(string id) =>
Content(id);
}
Implementacja elementu NoZeroesRouteConstraint
uniemożliwia 0
korzystanie z parametru trasy:
public class NoZeroesRouteConstraint : IRouteConstraint
{
private static readonly Regex _regex = new(
@"^[1-9]*$",
RegexOptions.CultureInvariant | RegexOptions.IgnoreCase,
TimeSpan.FromMilliseconds(100));
public bool Match(
HttpContext? httpContext, IRouter? route, string routeKey,
RouteValueDictionary values, RouteDirection routeDirection)
{
if (!values.TryGetValue(routeKey, out var routeValue))
{
return false;
}
var routeValueString = Convert.ToString(routeValue, CultureInfo.InvariantCulture);
if (routeValueString is null)
{
return false;
}
return _regex.IsMatch(routeValueString);
}
}
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.
Powyższy kod ma następujące działanie:
0
w {id}
segmencie trasy.Poniższy kod jest lepszym podejściem do zapobiegania przetwarzaniu elementu zawierającego id
0
element:
[HttpGet("{id}")]
public IActionResult Get(string id)
{
if (id.Contains('0'))
{
return StatusCode(StatusCodes.Status406NotAcceptable);
}
return Content(id);
}
Powyższy kod ma następujące zalety w porównaniu z podejściem NoZeroesRouteConstraint
:
0
wartość .Transformatory parametrów:
Na przykład funkcja przekształcania parametrów niestandardowych slugify
we wzorcu blog\{article:slugify}
trasy z poleceniem Url.Action(new { article = "MyTestArticle" })
generuje blog\my-test-article
wartość .
Rozważmy następującą IOutboundParameterTransformer
implementację:
public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
public string? TransformOutbound(object? value)
{
if (value is null)
{
return null;
}
return Regex.Replace(
value.ToString()!,
"([a-z])([A-Z])",
"$1-$2",
RegexOptions.CultureInvariant,
TimeSpan.FromMilliseconds(100))
.ToLowerInvariant();
}
}
Aby użyć transformatora parametrów we wzorcu trasy, skonfiguruj go przy użyciu ConstraintMap polecenia w pliku Program.cs
:
builder.Services.AddRouting(options =>
options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer));
Struktura ASP.NET Core używa funkcji przekształcania parametrów w celu przekształcenia identyfikatora URI, w którym punkt końcowy jest rozpoznawany. Na przykład transformatory parametrów przekształcają wartości tras używane do dopasowania area
wartości , , controller
action
i page
:
app.MapControllerRoute(
name: "default",
pattern: "{controller:slugify=Home}/{action:slugify=Index}/{id?}");
W przypadku poprzedniego szablonu trasy akcja SubscriptionManagementController.GetAll
jest zgodna z identyfikatorem URI /subscription-management/get-all
. Funkcja przekształcania parametrów nie zmienia wartości tras używanych do generowania łącza. Na przykład Url.Action("GetAll", "SubscriptionManagement")
dane wyjściowe ./subscription-management/get-all
ASP.NET Core udostępnia konwencje interfejsu API dotyczące używania transformatorów parametrów z wygenerowanymi trasami:
Ta sekcja zawiera odwołanie do algorytmu zaimplementowanego przez generowanie adresów URL. W praktyce większość złożonych przykładów generowania adresów URL używa kontrolerów lub Razor stron. Aby uzyskać dodatkowe informacje, zobacz routing w kontrolerach .
Proces generowania adresu URL rozpoczyna się od wywołania metody LinkGenerator.GetPathByAddress lub podobnej metody. Metoda jest dostarczana z adresem, zestawem wartości tras i opcjonalnie informacjami o bieżącym żądaniu z .HttpContext
Pierwszym krokiem jest użycie adresu w celu rozpoznania zestawu punktów końcowych kandydata przy użyciu elementu zgodnego z typem IEndpointAddressScheme<TAddress> adresu.
Po znalezieniu zestawu kandydatów przez schemat adresów punkty końcowe są uporządkowane i przetwarzane iteracyjnie, dopóki operacja generowania adresu URL nie powiedzie się. Generowanie adresu URL nie sprawdza niejednoznaczności. Zwracany pierwszy wynik jest wynikiem końcowym.
Pierwszym krokiem generowania adresu URL rozwiązywania problemów jest ustawienie poziomu rejestrowania Microsoft.AspNetCore.Routing
na TRACE
.
LinkGenerator
rejestruje wiele szczegółów dotyczących jego przetwarzania, które mogą być przydatne do rozwiązywania problemów.
Aby uzyskać szczegółowe informacje na temat generowania adresów URL, zobacz Dokumentację generowania adresów URL.
Adresy to koncepcja generowania adresów URL używana do powiązania wywołania z generatorem linków z zestawem kandydatów punktów końcowych.
Adresy to rozszerzalna koncepcja, która domyślnie zawiera dwie implementacje:
string
) jako adresu: IUrlHelper
pomocników tagów, pomocników HTML, wyniki akcji itp.Rolą schematu adresów jest skojarzenie adresu i pasujących punktów końcowych według dowolnych kryteriów:
Z bieżącego żądania routing uzyskuje dostęp do wartości tras bieżącego żądania HttpContext.Request.RouteValues
. Wartości skojarzone z bieżącym żądaniem są określane jako wartości otoczenia. W celu jasności dokumentacja odnosi się do wartości tras przekazywanych do metod jako jawnych wartości.
W poniższym przykładzie przedstawiono wartości otoczenia i jawne wartości. Zapewnia ona wartości otoczenia z bieżącego żądania i jawnych wartości:
public class WidgetController : ControllerBase
{
private readonly LinkGenerator _linkGenerator;
public WidgetController(LinkGenerator linkGenerator) =>
_linkGenerator = linkGenerator;
public IActionResult Index()
{
var indexPath = _linkGenerator.GetPathByAction(
HttpContext, values: new { id = 17 })!;
return Content(indexPath);
}
// ...
Powyższy kod ma następujące działanie:
/Widget/Index/17
Poniższy kod zawiera tylko jawne wartości i nie ma wartości otoczenia:
var subscribePath = _linkGenerator.GetPathByAction(
"Subscribe", "Home", new { id = 17 })!;
Poprzednia metoda zwraca /Home/Subscribe/17
Poniższy kod w zwracaniu WidgetController
wartości /Widget/Subscribe/17
:
var subscribePath = _linkGenerator.GetPathByAction(
HttpContext, "Subscribe", null, new { id = 17 });
Poniższy kod udostępnia kontroler z wartości otoczenia w bieżącym żądaniu i jawnych wartościach:
public class GadgetController : ControllerBase
{
public IActionResult Index() =>
Content(Url.Action("Edit", new { id = 17 })!);
}
Powyższy kod:
/Gadget/Edit/17
jest zwracany.action
nazwę i route
wartości.Poniższy kod zawiera wartości otoczenia z bieżącego żądania i jawnych wartości:
public class IndexModel : PageModel
{
public void OnGet()
{
var editUrl = Url.Page("./Edit", new { id = 17 });
// ...
}
}
Powyższy kod ustawia wartość url
, /Edit/17
gdy strona edycji Razor zawiera następującą dyrektywę strony:
@page "{id:int}"
Jeśli strona Edycja nie zawiera szablonu "{id:int}"
trasy, url
to /Edit?id=17
.
Zachowanie wzorca MVC IUrlHelper dodaje warstwę złożoności oprócz reguł opisanych tutaj:
IUrlHelper
zawsze udostępnia wartości tras z bieżącego żądania jako wartości otoczenia.action
wartości i controller
trasy jako wartości jawne, chyba że zostanie zastąpione przez dewelopera.page
wartość trasy jako jawną wartość, chyba że zostanie zastąpiona. IUrlHelper.Page
zawsze zastępuje bieżącą handler
wartość trasy wartością null
jawną, chyba że zostanie zastąpiona.Użytkownicy są często zaskoczeni szczegółami zachowania wartości otoczenia, ponieważ MVC nie wydaje się przestrzegać własnych reguł. Ze względów historycznych i zgodności niektóre wartości tras, takie jak action
, controller
, page
i handler
mają własne zachowanie specjalne.
Równoważna funkcjonalność zapewniana przez LinkGenerator.GetPathByAction
program i LinkGenerator.GetPathByPage
duplikuje te anomalie w IUrlHelper
celu zapewnienia zgodności.
Po znalezieniu zestawu punktów końcowych kandydata algorytm generowania adresów URL:
Pierwszym krokiem w tym procesie jest unieważnienie wartości trasy. Unieważnienie wartości trasy to proces, za pomocą którego routing decyduje, które wartości tras z wartości otoczenia powinny być używane i które powinny być ignorowane. Każda wartość otoczenia jest uwzględniana i połączona z jawnymi wartościami lub ignorowana.
Najlepszym sposobem myślenia o roli wartości otoczenia jest to, że próbują zapisać deweloperów aplikacji wpisywanie, w niektórych typowych przypadkach. Tradycyjnie scenariusze, w których wartości otoczenia są przydatne, są związane z MVC:
Wywołania do LinkGenerator
lub IUrlHelper
tego zwracania null
są zwykle spowodowane przez niezrozumienie unieważnienia wartości trasy. Rozwiąż problemy z nieprawidłową wartością trasy, określając jawnie więcej wartości trasy, aby sprawdzić, czy rozwiązuje to problem.
Unieważnienie wartości trasy działa zgodnie z założeniem, że schemat adresu URL aplikacji jest hierarchiczny, z hierarchią utworzoną od lewej do prawej. Rozważmy podstawowy szablon {controller}/{action}/{id?}
trasy kontrolera, aby uzyskać intuicyjne zrozumienie sposobu działania tej metody w praktyce.
Zmiana wartości unieważnia wszystkie wartości trasy, które są wyświetlane po prawej stronie. Odzwierciedla to założenie dotyczące hierarchii. Jeśli aplikacja ma wartość otoczenia dla id
, a operacja określa inną wartość dla elementu controller
:
id
nie będzie ponownie używany, ponieważ {controller}
znajduje się po lewej {id?}
stronie .Kilka przykładów demonstrujących tę zasadę:
id
, wartość otoczenia dla id
elementu jest ignorowana. Wartości otoczenia i controller
action
mogą być używane.action
, każda wartość otoczenia dla action
elementu jest ignorowana. Można użyć wartości controller
otoczenia. Jeśli jawna wartość parametru action
jest inna niż wartość otoczenia dla action
wartości , id
wartość nie zostanie użyta. Jeśli jawna wartość parametru action
jest taka sama jak wartość otoczenia dla action
, id
można użyć wartości .controller
, każda wartość otoczenia dla controller
elementu jest ignorowana. Jeśli jawna wartość parametru controller
jest inna niż wartość otoczenia dla controller
, action
wartości i id
nie będą używane. Jeśli jawna wartość parametru controller
jest taka sama jak wartość otoczenia dla controller
, action
można użyć wartości i id
.Ten proces jest jeszcze bardziej skomplikowany przez istnienie tras atrybutów i dedykowanych tras konwencjonalnych. Kontroler konwencjonalnych tras, takich jak {controller}/{action}/{id?}
określanie hierarchii przy użyciu parametrów trasy. W przypadku dedykowanych tras konwencjonalnych i tras atrybutów do kontrolerów i Razor stron:
W takich przypadkach generowanie adresów URL definiuje koncepcję wymaganych wartości . Punkty końcowe utworzone przez kontrolery i Razor strony mają wymagane wartości określone, które umożliwiają działanie unieważnienia wartości trasy.
Algorytm unieważniania wartości trasy szczegółowo:
W tym momencie operacja generowania adresu URL jest gotowa do oceny ograniczeń trasy. Zestaw akceptowanych wartości jest połączony z wartościami domyślnymi parametrów, które są dostarczane do ograniczeń. Jeśli wszystkie ograniczenia przechodzą, operacja będzie kontynuowana.
Następnie akceptowane wartości mogą służyć do rozszerzania szablonu trasy. Szablon trasy jest przetwarzany:
Wartości jawnie podane, które nie pasują do segmentu trasy, są dodawane do ciągu zapytania. W poniższej tabeli przedstawiono wynik użycia szablonu {controller}/{action}/{id?}
trasy .
Wartości otoczenia | Jawne wartości | Result |
---|---|---|
controller = "Home" | action = "Informacje" | /Home/About |
controller = "Home" | controller = "Order", action = "About" | /Order/About |
controller = "Home", color = "Red" | action = "Informacje" | /Home/About |
controller = "Home" | action = "About", color = "Red" | /Home/About?color=Red |
Opcjonalne parametry trasy muszą pochodzić po wszystkich wymaganych parametrach trasy. W poniższym kodzie id
parametry i name
muszą pochodzić po parametrze color
:
using Microsoft.AspNetCore.Mvc;
namespace WebApplication1.Controllers;
[Route("api/[controller]")]
public class MyController : ControllerBase
{
// GET /api/my/red/2/joe
// GET /api/my/red/2
// GET /api/my
[HttpGet("{color}/{id:int?}/{name?}")]
public IActionResult GetByIdAndOptionalName(string color, int id = 1, string? name = null)
{
return Ok($"{color} {id} {name ?? ""}");
}
}
Poniższy kod przedstawia przykład schematu generowania adresów URL, który nie jest obsługiwany przez routing:
app.MapControllerRoute(
"default",
"{culture}/{controller=Home}/{action=Index}/{id?}");
app.MapControllerRoute(
"blog",
"{culture}/{**slug}",
new { controller = "Blog", action = "ReadPost" });
W poprzednim kodzie culture
parametr trasy jest używany do lokalizacji. Pragnieniem jest, culture
aby parametr zawsze był akceptowany jako wartość otoczenia.
culture
Jednak parametr nie jest akceptowany jako wartość otoczenia ze względu na sposób działania wymaganych wartości:
"default"
culture
trasy parametr trasy znajduje się po lewej stronie controller
elementu , więc zmiany controller
nie będą unieważniać culture
elementu ."blog"
culture
trasy parametr trasy jest uznawany za znajdujący się po prawej stronie controller
elementu , który jest wyświetlany w wymaganych wartościach.Klasa LinkParser dodaje obsługę analizowania ścieżki adresu URL do zestawu wartości tras. Metoda ParsePathByEndpointName przyjmuje nazwę punktu końcowego i ścieżkę adresu URL i zwraca zestaw wartości tras wyodrębnionych ze ścieżki adresu URL.
W poniższym przykładowym kontrolerze akcja GetProduct
używa szablonu api/Products/{id}
trasy i ma NameGetProduct
wartość :
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
[HttpGet("{id}", Name = nameof(GetProduct))]
public IActionResult GetProduct(string id)
{
// ...
W tej samej klasie AddRelatedProduct
kontrolera akcja oczekuje ścieżki adresu URL , pathToRelatedProduct
która może być podana jako parametr ciągu zapytania:
[HttpPost("{id}/Related")]
public IActionResult AddRelatedProduct(
string id, string pathToRelatedProduct, [FromServices] LinkParser linkParser)
{
var routeValues = linkParser.ParsePathByEndpointName(
nameof(GetProduct), pathToRelatedProduct);
var relatedProductId = routeValues?["id"];
// ...
W poprzednim przykładzie akcja AddRelatedProduct
wyodrębnia id
wartość trasy ze ścieżki adresu URL. Na przykład ze ścieżką /api/Products/1
relatedProductId
adresu URL wartości jest ustawiona wartość 1
. Takie podejście umożliwia klientom interfejsu API używanie ścieżek url podczas odwoływania się do zasobów bez konieczności znajomości struktury takiego adresu URL.
Poniższe linki zawierają informacje na temat konfigurowania metadanych punktu końcowego:
[MinimumAgeAuthorize]
RequireHost stosuje ograniczenie do trasy, która wymaga określonego hosta. Parametr RequireHost
lub [Host] może być:
www.domain.com
, pasuje www.domain.com
do dowolnego portu.*.domain.com
, pasuje www.domain.com
do , subdomain.domain.com
lub www.subdomain.domain.com
na dowolnym porcie.*:5000
, pasuje do portu 5000 z dowolnym hostem.www.domain.com:5000
lub *.domain.com:5000
, pasuje do hosta i portu.Można określić wiele parametrów przy użyciu polecenia RequireHost
lub [Host]
. Ograniczenie pasuje do hostów prawidłowych dla dowolnego z parametrów. Na przykład pasuje [Host("domain.com", "*.domain.com")]
do domain.com
, www.domain.com
i subdomain.domain.com
.
Poniższy kod używa RequireHost
metody , aby wymagać określonego hosta na trasie:
app.MapGet("/", () => "Contoso").RequireHost("contoso.com");
app.MapGet("/", () => "AdventureWorks").RequireHost("adventure-works.com");
app.MapHealthChecks("/healthz").RequireHost("*:8080");
Poniższy kod używa atrybutu [Host]
na kontrolerze, aby wymagać któregokolwiek z określonych hostów:
[Host("contoso.com", "adventure-works.com")]
public class HostsController : Controller
{
public IActionResult Index() =>
View();
[Host("example.com")]
public IActionResult Example() =>
View();
}
Po zastosowaniu atrybutu [Host]
zarówno do kontrolera, jak i metody akcji:
Metoda MapGroup rozszerzenia ułatwia organizowanie grup punktów końcowych za pomocą wspólnego prefiksu. Zmniejsza powtarzalny kod i umożliwia dostosowywanie całych grup punktów końcowych za pomocą jednego wywołania metod, takich jak RequireAuthorization i WithMetadata które dodają metadane punktu końcowego.
Na przykład poniższy kod tworzy dwie podobne grupy punktów końcowych:
app.MapGroup("/public/todos")
.MapTodosApi()
.WithTags("Public");
app.MapGroup("/private/todos")
.MapTodosApi()
.WithTags("Private")
.AddEndpointFilterFactory(QueryPrivateTodos)
.RequireAuthorization();
EndpointFilterDelegate QueryPrivateTodos(EndpointFilterFactoryContext factoryContext, EndpointFilterDelegate next)
{
var dbContextIndex = -1;
foreach (var argument in factoryContext.MethodInfo.GetParameters())
{
if (argument.ParameterType == typeof(TodoDb))
{
dbContextIndex = argument.Position;
break;
}
}
// Skip filter if the method doesn't have a TodoDb parameter.
if (dbContextIndex < 0)
{
return next;
}
return async invocationContext =>
{
var dbContext = invocationContext.GetArgument<TodoDb>(dbContextIndex);
dbContext.IsPrivate = true;
try
{
return await next(invocationContext);
}
finally
{
// This should only be relevant if you're pooling or otherwise reusing the DbContext instance.
dbContext.IsPrivate = false;
}
};
}
public static RouteGroupBuilder MapTodosApi(this RouteGroupBuilder group)
{
group.MapGet("/", GetAllTodos);
group.MapGet("/{id}", GetTodo);
group.MapPost("/", CreateTodo);
group.MapPut("/{id}", UpdateTodo);
group.MapDelete("/{id}", DeleteTodo);
return group;
}
W tym scenariuszu możesz użyć względnego adresu nagłówka Location
w 201 Created
wyniku:
public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
await database.AddAsync(todo);
await database.SaveChangesAsync();
return TypedResults.Created($"{todo.Id}", todo);
}
Pierwsza grupa punktów końcowych będzie pasowała tylko do żądań z prefiksem /public/todos
i jest dostępna bez żadnego uwierzytelniania. Druga grupa punktów końcowych będzie pasowała tylko do żądań poprzedzonych prefiksem /private/todos
i wymaga uwierzytelniania.
QueryPrivateTodos
Fabryka filtrów punktów końcowych to funkcja lokalna, która modyfikuje parametry programu obsługi TodoDb
tras, aby umożliwić dostęp do prywatnych danych zadań do wykonania i przechowywanie ich.
Grupy tras obsługują również grupy zagnieżdżone i złożone wzorce prefiksów z parametrami i ograniczeniami trasy. W poniższym przykładzie program obsługi tras zamapowany na grupę user
może przechwytywać {org}
parametry trasy i {group}
zdefiniowane w prefiksach grup zewnętrznych.
Prefiks może być również pusty. Może to być przydatne w przypadku dodawania metadanych lub filtrów punktu końcowego do grupy punktów końcowych bez zmiany wzorca trasy.
var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");
Dodanie filtrów lub metadanych do grupy działa tak samo jak dodawanie ich indywidualnie do każdego punktu końcowego przed dodaniem dodatkowych filtrów lub metadanych, które mogły zostać dodane do grupy wewnętrznej lub określonego punktu końcowego.
var outer = app.MapGroup("/outer");
var inner = outer.MapGroup("/inner");
inner.AddEndpointFilter((context, next) =>
{
app.Logger.LogInformation("/inner group filter");
return next(context);
});
outer.AddEndpointFilter((context, next) =>
{
app.Logger.LogInformation("/outer group filter");
return next(context);
});
inner.MapGet("/", () => "Hi!").AddEndpointFilter((context, next) =>
{
app.Logger.LogInformation("MapGet filter");
return next(context);
});
W powyższym przykładzie zewnętrzny filtr będzie rejestrować żądanie przychodzące przed filtrem wewnętrznym, mimo że został dodany w sekundzie. Ponieważ filtry zostały zastosowane do różnych grup, kolejność, którą zostały dodane względem siebie, nie ma znaczenia. Filtry kolejności są dodawane niezależnie od tego, czy są stosowane do tej samej grupy lub określonego punktu końcowego.
Żądanie, aby zarejestrować /outer/inner/
następujące elementy:
/outer group filter
/inner group filter
MapGet filter
Gdy aplikacja ma problemy z wydajnością, routing jest często podejrzewany jako problem. Podejrzewa się, że routing polega na tym, że struktury, takie jak kontrolery i Razor strony, zgłaszają ilość czasu spędzonego w strukturze w komunikatach rejestrowania. W przypadku znacznej różnicy między czasem zgłoszonym przez kontrolery a łącznym czasem żądania:
Routing jest testowany pod kątem wydajności przy użyciu tysięcy punktów końcowych. Jest mało prawdopodobne, aby typowa aplikacja napotkała problem z wydajnością, ponieważ jest zbyt duża. Najczęstszą główną przyczyną niskiej wydajności routingu jest zwykle nieprawidłowe zachowanie niestandardowego oprogramowania pośredniczącego.
W poniższym przykładzie kodu przedstawiono podstawową technikę zawężania źródła opóźnienia:
var logger = app.Services.GetRequiredService<ILogger<Program>>();
app.Use(async (context, next) =>
{
var stopwatch = Stopwatch.StartNew();
await next(context);
stopwatch.Stop();
logger.LogInformation("Time 1: {ElapsedMilliseconds}ms", stopwatch.ElapsedMilliseconds);
});
app.UseRouting();
app.Use(async (context, next) =>
{
var stopwatch = Stopwatch.StartNew();
await next(context);
stopwatch.Stop();
logger.LogInformation("Time 2: {ElapsedMilliseconds}ms", stopwatch.ElapsedMilliseconds);
});
app.UseAuthorization();
app.Use(async (context, next) =>
{
var stopwatch = Stopwatch.StartNew();
await next(context);
stopwatch.Stop();
logger.LogInformation("Time 3: {ElapsedMilliseconds}ms", stopwatch.ElapsedMilliseconds);
});
app.MapGet("/", () => "Timing Test.");
Routing czasowy:
Jest to podstawowy sposób zawężenia opóźnienia, gdy jest to znaczące, na przykład więcej niż 10ms
. Odejmowanie Time 2
z Time 1
raportów czasu spędzonego wewnątrz oprogramowania pośredniczącego UseRouting
.
Poniższy kod używa bardziej kompaktowego podejścia do poprzedniego kodu chronometrażu:
public sealed class AutoStopwatch : IDisposable
{
private readonly ILogger _logger;
private readonly string _message;
private readonly Stopwatch _stopwatch;
private bool _disposed;
public AutoStopwatch(ILogger logger, string message) =>
(_logger, _message, _stopwatch) = (logger, message, Stopwatch.StartNew());
public void Dispose()
{
if (_disposed)
{
return;
}
_logger.LogInformation("{Message}: {ElapsedMilliseconds}ms",
_message, _stopwatch.ElapsedMilliseconds);
_disposed = true;
}
}
var logger = app.Services.GetRequiredService<ILogger<Program>>();
var timerCount = 0;
app.Use(async (context, next) =>
{
using (new AutoStopwatch(logger, $"Time {++timerCount}"))
{
await next(context);
}
});
app.UseRouting();
app.Use(async (context, next) =>
{
using (new AutoStopwatch(logger, $"Time {++timerCount}"))
{
await next(context);
}
});
app.UseAuthorization();
app.Use(async (context, next) =>
{
using (new AutoStopwatch(logger, $"Time {++timerCount}"))
{
await next(context);
}
});
app.MapGet("/", () => "Timing Test.");
Poniższa lista zawiera wgląd w funkcje routingu, które są stosunkowo kosztowne w porównaniu z podstawowymi szablonami tras:
{x}-{y}-{z}
): Domyślnie ASP.NET Core używa algorytmu routingu, który wymienia pamięć na czas procesora CPU. Ma to miły wpływ, że czas dopasowywania trasy zależy tylko od długości ścieżki do dopasowania, a nie liczby tras. Jednak takie podejście może być potencjalnie problematyczne w niektórych przypadkach, gdy aplikacja ma dużą liczbę tras (w tysiącach) i istnieje duża ilość zmiennych prefiksów w trasach. Jeśli na przykład trasy mają parametry we wczesnych segmentach trasy, na przykład {parameter}/some/literal
.
Jest mało prawdopodobne, aby aplikacja napotkała sytuację, w której jest to problem, chyba że:
Microsoft.AspNetCore.Routing.Matching.DfaNode
wystąpień.Istnieje kilka technik i optymalizacji, które można zastosować do tras, które w dużej mierze poprawią ten scenariusz:
{parameter:int}
, {parameter:guid}
, {parameter:regex(\\d+)}
itp., jeśli to możliwe.
MapDynamicControllerRoute
i MapDynamicPageRoute
.Ta sekcja zawiera wskazówki dotyczące autorów bibliotek opartych na routingu. Te szczegóły mają na celu zapewnienie, że deweloperzy aplikacji mają dobre środowisko korzystania z bibliotek i struktur rozszerzających routing.
Aby utworzyć strukturę korzystającą z routingu do dopasowywania adresów URL, zacznij od zdefiniowania środowiska użytkownika, które opiera się na systemie UseEndpoints.
Kompilacja DO na podstawie funkcji IEndpointRouteBuilder. Dzięki temu użytkownicy mogą tworzyć struktury przy użyciu innych funkcji ASP.NET Core bez pomyłek. Każdy szablon ASP.NET Core zawiera routing. Załóżmy, że routing jest obecny i znany użytkownikom.
// Your framework
app.MapMyFramework(...);
app.MapHealthChecks("/healthz");
NALEŻY zwrócić zapieczętowany typ betonowy z wywołania, do MapMyFramework(...)
którego implementuje IEndpointConventionBuilderelement . Większość metod struktury Map...
jest zgodne z tym wzorcem. Interfejs IEndpointConventionBuilder
:
Deklarowanie własnego typu umożliwia dodanie do konstruktora własnych funkcji specyficznych dla platformy. Można zawinąć konstruktora zadeklarowanego przez platformę i przekazać do niego wywołania.
// Your framework
app.MapMyFramework(...)
.RequireAuthorization()
.WithMyFrameworkFeature(awesome: true);
app.MapHealthChecks("/healthz");
ROZWAŻ napisanie własnego EndpointDataSourcepliku .
EndpointDataSource
jest elementem pierwotnym niskiego poziomu do deklarowania i aktualizowania kolekcji punktów końcowych.
EndpointDataSource
to zaawansowany interfejs API używany przez kontrolery i Razor strony.
Testy routingu mają podstawowy przykład nieakcyjnego źródła danych.
ROZWAŻ zaimplementowanie .GetGroupedEndpoints Zapewnia to pełną kontrolę nad uruchamianiem konwencji grupy i ostatnimi metadanymi w zgrupowanych punktach końcowych. Umożliwia to na przykład niestandardowe EndpointDataSource
implementacje uruchamiania filtrów punktów końcowych dodanych do grup.
NIE próbuj domyślnie rejestrować konta EndpointDataSource
. Wymagaj od użytkowników zarejestrowania struktury w programie UseEndpoints. Filozofią routingu jest to, że nic nie jest dołączane domyślnie i UseEndpoints
jest to miejsce do rejestrowania punktów końcowych.
ROZWAŻ zdefiniowanie typów metadanych jako interfejsu.
DO umożliwia używanie typów metadanych jako atrybutu w klasach i metodach.
public interface ICoolMetadata
{
bool IsCool { get; }
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CoolMetadataAttribute : Attribute, ICoolMetadata
{
public bool IsCool => true;
}
Struktury, takie jak kontrolery i Razor strony, obsługują stosowanie atrybutów metadanych do typów i metod. W przypadku deklarowania typów metadanych:
Deklarowanie typu metadanych jako interfejsu zwiększa kolejną warstwę elastyczności:
NIE umożliwia przesłaniania metadanych, jak pokazano w poniższym przykładzie:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class SuppressCoolMetadataAttribute : Attribute, ICoolMetadata
{
public bool IsCool => false;
}
[CoolMetadata]
public class MyController : Controller
{
public void MyCool() { }
[SuppressCoolMetadata]
public void Uncool() { }
}
Najlepszym sposobem przestrzegania tych wytycznych jest unikanie definiowania metadanych znaczników:
Kolekcja metadanych jest uporządkowana i obsługuje zastępowanie według priorytetu. W przypadku kontrolerów metadane metody akcji są najbardziej specyficzne.
DO sprawia, że oprogramowanie pośredniczące jest przydatne z routingiem i bez tego routingu:
app.UseAuthorization(new AuthorizationPolicy() { ... });
// Your framework
app.MapMyFramework(...).RequireAuthorization();
Jako przykład tej wskazówki należy wziąć pod uwagę UseAuthorization
oprogramowanie pośredniczące. Oprogramowanie pośredniczące autoryzacji umożliwia przekazanie zasad rezerwowych.
Zasady rezerwowe, jeśli zostały określone, mają zastosowanie do obu następujących elementów:
Dzięki temu oprogramowanie pośredniczące autoryzacji jest przydatne poza kontekstem routingu. Oprogramowanie pośredniczące autoryzacji może służyć do tradycyjnego programowania oprogramowania pośredniczącego.
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"
}
}
}
Routing jest odpowiedzialny za dopasowywanie przychodzących żądań HTTP i wysyłanie tych żądań do punktów końcowych wykonywalnych aplikacji. Punkty końcowe to jednostki aplikacji kodu obsługi żądań wykonywalnych. Punkty końcowe są definiowane w aplikacji i konfigurowane podczas uruchamiania aplikacji. Proces dopasowywania punktu końcowego może wyodrębnić wartości z adresu URL żądania i podać te wartości do przetwarzania żądań. Korzystając z informacji o punkcie końcowym z aplikacji, routing jest również w stanie wygenerować adresy URL mapujące na punkty końcowe.
Aplikacje mogą konfigurować routing przy użyciu:
W tym artykule opisano szczegóły niskiego poziomu dotyczące routingu ASP.NET Core. Aby uzyskać informacje na temat konfigurowania routingu:
Poniższy kod przedstawia podstawowy przykład routingu:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Powyższy przykład zawiera pojedynczy punkt końcowy przy użyciu MapGet metody :
GET
do głównego adresu URL /
: Hello World!
jest zapisywany w odpowiedzi HTTP.GET
jest lub główny adres URL nie /
jest , nie jest zgodna trasa i zwracany jest http 404.Routing używa pary oprogramowania pośredniczącego zarejestrowanego przez UseRouting i UseEndpoints:
UseRouting
Dodaje trasę zgodną z potokiem oprogramowania pośredniczącego. To oprogramowanie pośredniczące analizuje zestaw punktów końcowych zdefiniowanych w aplikacji i wybiera najlepsze dopasowanie na podstawie żądania.UseEndpoints
Dodaje wykonywanie punktu końcowego do potoku oprogramowania pośredniczącego. Uruchamia delegata skojarzonego z wybranym punktem końcowym.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
. Jednak aplikacje mogą zmieniać kolejność, w jakiej UseRouting
i UseEndpoints
uruchamiać, wywołując te metody jawnie. Na przykład następujący kod wykonuje jawne wywołanie metody UseRouting
:
app.Use(async (context, next) =>
{
// ...
await next(context);
});
app.UseRouting();
app.MapGet("/", () => "Hello World!");
Powyższy kod:
app.Use
zarejestrowania niestandardowego oprogramowania pośredniczącego uruchamianego na początku potoku.UseRouting
skonfigurowania odpowiedniego oprogramowania pośredniczącego trasy do uruchomienia po niestandardowym oprogramowaniem pośredniczącym.MapGet
przy użyciu jest uruchamiany na końcu potoku.Jeśli poprzedni przykład nie zawierał wywołania metody UseRouting
, niestandardowe oprogramowanie pośredniczące zostanie uruchomione po dopasowaniu trasy oprogramowania pośredniczącego.
Metoda MapGet
służy do definiowania punktu końcowego. Punkt końcowy to coś, co może być następujące:
Punkty końcowe, które mogą być dopasowane i wykonywane przez aplikację, są konfigurowane w programie UseEndpoints
. Na przykład metody MapGet, MapPosti podobne łączą delegatów żądań z systemem routingu. Dodatkowe metody mogą służyć do łączenia funkcji platformy ASP.NET Core z systemem routingu:
W poniższym przykładzie przedstawiono routing z bardziej zaawansowanym szablonem trasy:
app.MapGet("/hello/{name:alpha}", (string name) => $"Hello {name}!");
Ciąg /hello/{name:alpha}
jest szablonem trasy. Szablon trasy służy do konfigurowania sposobu dopasowania punktu końcowego. W takim przypadku szablon jest zgodny z następującymi elementami:
/hello/Docs
/hello/
od sekwencji znaków alfabetycznych.
:alpha
stosuje ograniczenie trasy, które pasuje tylko do znaków alfabetycznych.
Ograniczenia tras zostały wyjaśnione w dalszej części tego artykułu.Drugi segment ścieżki adresu URL: {name:alpha}
name
.W poniższym przykładzie przedstawiono routing z kontrolą kondycji i autoryzacją:
app.UseAuthentication();
app.UseAuthorization();
app.MapHealthChecks("/healthz").RequireAuthorization();
app.MapGet("/", () => "Hello World!");
W poprzednim przykładzie pokazano, jak:
Wywołanie MapHealthChecks dodaje punkt końcowy kontroli kondycji. Łączenie RequireAuthorization łańcucha do tego wywołania powoduje dołączenie zasad autoryzacji do punktu końcowego.
Wywoływanie UseAuthentication i UseAuthorization dodawanie oprogramowania pośredniczącego uwierzytelniania i autoryzacji. Te oprogramowanie pośredniczące są umieszczane między elementami UseRouting i UseEndpoints
, dzięki czemu mogą:
UseRouting
element .W poprzednim przykładzie istnieją dwa punkty końcowe, ale tylko punkt końcowy kontroli kondycji ma dołączone zasady autoryzacji. Jeśli żądanie pasuje do punktu końcowego sprawdzania kondycji, /healthz
zostanie wykonane sprawdzanie autoryzacji. Pokazuje to, że punkty końcowe mogą mieć dołączone dodatkowe dane. Te dodatkowe dane są nazywane metadanymi punktu końcowego:
System routingu opiera się na potoku oprogramowania pośredniczącego, dodając zaawansowaną koncepcję punktu końcowego . Punkty końcowe reprezentują jednostki funkcjonalności aplikacji, które różnią się od siebie pod względem routingu, autoryzacji i dowolnej liczby systemów ASP.NET Core.
Punkt końcowy platformy ASP.NET Core to:
Poniższy kod pokazuje, jak pobrać i sprawdzić punkt końcowy pasujący do bieżącego żądania:
app.Use(async (context, next) =>
{
var currentEndpoint = context.GetEndpoint();
if (currentEndpoint is null)
{
await next(context);
return;
}
Console.WriteLine($"Endpoint: {currentEndpoint.DisplayName}");
if (currentEndpoint is RouteEndpoint routeEndpoint)
{
Console.WriteLine($" - Route Pattern: {routeEndpoint.RoutePattern}");
}
foreach (var endpointMetadata in currentEndpoint.Metadata)
{
Console.WriteLine($" - Metadata: {endpointMetadata}");
}
await next(context);
});
app.MapGet("/", () => "Inspect Endpoint.");
Punkt końcowy, jeśli został wybrany, można pobrać z pliku HttpContext
. Jego właściwości można sprawdzić. Obiekty punktu końcowego są niezmienne i nie można ich modyfikować po utworzeniu. Najczęstszym typem RouteEndpointpunktu końcowego jest .
RouteEndpoint
zawiera informacje, które umożliwiają wybranie go przez system routingu.
W poprzednim kodzie aplikacja. Użyj konfiguracji wbudowanego oprogramowania pośredniczącego.
Poniższy kod pokazuje, że w zależności od tego, gdzie app.Use
jest wywoływana w potoku, punkt końcowy może nie być:
// Location 1: before routing runs, endpoint is always null here.
app.Use(async (context, next) =>
{
Console.WriteLine($"1. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
await next(context);
});
app.UseRouting();
// Location 2: after routing runs, endpoint will be non-null if routing found a match.
app.Use(async (context, next) =>
{
Console.WriteLine($"2. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
await next(context);
});
// Location 3: runs when this endpoint matches
app.MapGet("/", (HttpContext context) =>
{
Console.WriteLine($"3. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
return "Hello World!";
}).WithDisplayName("Hello");
app.UseEndpoints(_ => { });
// Location 4: runs after UseEndpoints - will only run if there was no match.
app.Use(async (context, next) =>
{
Console.WriteLine($"4. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
await next(context);
});
Poprzedni przykład dodaje Console.WriteLine
instrukcje, które wyświetlają, czy wybrano punkt końcowy. Aby uzyskać czytelność, przykład przypisuje nazwę wyświetlaną do podanego /
punktu końcowego.
Powyższy przykład obejmuje również wywołania i UseRouting
UseEndpoints
sterowanie dokładnie tym, kiedy oprogramowanie pośredniczące działa w potoku.
Uruchomienie tego kodu z adresem URL wyświetlania /
:
1. Endpoint: (null)
2. Endpoint: Hello
3. Endpoint: Hello
Uruchomienie tego kodu przy użyciu dowolnego innego adresu URL powoduje wyświetlenie:
1. Endpoint: (null)
2. Endpoint: (null)
4. Endpoint: (null)
Te dane wyjściowe pokazują, że:
UseRouting
wywołaniem.UseRouting
i UseEndpoints.UseEndpoints
pośredniczące jest terminalem po znalezieniu dopasowania.
Oprogramowanie pośredniczące terminala jest zdefiniowane w dalszej części tego artykułu.UseEndpoints
wykonaniu tylko wtedy, gdy nie zostanie znalezione dopasowanie.Oprogramowanie UseRouting
pośredniczące używa SetEndpoint metody w celu dołączenia punktu końcowego do bieżącego kontekstu. Można zastąpić UseRouting
oprogramowanie pośredniczące logiką niestandardową i nadal korzystać z zalet korzystania z punktów końcowych. Punkty końcowe są typami pierwotnymi niskiego poziomu, takimi jak oprogramowanie pośredniczące, i nie są powiązane z implementacją routingu. Większość aplikacji nie musi zastępować UseRouting
logiką niestandardową.
Oprogramowanie UseEndpoints
pośredniczące jest przeznaczone do użycia w parze z oprogramowaniem pośredniczącym UseRouting
. Podstawowa logika do wykonania punktu końcowego nie jest skomplikowana. Użyj GetEndpoint polecenia , aby pobrać punkt końcowy, a następnie wywołać jego RequestDelegate właściwość.
Poniższy kod pokazuje, jak oprogramowanie pośredniczące może wpływać na routing lub reagować na nie:
app.UseHttpMethodOverride();
app.UseRouting();
app.Use(async (context, next) =>
{
if (context.GetEndpoint()?.Metadata.GetMetadata<RequiresAuditAttribute>() is not null)
{
Console.WriteLine($"ACCESS TO SENSITIVE DATA AT: {DateTime.UtcNow}");
}
await next(context);
});
app.MapGet("/", () => "Audit isn't required.");
app.MapGet("/sensitive", () => "Audit required for sensitive data.")
.WithMetadata(new RequiresAuditAttribute());
public class RequiresAuditAttribute : Attribute { }
W poprzednim przykładzie przedstawiono dwie ważne pojęcia:
UseRouting
zmodyfikowaniem danych, na których działa routing.
UseRouting
i UseEndpoints w celu przetworzenia wyników routingu przed wykonaniem punktu końcowego.
UseRouting
i UseEndpoints
: UseAuthorization
i UseCors
.Powyższy kod przedstawia przykład niestandardowego oprogramowania pośredniczącego obsługującego zasady poszczególnych punktów końcowych. Oprogramowanie pośredniczące zapisuje dziennik inspekcji dostępu do poufnych danych w konsoli. Oprogramowanie pośredniczące można skonfigurować do inspekcji punktu końcowego za RequiresAuditAttribute
pomocą metadanych. W tym przykładzie pokazano wzorzec zgody, w którym są poddawane inspekcji tylko punkty końcowe oznaczone jako poufne. Tę logikę można zdefiniować w odwrotnej kolejności, przeprowadzając inspekcję wszystkich elementów, które nie są oznaczone jako bezpieczne, na przykład. System metadanych punktu końcowego jest elastyczny. Ta logika może być zaprojektowana w dowolny sposób odpowiednio do przypadku użycia.
Powyższy przykładowy kod ma na celu zademonstrowanie podstawowych pojęć dotyczących punktów końcowych. Przykład nie jest przeznaczony do użytku produkcyjnego. Bardziej kompletna wersja oprogramowania pośredniczącego dziennika inspekcji:
Metadane RequiresAuditAttribute
zasad inspekcji są definiowane jako Attribute
łatwiejsze w użyciu z platformami opartymi na klasach, takimi jak kontrolery i SignalR. W przypadku używania trasy do kodu:
Najlepszym rozwiązaniem dla typów metadanych jest zdefiniowanie ich jako interfejsów lub atrybutów. Interfejsy i atrybuty umożliwiają ponowne użycie kodu. System metadanych jest elastyczny i nie nakłada żadnych ograniczeń.
W poniższym przykładzie pokazano zarówno oprogramowanie pośredniczące terminala, jak i routing:
// Approach 1: Terminal Middleware.
app.Use(async (context, next) =>
{
if (context.Request.Path == "/")
{
await context.Response.WriteAsync("Terminal Middleware.");
return;
}
await next(context);
});
app.UseRouting();
// Approach 2: Routing.
app.MapGet("/Routing", () => "Routing.");
Styl oprogramowania pośredniczącego pokazany za pomocą Approach 1:
programu to oprogramowanie pośredniczące terminalu. Jest to nazywane oprogramowaniem pośredniczącym terminalu, ponieważ wykonuje zgodną operację:
Path == "/"
oprogramowania pośredniczącego i Path == "/Routing"
routingu.next
.Jest to nazywane oprogramowaniem pośredniczącym terminalu, ponieważ kończy wyszukiwanie, wykonuje niektóre funkcje, a następnie zwraca.
Poniższa lista porównuje oprogramowanie pośredniczące terminala z routingiem:
next
UseAuthorization
i UseCors
.
UseAuthorization
oprogramowaniem pośredniczącym lub UseCors
wymaga ręcznego łączenia się z systemem autoryzacji.Punkt końcowy definiuje oba:
Oprogramowanie pośredniczące terminalu może być skutecznym narzędziem, ale może wymagać:
Przed napisaniem oprogramowania pośredniczącego terminalu rozważ integrację z routingiem.
Istniejące oprogramowanie pośredniczące terminalu integrujące się z usługą Map lub MapWhen zwykle może zostać przekształcone w punkt końcowy obsługujący routing. MapHealthChecks demonstruje wzorzec oprogramowania routera:
Map
i podaj nowy potok oprogramowania pośredniczącego.Map
metodę rozszerzenia.Poniższy kod przedstawia użycie narzędzi MapHealthChecks:
app.UseAuthentication();
app.UseAuthorization();
app.MapHealthChecks("/healthz").RequireAuthorization();
W poprzednim przykładzie pokazano, dlaczego zwracanie obiektu konstruktora jest ważne. Zwracanie obiektu konstruktora umożliwia deweloperowi aplikacji konfigurowanie zasad, takich jak autoryzacja dla punktu końcowego. W tym przykładzie oprogramowanie pośredniczące kontroli kondycji nie ma bezpośredniej integracji z systemem autoryzacji.
System metadanych został utworzony w odpowiedzi na problemy napotykane przez autorów rozszerzeń przy użyciu oprogramowania pośredniczącego terminalu. Dla każdego oprogramowania pośredniczącego problematyczne jest zaimplementowanie własnej integracji z systemem autoryzacji.
Po wykonaniu oprogramowania pośredniczącego routingu ustawia Endpoint
ona wartości i kieruje je do funkcji żądania z HttpContext bieżącego żądania:
HttpRequest.RouteValues
pobiera kolekcję wartości tras.Oprogramowanie pośredniczące jest uruchamiane po tym, jak oprogramowanie pośredniczące routingu może sprawdzić punkt końcowy i podjąć działania. Na przykład oprogramowanie pośredniczące autoryzacji może przesłuchić kolekcję metadanych punktu końcowego dla zasad autoryzacji. Po wykonaniu całego oprogramowania pośredniczącego w potoku przetwarzania żądań wywoływany jest delegat wybranego punktu końcowego.
System routingu w routingu punktów końcowych jest odpowiedzialny za wszystkie decyzje dotyczące wysyłania. Ponieważ oprogramowanie pośredniczące stosuje zasady na podstawie wybranego punktu końcowego, ważne jest, aby:
Ostrzeżenie
W przypadku zgodności z poprzednimi wersjami po wykonaniu delegata punktu końcowego kontroler lub Razor strony właściwości RouteContext.RouteData są ustawione na odpowiednie wartości na podstawie przetwarzania żądań wykonanego do tej pory.
Typ RouteContext
zostanie oznaczony jako przestarzały w przyszłej wersji:
RouteData.Values
do HttpRequest.RouteValues
.RouteData.DataTokens
w celu pobrania IDataTokensMetadata z metadanych punktu końcowego.Dopasowywanie adresów URL działa w konfigurowalnym zestawie faz. W każdej fazie dane wyjściowe są zestawem dopasowań. Zestaw dopasowań można zawęzić dalej przez następną fazę. Implementacja routingu nie gwarantuje kolejności przetwarzania pasujących punktów końcowych. Wszystkie możliwe dopasowania są przetwarzane jednocześnie. Fazy dopasowywania adresów URL są wykonywane w następującej kolejności. ASP.NET Core:
Lista punktów końcowych jest priorytetowa zgodnie z:
Wszystkie pasujące punkty końcowe są przetwarzane w każdej fazie do momentu EndpointSelector osiągnięcia. Jest EndpointSelector
to ostatnia faza. Wybiera punkt końcowy o najwyższym priorytcie z dopasowań jako najlepszy. Jeśli istnieją inne dopasowania z tym samym priorytetem co najlepsze dopasowanie, zgłaszany jest niejednoznaczny wyjątek dopasowania.
Pierwszeństwo trasy jest obliczane na podstawie bardziej szczegółowego szablonu trasy o wyższym priorytecie. Rozważmy na przykład szablony /hello
i /{message}
:
/hello
adresu URL ./hello
jest bardziej szczegółowy i dlatego wyższy priorytet.Ogólnie rzecz biorąc, pierwszeństwo trasy dobrze nadaje się do wyboru najlepszego dopasowania do rodzajów schematów adresów URL używanych w praktyce. Używaj Order tylko wtedy, gdy jest to konieczne, aby uniknąć niejednoznaczności.
Ze względu na rodzaj rozszerzalności zapewnianej przez routing nie jest możliwe, aby system routingu obliczał przed upływem czasu niejednoznaczne trasy. Rozważmy przykład, taki jak szablony /{message:alpha}
tras i /{message:int}
:
alpha
pasuje tylko do znaków alfabetycznych.int
pasuje tylko do liczb.Ostrzeżenie
Kolejność operacji wewnątrz UseEndpoints nie ma wpływu na zachowanie routingu z jednym wyjątkiem. MapControllerRoute i MapAreaRoute automatycznie przypisz wartość zamówienia do swoich punktów końcowych na podstawie kolejności, w której są wywoływane. Symuluje to długotrwałe zachowanie kontrolerów bez systemu routingu zapewniające takie same gwarancje jak starsze implementacje routingu.
Routing punktów końcowych w ASP.NET Core:
Pierwszeństwo szablonu trasy to system, który przypisuje każdemu szablonowi trasy wartość na podstawie jego specyfiki. Pierwszeństwo szablonu trasy:
Rozważmy na przykład szablony /Products/List
i /Products/{id}
. Uzasadnione byłoby założenie, że /Products/List
jest to lepsze dopasowanie niż /Products/{id}
dla ścieżki /Products/List
adresu URL . Działa to, ponieważ segment /List
literału jest uznawany za lepszy priorytet niż segment /{id}
parametrów .
Szczegółowe informacje o sposobie działania pierwszeństwa są powiązane z definiowaniem szablonów tras:
Generowanie adresu URL:
Routing punktów końcowych obejmuje LinkGenerator interfejs API.
LinkGenerator
jest pojedynczą usługą dostępną z di. Interfejs LinkGenerator
API może być używany poza kontekstem wykonywania żądania.
Mvc.IUrlHelper i scenariusze, które opierają się na IUrlHelperelementach , takich jak Pomocnicy tagów, Pomocnicy HTML i Wyniki akcji, używają interfejsu LinkGenerator
API wewnętrznie, aby zapewnić możliwości generowania linków.
Generator linków jest wspierany przez koncepcję schematów adresów i adresów. Schemat adresów to sposób określania punktów końcowych, które powinny być brane pod uwagę podczas generowania linków. Na przykład scenariusze nazw tras i wartości tras, które wielu użytkowników zna z kontrolerów i Razor stron, są implementowane jako schemat adresów.
Generator linków może łączyć się z kontrolerami i Razor stronami za pomocą następujących metod rozszerzeń:
Przeciążenia tych metod akceptują argumenty, które obejmują HttpContext
element . Te metody są funkcjonalnie równoważne url.Action i Url.Page, ale oferują dodatkową elastyczność i opcje.
Metody GetPath*
są najbardziej podobne do Url.Action
metod i Url.Page
, w których generują identyfikator URI zawierający ścieżkę bezwzględną. Metody GetUri*
zawsze generują bezwzględny identyfikator URI zawierający schemat i host. Metody, które akceptują HttpContext
generowanie identyfikatora URI w kontekście wykonywania żądania. Wartości trasy otoczenia , ścieżka podstawowa adresu URL, schemat i host z wykonywanego żądania są używane, chyba że zostaną zastąpione.
LinkGenerator jest wywoływana przy użyciu adresu. Generowanie identyfikatora URI odbywa się w dwóch krokach:
Metody udostępniane przez LinkGenerator obsługę standardowych możliwości generowania linków dla dowolnego typu adresu. Najwygodniejszym sposobem korzystania z generatora linków jest użycie metod rozszerzeń, które wykonują operacje dla określonego typu adresu:
Extension, metoda | opis |
---|---|
GetPathByAddress | Generuje identyfikator URI ze ścieżką bezwzględną na podstawie podanych wartości. |
GetUriByAddress | Generuje bezwzględny identyfikator URI na podstawie podanych wartości. |
Ostrzeżenie
Zwróć uwagę na następujące konsekwencje wywoływania LinkGenerator metod:
Użyj GetUri*
metod rozszerzeń z ostrożnością w konfiguracji aplikacji, która nie weryfikuje Host
nagłówka żądań przychodzących.
Host
Jeśli nagłówek żądań przychodzących nie jest weryfikowany, niezaufane dane wejściowe żądania mogą być wysyłane z powrotem do klienta w identyfikatorach URI w widoku lub na stronie. Zalecamy skonfigurowanie serwera przez wszystkie aplikacje produkcyjne w celu zweryfikowania nagłówka Host
pod kątem znanych prawidłowych wartości.
Należy używać LinkGenerator z ostrożnością w oprogramowania pośredniczącego w połączeniu z Map
lub MapWhen
.
Map*
zmienia ścieżkę podstawową wykonywania żądania, co wpływa na dane wyjściowe generowania linków.
LinkGenerator Wszystkie interfejsy API umożliwiają określenie ścieżki podstawowej. Określ pustą ścieżkę podstawową, aby cofnąć wpływ na generowanie linków Map*
.
W poniższym przykładzie oprogramowanie pośredniczące używa interfejsu LinkGenerator API do utworzenia linku do metody akcji zawierającej listę produktów. Użycie generatora linków przez wstrzyknięcie go do klasy i wywołanie GenerateLink
jest dostępne dla dowolnej klasy w aplikacji:
public class ProductsMiddleware
{
private readonly LinkGenerator _linkGenerator;
public ProductsMiddleware(RequestDelegate next, LinkGenerator linkGenerator) =>
_linkGenerator = linkGenerator;
public async Task InvokeAsync(HttpContext httpContext)
{
httpContext.Response.ContentType = MediaTypeNames.Text.Plain;
var productsPath = _linkGenerator.GetPathByAction("Products", "Store");
await httpContext.Response.WriteAsync(
$"Go to {productsPath} to see our products.");
}
}
Tokeny w ramach {}
definicji parametrów trasy, które są powiązane, jeśli trasa jest zgodna. W segmencie trasy można zdefiniować więcej niż jeden parametr trasy, ale parametry trasy muszą być rozdzielone wartością literału. Na przykład:
{controller=Home}{action=Index}
nie jest prawidłową trasą, ponieważ nie ma wartości literału między {controller}
i {action}
. Parametry trasy muszą mieć nazwę i mogą mieć określone dodatkowe atrybuty.
Tekst literału inny niż parametry trasy (na przykład {id}
) i separator /
ścieżki musi być zgodny z tekstem w adresie URL. Dopasowywanie tekstu jest bez uwzględniania wielkości liter i oparte na dekodowanej reprezentacji ścieżki adresu URL. Aby dopasować ogranicznik {
parametru trasy literału lub }
, należy uruchomić ogranicznik, powtarzając znak. Na przykład {{
lub }}
.
Gwiazdka *
lub podwójna gwiazdka **
:
blog/{**slug}
blog/
i ma dowolną wartość po niej.blog/
jest przypisywana do wartości trasy slug .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:
{**slug}"
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.
Parametry catch-all mogą być również zgodne z pustym ciągiem.
Parametr catch-all ucieczki odpowiednich znaków, gdy trasa jest używana do generowania adresu URL, w tym znaków separatora /
ścieżki. Na przykład trasa foo/{*path}
z wartościami { path = "my/path" }
tras generuje foo/my%2Fpath
wartość . Zwróć uwagę na unikniętą ukośnik. Aby użyć znaków separatora ścieżki dwukierunkowej, użyj prefiksu parametru **
trasy. Trasa foo/{**path}
z elementem { path = "my/path" }
generuje foo/my/path
wartość .
Wzorce adresów URL, które próbują przechwycić nazwę pliku z opcjonalnym rozszerzeniem pliku, mają dodatkowe uwagi. Rozważmy na przykład szablon files/{filename}.{ext?}
. Gdy wartości obu filename
i ext
istnieją, oba wartości są wypełniane. Jeśli w adresie URL istnieje tylko wartość filename
, trasa jest zgodna, ponieważ końcowy .
jest opcjonalny. Następujące adresy URL pasują do tej trasy:
/files/myFile.txt
/files/myFile
Parametry trasy mogą mieć wartości domyślne wyznaczone przez określenie wartości domyślnej po nazwie parametru oddzielonej znakiem równości (=
). Na przykład {controller=Home}
definiuje Home
jako wartość domyślną dla elementu controller
. Wartość domyślna jest używana, jeśli w adresie URL parametru nie ma żadnej wartości. Parametry trasy są opcjonalne, dołączając znak zapytania (?
) na końcu nazwy parametru. Na przykład id?
. Różnica między opcjonalnymi wartościami a domyślnymi parametrami trasy to:
Parametry trasy mogą mieć ograniczenia, które muszą być zgodne z wartością trasy powiązaną z adresem URL. Dodanie :
i ograniczenie nazwy po nazwie parametru trasy określa ograniczenie wbudowane w parametrze trasy. Jeśli ograniczenie wymaga argumentów, są one ujęte w nawiasy (...)
po nazwie ograniczenia. Można określić wiele ograniczeń wbudowanych przez dołączenie innej :
nazwy i ograniczenia.
Nazwa ograniczenia i argumenty są przekazywane do IInlineConstraintResolver usługi w celu utworzenia wystąpienia IRouteConstraint do użycia w przetwarzaniu adresów URL. Na przykład szablon blog/{article:minlength(10)}
trasy określa minlength
ograniczenie z argumentem 10
. Aby uzyskać więcej informacji na temat ograniczeń tras i listy ograniczeń udostępnianych przez platformę, zobacz sekcję Ograniczenia trasy.
Parametry trasy mogą również zawierać transformatory parametrów. Transformatory parametrów przekształcają wartość parametru podczas generowania łączy i pasujących akcji i stron do adresów URL. Podobnie jak ograniczenia, transformatory parametrów można dodać w tekście do parametru trasy przez dodanie :
nazwy parametru i transformatora po nazwie parametru trasy. Na przykład szablon blog/{article:slugify}
trasy określa slugify
transformator. Aby uzyskać więcej informacji na temat transformatorów parametrów, zobacz sekcję Transformatory parametrów .
W poniższej tabeli przedstawiono przykładowe szablony tras i ich zachowanie:
Szablon trasy | Przykładowy pasujący identyfikator URI | Identyfikator URI żądania... |
---|---|---|
hello |
/hello |
Pasuje tylko do pojedynczej ścieżki /hello . |
{Page=Home} |
/ |
Dopasuj i ustawia wartość Page Home . |
{Page=Home} |
/Contact |
Dopasuj i ustawia wartość Page Contact . |
{controller}/{action}/{id?} |
/Products/List |
Mapuje na Products kontroler i List akcję. |
{controller}/{action}/{id?} |
/Products/Details/123 |
Mapuje na Products kontroler i Details akcję z ustawioną wartościąid 123. |
{controller=Home}/{action=Index}/{id?} |
/ |
Mapuje na kontroler i Home metodęIndex . Właściwość id jest ignorowana. |
{controller=Home}/{action=Index}/{id?} |
/Products |
Mapuje na kontroler i Products metodęIndex . Właściwość id jest ignorowana. |
Użycie szablonu jest zwykle najprostszym podejściem do routingu. Ograniczenia i wartości domyślne można również określić poza szablonem trasy.
Złożone segmenty są przetwarzane przez dopasowanie ograniczników literału od prawej do lewej w sposób niechłanny . Na przykład [Route("/a{b}c{d}")]
jest to złożony segment.
Złożone segmenty działają w określony sposób, aby można było z nich korzystać pomyślnie. W przykładzie w tej sekcji pokazano, dlaczego złożone segmenty działają prawidłowo tylko wtedy, gdy tekst ogranicznika nie pojawia się wewnątrz wartości parametrów. Użycie wyrażenia regularnego, a następnie ręczne wyodrębnienie wartości jest wymagane w przypadku bardziej złożonych przypadków.
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.
Jest to podsumowanie kroków, które routing wykonuje przy użyciu szablonu /a{b}c{d}
i ścieżki /abcd
adresu URL . Służy |
do wizualizacji sposobu działania algorytmu:
c
. Dlatego /abcd
jest wyszukiwany z prawej strony i znajduje /ab|c|d
.d
) jest teraz dopasowane do parametru {d}
trasy .a
. Dlatego /ab|c|d
jest wyszukiwany, zaczynając od miejsca, w którym odeszliśmy, a następnie a
znajduje się /|a|b|c|d
.b
) jest teraz zgodna z parametrem {b}
trasy .Oto przykład negatywnego przypadku przy użyciu tego samego szablonu /a{b}c{d}
i ścieżki /aabcd
adresu URL . Służy |
do wizualizowania sposobu działania algorytmu. Ten przypadek nie jest zgodny, co zostało wyjaśnione przez ten sam algorytm:
c
. Dlatego /aabcd
jest wyszukiwany z prawej strony i znajduje /aab|c|d
.d
) jest teraz dopasowane do parametru {d}
trasy .a
. Dlatego /aab|c|d
jest wyszukiwany, zaczynając od miejsca, w którym odeszliśmy, a następnie a
znajduje się /a|a|b|c|d
.b
) jest teraz zgodna z parametrem {b}
trasy .a
, ale algorytm zabrakło szablonu trasy do przeanalizowania, więc nie jest to dopasowanie.Ponieważ pasujący algorytm nie jest chciwy:
Wyrażenia regularne zapewniają znacznie większą kontrolę nad ich zachowaniem dopasowania.
Zachłanne dopasowanie, znane również jako leniwe dopasowanie, pasuje do największego możliwego ciągu. Niechłanny pasuje do najmniejszego możliwego ciągu.
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;
Ograniczenia trasy są wykonywane, gdy wystąpiło dopasowanie do przychodzącego adresu URL, a ścieżka adresu URL jest tokenizowana do wartości tras. Ograniczenia tras zazwyczaj sprawdzają wartość trasy skojarzoną za pośrednictwem szablonu trasy i podejmowania prawdziwej lub fałszywej decyzji o tym, czy wartość jest akceptowalna. Niektóre ograniczenia trasy używają danych poza wartością trasy, aby rozważyć, czy żądanie można kierować. Na przykład HttpMethodRouteConstraint może zaakceptować lub odrzucić żądanie na podstawie jego czasownika HTTP. Ograniczenia są używane w żądaniach routingu i generowaniu linków.
Ostrzeżenie
Nie używaj ograniczeń do walidacji danych wejściowych. Jeśli ograniczenia są używane do walidacji danych wejściowych, nieprawidłowe dane wejściowe będą skutkować odpowiedzią 404
Nie znaleziono. Nieprawidłowe dane wejściowe powinny spowodować wygenerowanie nieprawidłowego 400
żądania z odpowiednim komunikatem o błędzie. Ograniczenia trasy służą do uściślania podobnych tras, a nie sprawdzania poprawności danych wejściowych dla określonej trasy.
W poniższej tabeli przedstawiono przykładowe ograniczenia tras i ich oczekiwane zachowanie:
ograniczenie | Przykład | Przykładowe dopasowania | Uwagi |
---|---|---|---|
int |
{id:int} |
123456789 , -123456789 |
Pasuje do dowolnej liczby całkowitej |
bool |
{active:bool} |
true , FALSE |
Dopasuj lub true false . Bez uwzględniania wielkości liter |
datetime |
{dob:datetime} |
2016-12-31 , 2016-12-31 7:32pm |
Pasuje do prawidłowej DateTime wartości w niezmiennej kulturze. Zobacz poprzednie ostrzeżenie. |
decimal |
{price:decimal} |
49.99 , -1,000.01 |
Pasuje do prawidłowej decimal wartości w niezmiennej kulturze. Zobacz poprzednie ostrzeżenie. |
double |
{weight:double} |
1.234 , -1,001.01e8 |
Pasuje do prawidłowej double wartości w niezmiennej kulturze. Zobacz poprzednie ostrzeżenie. |
float |
{weight:float} |
1.234 , -1,001.01e8 |
Pasuje do prawidłowej float wartości w niezmiennej kulturze. Zobacz poprzednie ostrzeżenie. |
guid |
{id:guid} |
CD2C1638-1638-72D5-1638-DEADBEEF1638 |
Pasuje do prawidłowej Guid wartości |
long |
{ticks:long} |
123456789 , -123456789 |
Pasuje do prawidłowej long wartości |
minlength(value) |
{username:minlength(4)} |
Rick |
Ciąg musi mieć co najmniej 4 znaki |
maxlength(value) |
{filename:maxlength(8)} |
MyFile |
Ciąg nie może zawierać więcej niż 8 znaków |
length(length) |
{filename:length(12)} |
somefile.txt |
Ciąg musi mieć długość dokładnie 12 znaków |
length(min,max) |
{filename:length(8,16)} |
somefile.txt |
Ciąg musi mieć co najmniej 8 znaków i nie więcej niż 16 znaków |
min(value) |
{age:min(18)} |
19 |
Wartość całkowita musi być co najmniej 18 |
max(value) |
{age:max(120)} |
91 |
Wartość całkowita nie może być większa niż 120 |
range(min,max) |
{age:range(18,120)} |
91 |
Wartość całkowita musi być co najmniej 18, ale nie większa niż 120 |
alpha |
{name:alpha} |
Rick |
Ciąg musi składać się z co najmniej jednego znaku a -z alfabetycznego i bez uwzględniania wielkości liter. |
regex(expression) |
{ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)} |
123-45-6789 |
Ciąg musi być zgodny z wyrażeniem regularnym. Zobacz porady dotyczące definiowania wyrażenia regularnego. |
required |
{name:required} |
Rick |
Służy do wymuszania, że wartość nieparametrowa jest obecna podczas generowania adresu URL |
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 ograniczeń rozdzielonych dwukropkami można zastosować do jednego parametru. Na przykład następujące ograniczenie ogranicza parametr do wartości całkowitej 1 lub większej:
[Route("users/{id:int:min(1)}")]
public User GetUserById(int id) { }
Ostrzeżenie
Ograniczenia trasy, które weryfikują adres URL i są konwertowane na typ CLR, zawsze używają niezmiennej kultury. Na przykład konwersja na typ int
CLR lub DateTime
. Te ograniczenia zakładają, że adres URL nie jest lokalizowalny. Ograniczenia trasy dostarczone przez platformę nie modyfikują wartości przechowywanych w wartościach tras. Wszystkie wartości tras analizowane z adresu URL są przechowywane jako ciągi. Na przykład float
ograniczenie próbuje przekonwertować wartość trasy na zmiennoprzecinkowy, ale przekonwertowana wartość jest używana tylko do sprawdzania, czy można ją przekonwertować na zmiennoprzecinkowy.
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.
Wyrażenia regularne można określić jako ograniczenia wbudowane przy użyciu regex(...)
ograniczenia trasy. Metody w MapControllerRoute rodzinie akceptują również literał obiektów ograniczeń. Jeśli ten formularz jest używany, wartości ciągów są interpretowane jako wyrażenia regularne.
Poniższy kod używa wbudowanego ograniczenia wyrażeń regularnych:
app.MapGet("{message:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)}",
() => "Inline Regex Constraint Matched");
Poniższy kod używa literału obiektu do określenia ograniczenia wyrażenia regularnego:
app.MapControllerRoute(
name: "people",
pattern: "people/{ssn}",
constraints: new { ssn = "^\\d{3}-\\d{2}-\\d{4}$", },
defaults: new { controller = "People", action = "List" });
Struktura ASP.NET Core dodaje RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant
do konstruktora wyrażeń regularnych. Zobacz RegexOptions opis tych elementów członkowskich.
Wyrażenia regularne używają ograniczników i tokenów podobnych do tych używanych przez routing i język C#. Tokeny wyrażeń regularnych muszą zostać uniknięci. Aby użyć wyrażenia ^\d{3}-\d{2}-\d{4}$
regularnego w ograniczeniu wbudowanym, użyj jednego z następujących elementów:
\
znaki podane w ciągu jako \\
znaki w pliku źródłowym języka C#, aby uniknąć \
znaku ucieczki ciągu.Aby uniknąć znaków {
ogranicznika parametru routingu , , }
[
, ]
, podwaja znaki w wyrażeniu, na przykład {{
, , }}
, [[
, ]]
. W poniższej tabeli przedstawiono wyrażenie regularne i jego wersję unikniętą:
Regular expression | Wyrażenie regularne o wartości ucieczki |
---|---|
^\d{3}-\d{2}-\d{4}$ |
^\\d{{3}}-\\d{{2}}-\\d{{4}}$ |
^[a-z]{2}$ |
^[[a-z]]{{2}}$ |
Wyrażenia regularne używane w routingu często zaczynają się od ^
znaku i pasują do pozycji początkowej ciągu. Wyrażenia często kończą się znakiem $
i pasują do końca ciągu. Znaki ^
i $
zapewniają, że wyrażenie regularne jest zgodne z całą wartością parametru trasy.
^
Bez znaków i $
wyrażenie regularne pasuje do dowolnego podciągu w ciągu, co jest często niepożądane. W poniższej tabeli przedstawiono przykłady i wyjaśniono, dlaczego są one zgodne lub nie są zgodne:
Wyrażenie | String | Match | Komentarz |
---|---|---|---|
[a-z]{2} |
hello | Tak | Dopasowania podciągów |
[a-z]{2} |
123abc456 | Tak | Dopasowania podciągów |
[a-z]{2} |
mz | Tak | Dopasuj wyrażenie |
[a-z]{2} |
MZ | Tak | Nie uwzględnia wielkości liter |
^[a-z]{2}$ |
hello | Nie. | Zobacz ^ i $ powyżej |
^[a-z]{2}$ |
123abc456 | Nie. | Zobacz ^ i $ powyżej |
Aby uzyskać więcej informacji na temat składni wyrażeń regularnych, zobacz .NET Framework Regular Expressions (Wyrażenia regularne programu .NET Framework).
Aby ograniczyć parametr do znanego zestawu możliwych wartości, użyj wyrażenia regularnego. Na przykład {action:regex(^(list|get|create)$)}
pasuje action
tylko do wartości trasy do list
, get
lub create
. Jeśli zostanie przekazany do słownika ograniczeń, ciąg ^(list|get|create)$
jest równoważny. Ograniczenia przekazywane w słowniku ograniczeń, które nie są zgodne z jednym ze znanych ograniczeń, są również traktowane jako wyrażenia regularne. Ograniczenia przekazywane w szablonie, które nie pasują do jednego ze znanych ograniczeń, nie są traktowane jako wyrażenia regularne.
Ograniczenia trasy niestandardowej można utworzyć przez zaimplementowanie interfejsu IRouteConstraint . Interfejs IRouteConstraint
zawiera Matchwartość , która zwraca true
wartość, jeśli ograniczenie jest spełnione i false
w przeciwnym razie.
Ograniczenia trasy niestandardowej są rzadko potrzebne. Przed zaimplementowaniem ograniczenia trasy niestandardowej rozważ alternatywy, takie jak powiązanie modelu.
Folder ASP.NET Core Constraints zawiera dobre przykłady tworzenia ograniczeń. Na przykład GuidRouteConstraint.
Aby użyć niestandardowego IRouteConstraint
, typ ograniczenia trasy musi być zarejestrowany w aplikacji ConstraintMap w kontenerze usługi. Jest ConstraintMap
to słownik, który mapuje klucze ograniczeń trasy na IRouteConstraint
implementacje, które weryfikują te ograniczenia. Aplikację ConstraintMap
można zaktualizować w Program.cs
ramach AddRouting wywołania lub konfigurując bezpośrednio za pomocą RouteOptionspolecenia builder.Services.Configure<RouteOptions>
. Na przykład:
builder.Services.AddRouting(options =>
options.ConstraintMap.Add("noZeroes", typeof(NoZeroesRouteConstraint)));
Powyższe ograniczenie jest stosowane w następującym kodzie:
[ApiController]
[Route("api/[controller]")]
public class NoZeroesController : ControllerBase
{
[HttpGet("{id:noZeroes}")]
public IActionResult Get(string id) =>
Content(id);
}
Implementacja elementu NoZeroesRouteConstraint
uniemożliwia 0
korzystanie z parametru trasy:
public class NoZeroesRouteConstraint : IRouteConstraint
{
private static readonly Regex _regex = new(
@"^[1-9]*$",
RegexOptions.CultureInvariant | RegexOptions.IgnoreCase,
TimeSpan.FromMilliseconds(100));
public bool Match(
HttpContext? httpContext, IRouter? route, string routeKey,
RouteValueDictionary values, RouteDirection routeDirection)
{
if (!values.TryGetValue(routeKey, out var routeValue))
{
return false;
}
var routeValueString = Convert.ToString(routeValue, CultureInfo.InvariantCulture);
if (routeValueString is null)
{
return false;
}
return _regex.IsMatch(routeValueString);
}
}
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.
Powyższy kod ma następujące działanie:
0
w {id}
segmencie trasy.Poniższy kod jest lepszym podejściem do zapobiegania przetwarzaniu elementu zawierającego id
0
element:
[HttpGet("{id}")]
public IActionResult Get(string id)
{
if (id.Contains('0'))
{
return StatusCode(StatusCodes.Status406NotAcceptable);
}
return Content(id);
}
Powyższy kod ma następujące zalety w porównaniu z podejściem NoZeroesRouteConstraint
:
0
wartość .Transformatory parametrów:
Na przykład funkcja przekształcania parametrów niestandardowych slugify
we wzorcu blog\{article:slugify}
trasy z poleceniem Url.Action(new { article = "MyTestArticle" })
generuje blog\my-test-article
wartość .
Rozważmy następującą IOutboundParameterTransformer
implementację:
public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
public string? TransformOutbound(object? value)
{
if (value is null)
{
return null;
}
return Regex.Replace(
value.ToString()!,
"([a-z])([A-Z])",
"$1-$2",
RegexOptions.CultureInvariant,
TimeSpan.FromMilliseconds(100))
.ToLowerInvariant();
}
}
Aby użyć transformatora parametrów we wzorcu trasy, skonfiguruj go przy użyciu ConstraintMap polecenia w pliku Program.cs
:
builder.Services.AddRouting(options =>
options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer));
Struktura ASP.NET Core używa funkcji przekształcania parametrów w celu przekształcenia identyfikatora URI, w którym punkt końcowy jest rozpoznawany. Na przykład transformatory parametrów przekształcają wartości tras używane do dopasowania area
wartości , , controller
action
i page
:
app.MapControllerRoute(
name: "default",
pattern: "{controller:slugify=Home}/{action:slugify=Index}/{id?}");
W przypadku poprzedniego szablonu trasy akcja SubscriptionManagementController.GetAll
jest zgodna z identyfikatorem URI /subscription-management/get-all
. Funkcja przekształcania parametrów nie zmienia wartości tras używanych do generowania łącza. Na przykład Url.Action("GetAll", "SubscriptionManagement")
dane wyjściowe ./subscription-management/get-all
ASP.NET Core udostępnia konwencje interfejsu API dotyczące używania transformatorów parametrów z wygenerowanymi trasami:
Ta sekcja zawiera odwołanie do algorytmu zaimplementowanego przez generowanie adresów URL. W praktyce większość złożonych przykładów generowania adresów URL używa kontrolerów lub Razor stron. Aby uzyskać dodatkowe informacje, zobacz routing w kontrolerach .
Proces generowania adresu URL rozpoczyna się od wywołania metody LinkGenerator.GetPathByAddress lub podobnej metody. Metoda jest dostarczana z adresem, zestawem wartości tras i opcjonalnie informacjami o bieżącym żądaniu z .HttpContext
Pierwszym krokiem jest użycie adresu w celu rozpoznania zestawu punktów końcowych kandydata przy użyciu elementu zgodnego z typem IEndpointAddressScheme<TAddress> adresu.
Po znalezieniu zestawu kandydatów przez schemat adresów punkty końcowe są uporządkowane i przetwarzane iteracyjnie, dopóki operacja generowania adresu URL nie powiedzie się. Generowanie adresu URL nie sprawdza niejednoznaczności. Zwracany pierwszy wynik jest wynikiem końcowym.
Pierwszym krokiem generowania adresu URL rozwiązywania problemów jest ustawienie poziomu rejestrowania Microsoft.AspNetCore.Routing
na TRACE
.
LinkGenerator
rejestruje wiele szczegółów dotyczących jego przetwarzania, które mogą być przydatne do rozwiązywania problemów.
Aby uzyskać szczegółowe informacje na temat generowania adresów URL, zobacz Dokumentację generowania adresów URL.
Adresy to koncepcja generowania adresów URL używana do powiązania wywołania z generatorem linków z zestawem kandydatów punktów końcowych.
Adresy to rozszerzalna koncepcja, która domyślnie zawiera dwie implementacje:
string
) jako adresu: IUrlHelper
pomocników tagów, pomocników HTML, wyniki akcji itp.Rolą schematu adresów jest skojarzenie adresu i pasujących punktów końcowych według dowolnych kryteriów:
Z bieżącego żądania routing uzyskuje dostęp do wartości tras bieżącego żądania HttpContext.Request.RouteValues
. Wartości skojarzone z bieżącym żądaniem są określane jako wartości otoczenia. W celu jasności dokumentacja odnosi się do wartości tras przekazywanych do metod jako jawnych wartości.
W poniższym przykładzie przedstawiono wartości otoczenia i jawne wartości. Zapewnia ona wartości otoczenia z bieżącego żądania i jawnych wartości:
public class WidgetController : ControllerBase
{
private readonly LinkGenerator _linkGenerator;
public WidgetController(LinkGenerator linkGenerator) =>
_linkGenerator = linkGenerator;
public IActionResult Index()
{
var indexPath = _linkGenerator.GetPathByAction(
HttpContext, values: new { id = 17 })!;
return Content(indexPath);
}
// ...
Powyższy kod ma następujące działanie:
/Widget/Index/17
Poniższy kod zawiera tylko jawne wartości i nie ma wartości otoczenia:
var subscribePath = _linkGenerator.GetPathByAction(
"Subscribe", "Home", new { id = 17 })!;
Poprzednia metoda zwraca /Home/Subscribe/17
Poniższy kod w zwracaniu WidgetController
wartości /Widget/Subscribe/17
:
var subscribePath = _linkGenerator.GetPathByAction(
HttpContext, "Subscribe", null, new { id = 17 });
Poniższy kod udostępnia kontroler z wartości otoczenia w bieżącym żądaniu i jawnych wartościach:
public class GadgetController : ControllerBase
{
public IActionResult Index() =>
Content(Url.Action("Edit", new { id = 17 })!);
}
Powyższy kod:
/Gadget/Edit/17
jest zwracany.action
nazwę i route
wartości.Poniższy kod zawiera wartości otoczenia z bieżącego żądania i jawnych wartości:
public class IndexModel : PageModel
{
public void OnGet()
{
var editUrl = Url.Page("./Edit", new { id = 17 });
// ...
}
}
Powyższy kod ustawia wartość url
, /Edit/17
gdy strona edycji Razor zawiera następującą dyrektywę strony:
@page "{id:int}"
Jeśli strona Edycja nie zawiera szablonu "{id:int}"
trasy, url
to /Edit?id=17
.
Zachowanie wzorca MVC IUrlHelper dodaje warstwę złożoności oprócz reguł opisanych tutaj:
IUrlHelper
zawsze udostępnia wartości tras z bieżącego żądania jako wartości otoczenia.action
wartości i controller
trasy jako wartości jawne, chyba że zostanie zastąpione przez dewelopera.page
wartość trasy jako jawną wartość, chyba że zostanie zastąpiona. IUrlHelper.Page
zawsze zastępuje bieżącą handler
wartość trasy wartością null
jawną, chyba że zostanie zastąpiona.Użytkownicy są często zaskoczeni szczegółami zachowania wartości otoczenia, ponieważ MVC nie wydaje się przestrzegać własnych reguł. Ze względów historycznych i zgodności niektóre wartości tras, takie jak action
, controller
, page
i handler
mają własne zachowanie specjalne.
Równoważna funkcjonalność zapewniana przez LinkGenerator.GetPathByAction
program i LinkGenerator.GetPathByPage
duplikuje te anomalie w IUrlHelper
celu zapewnienia zgodności.
Po znalezieniu zestawu punktów końcowych kandydata algorytm generowania adresów URL:
Pierwszym krokiem w tym procesie jest unieważnienie wartości trasy. Unieważnienie wartości trasy to proces, za pomocą którego routing decyduje, które wartości tras z wartości otoczenia powinny być używane i które powinny być ignorowane. Każda wartość otoczenia jest uwzględniana i połączona z jawnymi wartościami lub ignorowana.
Najlepszym sposobem myślenia o roli wartości otoczenia jest to, że próbują zapisać deweloperów aplikacji wpisywanie, w niektórych typowych przypadkach. Tradycyjnie scenariusze, w których wartości otoczenia są przydatne, są związane z MVC:
Wywołania do LinkGenerator
lub IUrlHelper
tego zwracania null
są zwykle spowodowane przez niezrozumienie unieważnienia wartości trasy. Rozwiąż problemy z nieprawidłową wartością trasy, określając jawnie więcej wartości trasy, aby sprawdzić, czy rozwiązuje to problem.
Unieważnienie wartości trasy działa zgodnie z założeniem, że schemat adresu URL aplikacji jest hierarchiczny, z hierarchią utworzoną od lewej do prawej. Rozważmy podstawowy szablon {controller}/{action}/{id?}
trasy kontrolera, aby uzyskać intuicyjne zrozumienie sposobu działania tej metody w praktyce.
Zmiana wartości unieważnia wszystkie wartości trasy, które są wyświetlane po prawej stronie. Odzwierciedla to założenie dotyczące hierarchii. Jeśli aplikacja ma wartość otoczenia dla id
, a operacja określa inną wartość dla elementu controller
:
id
nie będzie ponownie używany, ponieważ {controller}
znajduje się po lewej {id?}
stronie .Kilka przykładów demonstrujących tę zasadę:
id
, wartość otoczenia dla id
elementu jest ignorowana. Wartości otoczenia i controller
action
mogą być używane.action
, każda wartość otoczenia dla action
elementu jest ignorowana. Można użyć wartości controller
otoczenia. Jeśli jawna wartość parametru action
jest inna niż wartość otoczenia dla action
wartości , id
wartość nie zostanie użyta. Jeśli jawna wartość parametru action
jest taka sama jak wartość otoczenia dla action
, id
można użyć wartości .controller
, każda wartość otoczenia dla controller
elementu jest ignorowana. Jeśli jawna wartość parametru controller
jest inna niż wartość otoczenia dla controller
, action
wartości i id
nie będą używane. Jeśli jawna wartość parametru controller
jest taka sama jak wartość otoczenia dla controller
, action
można użyć wartości i id
.Ten proces jest jeszcze bardziej skomplikowany przez istnienie tras atrybutów i dedykowanych tras konwencjonalnych. Kontroler konwencjonalnych tras, takich jak {controller}/{action}/{id?}
określanie hierarchii przy użyciu parametrów trasy. W przypadku dedykowanych tras konwencjonalnych i tras atrybutów do kontrolerów i Razor stron:
W takich przypadkach generowanie adresów URL definiuje koncepcję wymaganych wartości . Punkty końcowe utworzone przez kontrolery i Razor strony mają wymagane wartości określone, które umożliwiają działanie unieważnienia wartości trasy.
Algorytm unieważniania wartości trasy szczegółowo:
W tym momencie operacja generowania adresu URL jest gotowa do oceny ograniczeń trasy. Zestaw akceptowanych wartości jest połączony z wartościami domyślnymi parametrów, które są dostarczane do ograniczeń. Jeśli wszystkie ograniczenia przechodzą, operacja będzie kontynuowana.
Następnie akceptowane wartości mogą służyć do rozszerzania szablonu trasy. Szablon trasy jest przetwarzany:
Wartości jawnie podane, które nie pasują do segmentu trasy, są dodawane do ciągu zapytania. W poniższej tabeli przedstawiono wynik użycia szablonu {controller}/{action}/{id?}
trasy .
Wartości otoczenia | Jawne wartości | Result |
---|---|---|
controller = "Home" | action = "Informacje" | /Home/About |
controller = "Home" | controller = "Order", action = "About" | /Order/About |
controller = "Home", color = "Red" | action = "Informacje" | /Home/About |
controller = "Home" | action = "About", color = "Red" | /Home/About?color=Red |
Poniższy kod przedstawia przykład schematu generowania adresów URL, który nie jest obsługiwany przez routing:
app.MapControllerRoute(
"default",
"{culture}/{controller=Home}/{action=Index}/{id?}");
app.MapControllerRoute(
"blog",
"{culture}/{**slug}",
new { controller = "Blog", action = "ReadPost" });
W poprzednim kodzie culture
parametr trasy jest używany do lokalizacji. Pragnieniem jest, culture
aby parametr zawsze był akceptowany jako wartość otoczenia.
culture
Jednak parametr nie jest akceptowany jako wartość otoczenia ze względu na sposób działania wymaganych wartości:
"default"
culture
trasy parametr trasy znajduje się po lewej stronie controller
elementu , więc zmiany controller
nie będą unieważniać culture
elementu ."blog"
culture
trasy parametr trasy jest uznawany za znajdujący się po prawej stronie controller
elementu , który jest wyświetlany w wymaganych wartościach.Klasa LinkParser dodaje obsługę analizowania ścieżki adresu URL do zestawu wartości tras. Metoda ParsePathByEndpointName przyjmuje nazwę punktu końcowego i ścieżkę adresu URL i zwraca zestaw wartości tras wyodrębnionych ze ścieżki adresu URL.
W poniższym przykładowym kontrolerze akcja GetProduct
używa szablonu api/Products/{id}
trasy i ma NameGetProduct
wartość :
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
[HttpGet("{id}", Name = nameof(GetProduct))]
public IActionResult GetProduct(string id)
{
// ...
W tej samej klasie AddRelatedProduct
kontrolera akcja oczekuje ścieżki adresu URL , pathToRelatedProduct
która może być podana jako parametr ciągu zapytania:
[HttpPost("{id}/Related")]
public IActionResult AddRelatedProduct(
string id, string pathToRelatedProduct, [FromServices] LinkParser linkParser)
{
var routeValues = linkParser.ParsePathByEndpointName(
nameof(GetProduct), pathToRelatedProduct);
var relatedProductId = routeValues?["id"];
// ...
W poprzednim przykładzie akcja AddRelatedProduct
wyodrębnia id
wartość trasy ze ścieżki adresu URL. Na przykład ze ścieżką /api/Products/1
relatedProductId
adresu URL wartości jest ustawiona wartość 1
. Takie podejście umożliwia klientom interfejsu API używanie ścieżek url podczas odwoływania się do zasobów bez konieczności znajomości struktury takiego adresu URL.
Poniższe linki zawierają informacje na temat konfigurowania metadanych punktu końcowego:
[MinimumAgeAuthorize]
RequireHost stosuje ograniczenie do trasy, która wymaga określonego hosta. Parametr RequireHost
lub [Host] może być:
www.domain.com
, pasuje www.domain.com
do dowolnego portu.*.domain.com
, pasuje www.domain.com
do , subdomain.domain.com
lub www.subdomain.domain.com
na dowolnym porcie.*:5000
, pasuje do portu 5000 z dowolnym hostem.www.domain.com:5000
lub *.domain.com:5000
, pasuje do hosta i portu.Można określić wiele parametrów przy użyciu polecenia RequireHost
lub [Host]
. Ograniczenie pasuje do hostów prawidłowych dla dowolnego z parametrów. Na przykład pasuje [Host("domain.com", "*.domain.com")]
do domain.com
, www.domain.com
i subdomain.domain.com
.
Poniższy kod używa RequireHost
metody , aby wymagać określonego hosta na trasie:
app.MapGet("/", () => "Contoso").RequireHost("contoso.com");
app.MapGet("/", () => "AdventureWorks").RequireHost("adventure-works.com");
app.MapHealthChecks("/healthz").RequireHost("*:8080");
Poniższy kod używa atrybutu [Host]
na kontrolerze, aby wymagać któregokolwiek z określonych hostów:
[Host("contoso.com", "adventure-works.com")]
public class HostsController : Controller
{
public IActionResult Index() =>
View();
[Host("example.com")]
public IActionResult Example() =>
View();
}
Po zastosowaniu atrybutu [Host]
zarówno do kontrolera, jak i metody akcji:
Gdy aplikacja ma problemy z wydajnością, routing jest często podejrzewany jako problem. Podejrzewa się, że routing polega na tym, że struktury, takie jak kontrolery i Razor strony, zgłaszają ilość czasu spędzonego w strukturze w komunikatach rejestrowania. W przypadku znacznej różnicy między czasem zgłoszonym przez kontrolery a łącznym czasem żądania:
Routing jest testowany pod kątem wydajności przy użyciu tysięcy punktów końcowych. Jest mało prawdopodobne, aby typowa aplikacja napotkała problem z wydajnością, ponieważ jest zbyt duża. Najczęstszą główną przyczyną niskiej wydajności routingu jest zwykle nieprawidłowe zachowanie niestandardowego oprogramowania pośredniczącego.
W poniższym przykładzie kodu przedstawiono podstawową technikę zawężania źródła opóźnienia:
var logger = app.Services.GetRequiredService<ILogger<Program>>();
app.Use(async (context, next) =>
{
var stopwatch = Stopwatch.StartNew();
await next(context);
stopwatch.Stop();
logger.LogInformation("Time 1: {ElapsedMilliseconds}ms", stopwatch.ElapsedMilliseconds);
});
app.UseRouting();
app.Use(async (context, next) =>
{
var stopwatch = Stopwatch.StartNew();
await next(context);
stopwatch.Stop();
logger.LogInformation("Time 2: {ElapsedMilliseconds}ms", stopwatch.ElapsedMilliseconds);
});
app.UseAuthorization();
app.Use(async (context, next) =>
{
var stopwatch = Stopwatch.StartNew();
await next(context);
stopwatch.Stop();
logger.LogInformation("Time 3: {ElapsedMilliseconds}ms", stopwatch.ElapsedMilliseconds);
});
app.MapGet("/", () => "Timing Test.");
Routing czasowy:
Jest to podstawowy sposób zawężenia opóźnienia, gdy jest to znaczące, na przykład więcej niż 10ms
. Odejmowanie Time 2
z Time 1
raportów czasu spędzonego wewnątrz oprogramowania pośredniczącego UseRouting
.
Poniższy kod używa bardziej kompaktowego podejścia do poprzedniego kodu chronometrażu:
public sealed class AutoStopwatch : IDisposable
{
private readonly ILogger _logger;
private readonly string _message;
private readonly Stopwatch _stopwatch;
private bool _disposed;
public AutoStopwatch(ILogger logger, string message) =>
(_logger, _message, _stopwatch) = (logger, message, Stopwatch.StartNew());
public void Dispose()
{
if (_disposed)
{
return;
}
_logger.LogInformation("{Message}: {ElapsedMilliseconds}ms",
_message, _stopwatch.ElapsedMilliseconds);
_disposed = true;
}
}
var logger = app.Services.GetRequiredService<ILogger<Program>>();
var timerCount = 0;
app.Use(async (context, next) =>
{
using (new AutoStopwatch(logger, $"Time {++timerCount}"))
{
await next(context);
}
});
app.UseRouting();
app.Use(async (context, next) =>
{
using (new AutoStopwatch(logger, $"Time {++timerCount}"))
{
await next(context);
}
});
app.UseAuthorization();
app.Use(async (context, next) =>
{
using (new AutoStopwatch(logger, $"Time {++timerCount}"))
{
await next(context);
}
});
app.MapGet("/", () => "Timing Test.");
Poniższa lista zawiera wgląd w funkcje routingu, które są stosunkowo kosztowne w porównaniu z podstawowymi szablonami tras:
{x}-{y}-{z}
): Domyślnie ASP.NET Core używa algorytmu routingu, który wymienia pamięć na czas procesora CPU. Ma to miły wpływ, że czas dopasowywania trasy zależy tylko od długości ścieżki do dopasowania, a nie liczby tras. Jednak takie podejście może być potencjalnie problematyczne w niektórych przypadkach, gdy aplikacja ma dużą liczbę tras (w tysiącach) i istnieje duża ilość zmiennych prefiksów w trasach. Jeśli na przykład trasy mają parametry we wczesnych segmentach trasy, na przykład {parameter}/some/literal
.
Jest mało prawdopodobne, aby aplikacja napotkała sytuację, w której jest to problem, chyba że:
Microsoft.AspNetCore.Routing.Matching.DfaNode
wystąpień.Istnieje kilka technik i optymalizacji, które można zastosować do tras, które w dużej mierze poprawią ten scenariusz:
{parameter:int}
, {parameter:guid}
, {parameter:regex(\\d+)}
itp., jeśli to możliwe.
MapDynamicControllerRoute
i MapDynamicPageRoute
.Ta sekcja zawiera wskazówki dotyczące autorów bibliotek opartych na routingu. Te szczegóły mają na celu zapewnienie, że deweloperzy aplikacji mają dobre środowisko korzystania z bibliotek i struktur rozszerzających routing.
Aby utworzyć strukturę korzystającą z routingu do dopasowywania adresów URL, zacznij od zdefiniowania środowiska użytkownika, które opiera się na systemie UseEndpoints.
Kompilacja DO na podstawie funkcji IEndpointRouteBuilder. Dzięki temu użytkownicy mogą tworzyć struktury przy użyciu innych funkcji ASP.NET Core bez pomyłek. Każdy szablon ASP.NET Core zawiera routing. Załóżmy, że routing jest obecny i znany użytkownikom.
// Your framework
app.MapMyFramework(...);
app.MapHealthChecks("/healthz");
NALEŻY zwrócić zapieczętowany typ betonowy z wywołania, do MapMyFramework(...)
którego implementuje IEndpointConventionBuilderelement . Większość metod struktury Map...
jest zgodne z tym wzorcem. Interfejs IEndpointConventionBuilder
:
Deklarowanie własnego typu umożliwia dodanie do konstruktora własnych funkcji specyficznych dla platformy. Można zawinąć konstruktora zadeklarowanego przez platformę i przekazać do niego wywołania.
// Your framework
app.MapMyFramework(...)
.RequireAuthorization()
.WithMyFrameworkFeature(awesome: true);
app.MapHealthChecks("/healthz");
ROZWAŻ napisanie własnego EndpointDataSourcepliku .
EndpointDataSource
jest elementem pierwotnym niskiego poziomu do deklarowania i aktualizowania kolekcji punktów końcowych.
EndpointDataSource
to zaawansowany interfejs API używany przez kontrolery i Razor strony.
Testy routingu mają podstawowy przykład nieakcyjnego źródła danych.
NIE próbuj domyślnie rejestrować konta EndpointDataSource
. Wymagaj od użytkowników zarejestrowania struktury w programie UseEndpoints. Filozofią routingu jest to, że nic nie jest dołączane domyślnie i UseEndpoints
jest to miejsce do rejestrowania punktów końcowych.
ROZWAŻ zdefiniowanie typów metadanych jako interfejsu.
DO umożliwia używanie typów metadanych jako atrybutu w klasach i metodach.
public interface ICoolMetadata
{
bool IsCool { get; }
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CoolMetadataAttribute : Attribute, ICoolMetadata
{
public bool IsCool => true;
}
Struktury, takie jak kontrolery i Razor strony, obsługują stosowanie atrybutów metadanych do typów i metod. W przypadku deklarowania typów metadanych:
Deklarowanie typu metadanych jako interfejsu zwiększa kolejną warstwę elastyczności:
NIE umożliwia przesłaniania metadanych, jak pokazano w poniższym przykładzie:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class SuppressCoolMetadataAttribute : Attribute, ICoolMetadata
{
public bool IsCool => false;
}
[CoolMetadata]
public class MyController : Controller
{
public void MyCool() { }
[SuppressCoolMetadata]
public void Uncool() { }
}
Najlepszym sposobem przestrzegania tych wytycznych jest unikanie definiowania metadanych znaczników:
Kolekcja metadanych jest uporządkowana i obsługuje zastępowanie według priorytetu. W przypadku kontrolerów metadane metody akcji są najbardziej specyficzne.
DO sprawia, że oprogramowanie pośredniczące jest przydatne z routingiem i bez tego routingu:
app.UseAuthorization(new AuthorizationPolicy() { ... });
// Your framework
app.MapMyFramework(...).RequireAuthorization();
Jako przykład tej wskazówki należy wziąć pod uwagę UseAuthorization
oprogramowanie pośredniczące. Oprogramowanie pośredniczące autoryzacji umożliwia przekazanie zasad rezerwowych.
Zasady rezerwowe, jeśli zostały określone, mają zastosowanie do obu następujących elementów:
Dzięki temu oprogramowanie pośredniczące autoryzacji jest przydatne poza kontekstem routingu. Oprogramowanie pośredniczące autoryzacji może służyć do tradycyjnego programowania oprogramowania pośredniczącego.
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"
}
}
}
Routing jest odpowiedzialny za dopasowywanie przychodzących żądań HTTP i wysyłanie tych żądań do punktów końcowych wykonywalnych aplikacji. Punkty końcowe to jednostki aplikacji kodu obsługi żądań wykonywalnych. Punkty końcowe są definiowane w aplikacji i konfigurowane podczas uruchamiania aplikacji. Proces dopasowywania punktu końcowego może wyodrębnić wartości z adresu URL żądania i podać te wartości do przetwarzania żądań. Korzystając z informacji o punkcie końcowym z aplikacji, routing jest również w stanie wygenerować adresy URL mapujące na punkty końcowe.
Aplikacje mogą konfigurować routing przy użyciu:
Ten dokument zawiera szczegółowe informacje o niskim poziomie routingu ASP.NET Core. Aby uzyskać informacje na temat konfigurowania routingu:
System routingu punktów końcowych opisany w tym dokumencie ma zastosowanie do ASP.NET Core 3.0 i nowszych. Aby uzyskać informacje na temat poprzedniego systemu routingu opartego na IRoutersystemie , wybierz wersję ASP.NET Core 2.1 przy użyciu jednej z następujących metod:
Wyświetl lub pobierz przykładowy kod (jak pobrać)
Przykłady pobierania dla tego dokumentu są włączone przez określoną Startup
klasę. Aby uruchomić konkretny przykład, zmodyfikuj Program.cs
, aby wywołać żądaną Startup
klasę.
Wszystkie szablony ASP.NET Core obejmują routing w wygenerowany kod. Routing jest rejestrowany w potoku oprogramowania pośredniczącego w programie Startup.Configure
.
Poniższy kod przedstawia podstawowy przykład routingu:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});
});
}
Routing używa pary oprogramowania pośredniczącego zarejestrowanego przez UseRouting i UseEndpoints:
UseRouting
Dodaje trasę zgodną z potokiem oprogramowania pośredniczącego. To oprogramowanie pośredniczące analizuje zestaw punktów końcowych zdefiniowanych w aplikacji i wybiera najlepsze dopasowanie na podstawie żądania.UseEndpoints
Dodaje wykonywanie punktu końcowego do potoku oprogramowania pośredniczącego. Uruchamia delegata skojarzonego z wybranym punktem końcowym.Powyższy przykład zawiera pojedynczą trasę do punktu końcowego kodu przy użyciu metody MapGet :
GET
do głównego adresu URL /
: Hello World!
jest zapisywany w odpowiedzi HTTP. Domyślnie główny adres URL /
to https://localhost:5001/
.GET
jest lub główny adres URL nie /
jest , nie jest zgodna trasa i zwracany jest http 404.Metoda MapGet
służy do definiowania punktu końcowego. Punkt końcowy to coś, co może być następujące:
Punkty końcowe, które mogą być dopasowane i wykonywane przez aplikację, są konfigurowane w programie UseEndpoints
. Na przykład metody MapGet, MapPosti podobne łączą delegatów żądań z systemem routingu. Dodatkowe metody mogą służyć do łączenia funkcji platformy ASP.NET Core z systemem routingu:
W poniższym przykładzie przedstawiono routing z bardziej zaawansowanym szablonem trasy:
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/hello/{name:alpha}", async context =>
{
var name = context.Request.RouteValues["name"];
await context.Response.WriteAsync($"Hello {name}!");
});
});
Ciąg /hello/{name:alpha}
jest szablonem trasy. Służy do konfigurowania sposobu dopasowania punktu końcowego. W takim przypadku szablon jest zgodny z następującymi elementami:
/hello/Ryan
/hello/
od sekwencji znaków alfabetycznych.
:alpha
stosuje ograniczenie trasy, które pasuje tylko do znaków alfabetycznych.
Ograniczenia tras zostały wyjaśnione w dalszej części tego dokumentu.Drugi segment ścieżki adresu URL: {name:alpha}
name
.System routingu punktów końcowych opisany w tym dokumencie jest nowy w wersji ASP.NET Core 3.0. Jednak wszystkie wersje ASP.NET Core obsługują ten sam zestaw funkcji szablonu trasy i ograniczeń trasy.
W poniższym przykładzie przedstawiono routing z kontrolą kondycji i autoryzacją:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// Matches request to an endpoint.
app.UseRouting();
// Endpoint aware middleware.
// Middleware can use metadata from the matched endpoint.
app.UseAuthentication();
app.UseAuthorization();
// Execute the matched endpoint.
app.UseEndpoints(endpoints =>
{
// Configure the Health Check endpoint and require an authorized user.
endpoints.MapHealthChecks("/healthz").RequireAuthorization();
// Configure another endpoint, no authorization requirements.
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});
});
}
Jeśli chcesz zobaczyć komentarze kodu przetłumaczone na języki inne niż angielski, poinformuj nas o tym w tym problemie z dyskusją w usłudze GitHub.
W poprzednim przykładzie pokazano, jak:
Wywołanie MapHealthChecks dodaje punkt końcowy kontroli kondycji. Łączenie RequireAuthorization łańcucha do tego wywołania powoduje dołączenie zasad autoryzacji do punktu końcowego.
Wywoływanie UseAuthentication i UseAuthorization dodawanie oprogramowania pośredniczącego uwierzytelniania i autoryzacji. Te oprogramowanie pośredniczące są umieszczane między elementami UseRouting i UseEndpoints
, dzięki czemu mogą:
UseRouting
element .W poprzednim przykładzie istnieją dwa punkty końcowe, ale tylko punkt końcowy kontroli kondycji ma dołączone zasady autoryzacji. Jeśli żądanie pasuje do punktu końcowego sprawdzania kondycji, /healthz
zostanie wykonane sprawdzanie autoryzacji. Pokazuje to, że punkty końcowe mogą mieć dołączone dodatkowe dane. Te dodatkowe dane są nazywane metadanymi punktu końcowego:
System routingu opiera się na potoku oprogramowania pośredniczącego, dodając zaawansowaną koncepcję punktu końcowego . Punkty końcowe reprezentują jednostki funkcjonalności aplikacji, które różnią się od siebie pod względem routingu, autoryzacji i dowolnej liczby systemów ASP.NET Core.
Punkt końcowy platformy ASP.NET Core to:
Poniższy kod pokazuje, jak pobrać i sprawdzić punkt końcowy pasujący do bieżącego żądania:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.Use(next => context =>
{
var endpoint = context.GetEndpoint();
if (endpoint is null)
{
return Task.CompletedTask;
}
Console.WriteLine($"Endpoint: {endpoint.DisplayName}");
if (endpoint is RouteEndpoint routeEndpoint)
{
Console.WriteLine("Endpoint has route pattern: " +
routeEndpoint.RoutePattern.RawText);
}
foreach (var metadata in endpoint.Metadata)
{
Console.WriteLine($"Endpoint has metadata: {metadata}");
}
return Task.CompletedTask;
});
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});
});
}
Punkt końcowy, jeśli został wybrany, można pobrać z pliku HttpContext
. Jego właściwości można sprawdzić. Obiekty punktu końcowego są niezmienne i nie można ich modyfikować po utworzeniu. Najczęstszym typem RouteEndpointpunktu końcowego jest .
RouteEndpoint
zawiera informacje, które umożliwiają wybranie go przez system routingu.
W poprzednim kodzie aplikacja. Użyj konfiguracji oprogramowania pośredniczącego wbudowanego.
Poniższy kod pokazuje, że w zależności od tego, gdzie app.Use
jest wywoływana w potoku, punkt końcowy może nie być:
// Location 1: before routing runs, endpoint is always null here
app.Use(next => context =>
{
Console.WriteLine($"1. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
return next(context);
});
app.UseRouting();
// Location 2: after routing runs, endpoint will be non-null if routing found a match
app.Use(next => context =>
{
Console.WriteLine($"2. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
return next(context);
});
app.UseEndpoints(endpoints =>
{
// Location 3: runs when this endpoint matches
endpoints.MapGet("/", context =>
{
Console.WriteLine(
$"3. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
return Task.CompletedTask;
}).WithDisplayName("Hello");
});
// Location 4: runs after UseEndpoints - will only run if there was no match
app.Use(next => context =>
{
Console.WriteLine($"4. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
return next(context);
});
W powyższym przykładzie dodano Console.WriteLine
instrukcje, które wyświetlają, czy wybrano punkt końcowy. Aby uzyskać czytelność, przykład przypisuje nazwę wyświetlaną do podanego /
punktu końcowego.
Uruchomienie tego kodu z adresem URL wyświetlania /
:
1. Endpoint: (null)
2. Endpoint: Hello
3. Endpoint: Hello
Uruchomienie tego kodu przy użyciu dowolnego innego adresu URL powoduje wyświetlenie:
1. Endpoint: (null)
2. Endpoint: (null)
4. Endpoint: (null)
Te dane wyjściowe pokazują, że:
UseRouting
wywołaniem.UseRouting
i UseEndpoints.UseEndpoints
pośredniczące jest terminalem po znalezieniu dopasowania.
Oprogramowanie pośredniczące terminalu jest zdefiniowane w dalszej części tego dokumentu.UseEndpoints
wykonaniu tylko wtedy, gdy nie zostanie znalezione dopasowanie.Oprogramowanie UseRouting
pośredniczące używa metody SetEndpoint , aby dołączyć punkt końcowy do bieżącego kontekstu. Można zastąpić UseRouting
oprogramowanie pośredniczące logiką niestandardową i nadal korzystać z zalet korzystania z punktów końcowych. Punkty końcowe są typami pierwotnymi niskiego poziomu, takimi jak oprogramowanie pośredniczące, i nie są powiązane z implementacją routingu. Większość aplikacji nie musi zastępować UseRouting
logiką niestandardową.
Oprogramowanie UseEndpoints
pośredniczące jest przeznaczone do użycia w parze z oprogramowaniem pośredniczącym UseRouting
. Podstawowa logika do wykonania punktu końcowego nie jest skomplikowana. Użyj GetEndpoint polecenia , aby pobrać punkt końcowy, a następnie wywołać jego RequestDelegate właściwość.
Poniższy kod pokazuje, jak oprogramowanie pośredniczące może wpływać na routing lub reagować na nie:
public class IntegratedMiddlewareStartup
{
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// Location 1: Before routing runs. Can influence request before routing runs.
app.UseHttpMethodOverride();
app.UseRouting();
// Location 2: After routing runs. Middleware can match based on metadata.
app.Use(next => context =>
{
var endpoint = context.GetEndpoint();
if (endpoint?.Metadata.GetMetadata<AuditPolicyAttribute>()?.NeedsAudit
== true)
{
Console.WriteLine($"ACCESS TO SENSITIVE DATA AT: {DateTime.UtcNow}");
}
return next(context);
});
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello world!");
});
// Using metadata to configure the audit policy.
endpoints.MapGet("/sensitive", async context =>
{
await context.Response.WriteAsync("sensitive data");
})
.WithMetadata(new AuditPolicyAttribute(needsAudit: true));
});
}
}
public class AuditPolicyAttribute : Attribute
{
public AuditPolicyAttribute(bool needsAudit)
{
NeedsAudit = needsAudit;
}
public bool NeedsAudit { get; }
}
W poprzednim przykładzie przedstawiono dwie ważne pojęcia:
UseRouting
zmodyfikowaniem danych, na których działa routing.
UseRouting
i UseEndpoints w celu przetworzenia wyników routingu przed wykonaniem punktu końcowego.
UseRouting
i UseEndpoints
: UseAuthorization
i UseCors
.Powyższy kod przedstawia przykład niestandardowego oprogramowania pośredniczącego obsługującego zasady poszczególnych punktów końcowych. Oprogramowanie pośredniczące zapisuje dziennik inspekcji dostępu do poufnych danych w konsoli. Oprogramowanie pośredniczące można skonfigurować do inspekcji punktu końcowego za AuditPolicyAttribute
pomocą metadanych. W tym przykładzie pokazano wzorzec zgody, w którym są poddawane inspekcji tylko punkty końcowe oznaczone jako poufne. Tę logikę można zdefiniować w odwrotnej kolejności, przeprowadzając inspekcję wszystkich elementów, które nie są oznaczone jako bezpieczne, na przykład. System metadanych punktu końcowego jest elastyczny. Ta logika może być zaprojektowana w dowolny sposób odpowiednio do przypadku użycia.
Powyższy przykładowy kod ma na celu zademonstrowanie podstawowych pojęć dotyczących punktów końcowych. Przykład nie jest przeznaczony do użytku produkcyjnego. Bardziej kompletna wersja oprogramowania pośredniczącego dziennika inspekcji:
Metadane AuditPolicyAttribute
zasad inspekcji są definiowane jako Attribute
łatwiejsze w użyciu z platformami opartymi na klasach, takimi jak kontrolery i SignalR. W przypadku używania trasy do kodu:
Najlepszym rozwiązaniem dla typów metadanych jest zdefiniowanie ich jako interfejsów lub atrybutów. Interfejsy i atrybuty umożliwiają ponowne użycie kodu. System metadanych jest elastyczny i nie nakłada żadnych ograniczeń.
Poniższy przykładowy kod kontrastuje przy użyciu oprogramowania pośredniczącego z użyciem routingu:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// Approach 1: Writing a terminal middleware.
app.Use(next => async context =>
{
if (context.Request.Path == "/")
{
await context.Response.WriteAsync("Hello terminal middleware!");
return;
}
await next(context);
});
app.UseRouting();
app.UseEndpoints(endpoints =>
{
// Approach 2: Using routing.
endpoints.MapGet("/Movie", async context =>
{
await context.Response.WriteAsync("Hello routing!");
});
});
}
Styl oprogramowania pośredniczącego pokazany za pomocą Approach 1:
programu to oprogramowanie pośredniczące terminalu. Jest to nazywane oprogramowaniem pośredniczącym terminalu, ponieważ wykonuje zgodną operację:
Path == "/"
oprogramowania pośredniczącego i Path == "/Movie"
routingu.next
.Jest to nazywane oprogramowaniem pośredniczącym terminalu, ponieważ kończy wyszukiwanie, wykonuje niektóre funkcje, a następnie zwraca.
Porównanie oprogramowania pośredniczącego terminalu i routingu:
next
UseAuthorization
i UseCors
.
UseAuthorization
oprogramowaniem pośredniczącym lub UseCors
wymaga ręcznego łączenia się z systemem autoryzacji.Punkt końcowy definiuje oba:
Oprogramowanie pośredniczące terminalu może być skutecznym narzędziem, ale może wymagać:
Przed napisaniem oprogramowania pośredniczącego terminalu rozważ integrację z routingiem.
Istniejące oprogramowanie pośredniczące terminalu integrujące się z usługą Map lub MapWhen zwykle może zostać przekształcone w punkt końcowy obsługujący routing. MapHealthChecks demonstruje wzorzec oprogramowania routera:
Map
i podaj nowy potok oprogramowania pośredniczącego.Map
metodę rozszerzenia.Poniższy kod przedstawia użycie narzędzi MapHealthChecks:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// Matches request to an endpoint.
app.UseRouting();
// Endpoint aware middleware.
// Middleware can use metadata from the matched endpoint.
app.UseAuthentication();
app.UseAuthorization();
// Execute the matched endpoint.
app.UseEndpoints(endpoints =>
{
// Configure the Health Check endpoint and require an authorized user.
endpoints.MapHealthChecks("/healthz").RequireAuthorization();
// Configure another endpoint, no authorization requirements.
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});
});
}
W poprzednim przykładzie pokazano, dlaczego zwracanie obiektu konstruktora jest ważne. Zwracanie obiektu konstruktora umożliwia deweloperowi aplikacji konfigurowanie zasad, takich jak autoryzacja dla punktu końcowego. W tym przykładzie oprogramowanie pośredniczące kontroli kondycji nie ma bezpośredniej integracji z systemem autoryzacji.
System metadanych został utworzony w odpowiedzi na problemy napotykane przez autorów rozszerzeń przy użyciu oprogramowania pośredniczącego terminalu. Dla każdego oprogramowania pośredniczącego problematyczne jest zaimplementowanie własnej integracji z systemem autoryzacji.
Po wykonaniu oprogramowania pośredniczącego routingu ustawia Endpoint
ona wartości i kieruje je do funkcji żądania z HttpContext bieżącego żądania:
HttpRequest.RouteValues
pobiera kolekcję wartości tras.Oprogramowanie pośredniczące jest uruchamiane po tym, jak oprogramowanie pośredniczące routingu może sprawdzić punkt końcowy i podjąć działania. Na przykład oprogramowanie pośredniczące autoryzacji może przesłuchić kolekcję metadanych punktu końcowego dla zasad autoryzacji. Po wykonaniu całego oprogramowania pośredniczącego w potoku przetwarzania żądań wywoływany jest delegat wybranego punktu końcowego.
System routingu w routingu punktów końcowych jest odpowiedzialny za wszystkie decyzje dotyczące wysyłania. Ponieważ oprogramowanie pośredniczące stosuje zasady na podstawie wybranego punktu końcowego, ważne jest, aby:
Ostrzeżenie
W przypadku zgodności z poprzednimi wersjami po wykonaniu delegata punktu końcowego kontroler lub Razor strony właściwości RouteContext.RouteData są ustawione na odpowiednie wartości na podstawie przetwarzania żądań wykonywanego do tej pory.
Typ RouteContext
zostanie oznaczony jako przestarzały w przyszłej wersji:
RouteData.Values
do HttpRequest.RouteValues
.RouteData.DataTokens
, aby pobrać dane IDataTokensMetadata z metadanych punktu końcowego.Dopasowywanie adresów URL działa w konfigurowalnym zestawie faz. W każdej fazie dane wyjściowe są zestawem dopasowań. Zestaw dopasowań można zawęzić dalej przez następną fazę. Implementacja routingu nie gwarantuje kolejności przetwarzania pasujących punktów końcowych. Wszystkie możliwe dopasowania są przetwarzane jednocześnie. Fazy dopasowywania adresów URL są wykonywane w następującej kolejności. ASP.NET Core:
Lista punktów końcowych jest priorytetowa zgodnie z:
Wszystkie pasujące punkty końcowe są przetwarzane w każdej fazie do momentu EndpointSelector osiągnięcia. Jest EndpointSelector
to ostatnia faza. Wybiera punkt końcowy o najwyższym priorytcie z dopasowań jako najlepszy. Jeśli istnieją inne dopasowania z tym samym priorytetem co najlepsze dopasowanie, zgłaszany jest niejednoznaczny wyjątek dopasowania.
Pierwszeństwo trasy jest obliczane na podstawie bardziej szczegółowego szablonu trasy o wyższym priorytecie. Rozważmy na przykład szablony /hello
i /{message}
:
/hello
adresu URL ./hello
jest bardziej szczegółowy i dlatego wyższy priorytet.Ogólnie rzecz biorąc, pierwszeństwo trasy dobrze nadaje się do wyboru najlepszego dopasowania do rodzajów schematów adresów URL używanych w praktyce. Używaj Order tylko wtedy, gdy jest to konieczne, aby uniknąć niejednoznaczności.
Ze względu na rodzaj rozszerzalności zapewnianej przez routing nie jest możliwe, aby system routingu obliczał przed upływem czasu niejednoznaczne trasy. Rozważmy przykład, taki jak szablony /{message:alpha}
tras i /{message:int}
:
alpha
pasuje tylko do znaków alfabetycznych.int
pasuje tylko do liczb.Ostrzeżenie
Kolejność operacji wewnątrz UseEndpoints nie ma wpływu na zachowanie routingu z jednym wyjątkiem. MapControllerRoute i MapAreaRoute automatycznie przypisz wartość zamówienia do swoich punktów końcowych na podstawie kolejności, w której są wywoływane. Symuluje to długotrwałe zachowanie kontrolerów bez systemu routingu zapewniające takie same gwarancje jak starsze implementacje routingu.
W starszej implementacji routingu można zaimplementować rozszerzalność routingu, która ma zależność od kolejności przetwarzania tras. Routing punktów końcowych w programie ASP.NET Core 3.0 lub nowszym:
Pierwszeństwo szablonu trasy to system, który przypisuje każdemu szablonowi trasy wartość na podstawie jego specyfiki. Pierwszeństwo szablonu trasy:
Rozważmy na przykład szablony /Products/List
i /Products/{id}
. Uzasadnione byłoby założenie, że /Products/List
jest to lepsze dopasowanie niż /Products/{id}
dla ścieżki /Products/List
adresu URL . Działa to, ponieważ segment /List
literału jest uznawany za lepszy priorytet niż segment /{id}
parametrów .
Szczegółowe informacje o sposobie działania pierwszeństwa są powiązane z definiowaniem szablonów tras:
Zobacz kod źródłowy w witrynie GitHub , aby uzyskać odwołanie do dokładnych wartości.
Generowanie adresu URL:
Routing punktów końcowych obejmuje LinkGenerator interfejs API.
LinkGenerator
jest pojedynczą usługą dostępną z di. Interfejs LinkGenerator
API może być używany poza kontekstem wykonywania żądania.
Mvc.IUrlHelper i scenariusze, które opierają się na IUrlHelperelementach , takich jak Pomocnicy tagów, Pomocnicy HTML i Wyniki akcji, używają interfejsu LinkGenerator
API wewnętrznie, aby zapewnić możliwości generowania linków.
Generator linków jest wspierany przez koncepcję schematów adresów i adresów. Schemat adresów to sposób określania punktów końcowych, które powinny być brane pod uwagę podczas generowania linków. Na przykład scenariusze nazw tras i wartości tras, które wielu użytkowników zna z kontrolerów i Razor stron, są implementowane jako schemat adresów.
Generator linków może łączyć się z kontrolerami i Razor stronami za pomocą następujących metod rozszerzeń:
Przeciążenia tych metod akceptują argumenty, które obejmują HttpContext
element . Te metody są funkcjonalnie równoważne url.Action i Url.Page, ale oferują dodatkową elastyczność i opcje.
Metody GetPath*
są najbardziej podobne do Url.Action
metod i Url.Page
, w których generują identyfikator URI zawierający ścieżkę bezwzględną. Metody GetUri*
zawsze generują bezwzględny identyfikator URI zawierający schemat i host. Metody, które akceptują HttpContext
generowanie identyfikatora URI w kontekście wykonywania żądania. Wartości trasy otoczenia , ścieżka podstawowa adresu URL, schemat i host z wykonywanego żądania są używane, chyba że zostaną zastąpione.
LinkGenerator jest wywoływana przy użyciu adresu. Generowanie identyfikatora URI odbywa się w dwóch krokach:
Metody udostępniane przez LinkGenerator obsługę standardowych możliwości generowania linków dla dowolnego typu adresu. Najwygodniejszym sposobem korzystania z generatora linków jest użycie metod rozszerzeń, które wykonują operacje dla określonego typu adresu:
Extension, metoda | opis |
---|---|
GetPathByAddress | Generuje identyfikator URI ze ścieżką bezwzględną na podstawie podanych wartości. |
GetUriByAddress | Generuje bezwzględny identyfikator URI na podstawie podanych wartości. |
Ostrzeżenie
Zwróć uwagę na następujące konsekwencje wywoływania LinkGenerator metod:
Użyj GetUri*
metod rozszerzeń z ostrożnością w konfiguracji aplikacji, która nie weryfikuje Host
nagłówka żądań przychodzących.
Host
Jeśli nagłówek żądań przychodzących nie jest weryfikowany, niezaufane dane wejściowe żądania mogą być wysyłane z powrotem do klienta w identyfikatorach URI w widoku lub na stronie. Zalecamy skonfigurowanie serwera przez wszystkie aplikacje produkcyjne w celu zweryfikowania nagłówka Host
pod kątem znanych prawidłowych wartości.
Należy używać LinkGenerator z ostrożnością w oprogramowania pośredniczącego w połączeniu z Map
lub MapWhen
.
Map*
zmienia ścieżkę podstawową wykonywania żądania, co wpływa na dane wyjściowe generowania linków.
LinkGenerator Wszystkie interfejsy API umożliwiają określenie ścieżki podstawowej. Określ pustą ścieżkę podstawową, aby cofnąć wpływ na generowanie linków Map*
.
W poniższym przykładzie oprogramowanie pośredniczące używa interfejsu LinkGenerator API do utworzenia linku do metody akcji zawierającej listę produktów. Użycie generatora linków przez wstrzyknięcie go do klasy i wywołanie GenerateLink
jest dostępne dla dowolnej klasy w aplikacji:
public class ProductsLinkMiddleware
{
private readonly LinkGenerator _linkGenerator;
public ProductsLinkMiddleware(RequestDelegate next, LinkGenerator linkGenerator)
{
_linkGenerator = linkGenerator;
}
public async Task InvokeAsync(HttpContext httpContext)
{
var url = _linkGenerator.GetPathByAction("ListProducts", "Store");
httpContext.Response.ContentType = "text/plain";
await httpContext.Response.WriteAsync($"Go to {url} to see our products.");
}
}
Tokeny w ramach {}
definicji parametrów trasy, które są powiązane, jeśli trasa jest zgodna. W segmencie trasy można zdefiniować więcej niż jeden parametr trasy, ale parametry trasy muszą być rozdzielone wartością literału. Na przykład nie jest prawidłową trasą, {controller=Home}{action=Index}
ponieważ nie ma wartości literału między {controller}
i {action}
. Parametry trasy muszą mieć nazwę i mogą mieć określone dodatkowe atrybuty.
Tekst literału inny niż parametry trasy (na przykład {id}
) i separator /
ścieżki musi być zgodny z tekstem w adresie URL. Dopasowywanie tekstu jest bez uwzględniania wielkości liter i oparte na dekodowanej reprezentacji ścieżki adresu URL. Aby dopasować ogranicznik {
parametru trasy literału lub }
, należy uruchomić ogranicznik, powtarzając znak. Na przykład {{
lub }}
.
Gwiazdka *
lub podwójna gwiazdka **
:
blog/{**slug}
/blog
i ma dowolną wartość po niej./blog
jest przypisywana do wartości trasy slug .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:
{**slug}"
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.
Parametry catch-all mogą być również zgodne z pustym ciągiem.
Parametr catch-all ucieczki odpowiednich znaków, gdy trasa jest używana do generowania adresu URL, w tym znaków separatora /
ścieżki. Na przykład trasa foo/{*path}
z wartościami { path = "my/path" }
tras generuje foo/my%2Fpath
wartość . Zwróć uwagę na unikniętą ukośnik. Aby użyć znaków separatora ścieżki dwukierunkowej, użyj prefiksu parametru **
trasy. Trasa foo/{**path}
z elementem { path = "my/path" }
generuje foo/my/path
wartość .
Wzorce adresów URL, które próbują przechwycić nazwę pliku z opcjonalnym rozszerzeniem pliku, mają dodatkowe uwagi. Rozważmy na przykład szablon files/{filename}.{ext?}
. Gdy wartości obu filename
i ext
istnieją, oba wartości są wypełniane. Jeśli w adresie URL istnieje tylko wartość filename
, trasa jest zgodna, ponieważ końcowy .
jest opcjonalny. Następujące adresy URL pasują do tej trasy:
/files/myFile.txt
/files/myFile
Parametry trasy mogą mieć wartości domyślne wyznaczone przez określenie wartości domyślnej po nazwie parametru oddzielonej znakiem równości (=
). Na przykład {controller=Home}
definiuje Home
jako wartość domyślną dla elementu controller
. Wartość domyślna jest używana, jeśli w adresie URL parametru nie ma żadnej wartości. Parametry trasy są opcjonalne, dołączając znak zapytania (?
) na końcu nazwy parametru. Na przykład id?
. Różnica między opcjonalnymi wartościami a domyślnymi parametrami trasy to:
Parametry trasy mogą mieć ograniczenia, które muszą być zgodne z wartością trasy powiązaną z adresem URL. Dodanie :
i ograniczenie nazwy po nazwie parametru trasy określa ograniczenie wbudowane w parametrze trasy. Jeśli ograniczenie wymaga argumentów, są one ujęte w nawiasy (...)
po nazwie ograniczenia. Można określić wiele ograniczeń wbudowanych przez dołączenie innej :
nazwy i ograniczenia.
Nazwa ograniczenia i argumenty są przekazywane do IInlineConstraintResolver usługi w celu utworzenia wystąpienia IRouteConstraint do użycia w przetwarzaniu adresów URL. Na przykład szablon blog/{article:minlength(10)}
trasy określa minlength
ograniczenie z argumentem 10
. Aby uzyskać więcej informacji na temat ograniczeń tras i listy ograniczeń udostępnianych przez platformę, zobacz sekcję Dokumentacja ograniczeń trasy.
Parametry trasy mogą również zawierać transformatory parametrów. Transformatory parametrów przekształcają wartość parametru podczas generowania łączy i pasujących akcji i stron do adresów URL. Podobnie jak ograniczenia, transformatory parametrów można dodać w tekście do parametru trasy przez dodanie :
nazwy parametru i transformatora po nazwie parametru trasy. Na przykład szablon blog/{article:slugify}
trasy określa slugify
transformator. Aby uzyskać więcej informacji na temat transformatorów parametrów, zobacz sekcję Dokumentacja przekształcania parametrów.
W poniższej tabeli przedstawiono przykładowe szablony tras i ich zachowanie:
Szablon trasy | Przykładowy pasujący identyfikator URI | Identyfikator URI żądania... |
---|---|---|
hello |
/hello |
Pasuje tylko do pojedynczej ścieżki /hello . |
{Page=Home} |
/ |
Dopasuj i ustawia wartość Page Home . |
{Page=Home} |
/Contact |
Dopasuj i ustawia wartość Page Contact . |
{controller}/{action}/{id?} |
/Products/List |
Mapuje na Products kontroler i List akcję. |
{controller}/{action}/{id?} |
/Products/Details/123 |
Mapuje na Products kontroler i Details akcję z ustawioną wartościąid 123. |
{controller=Home}/{action=Index}/{id?} |
/ |
Mapuje na kontroler i Home metodęIndex . Właściwość id jest ignorowana. |
{controller=Home}/{action=Index}/{id?} |
/Products |
Mapuje na kontroler i Products metodęIndex . Właściwość id jest ignorowana. |
Użycie szablonu jest zwykle najprostszym podejściem do routingu. Ograniczenia i wartości domyślne można również określić poza szablonem trasy.
Złożone segmenty są przetwarzane przez dopasowanie ograniczników literału od prawej do lewej w sposób niechłanny . Na przykład [Route("/a{b}c{d}")]
jest to złożony segment.
Złożone segmenty działają w określony sposób, aby można było z nich korzystać pomyślnie. W przykładzie w tej sekcji pokazano, dlaczego złożone segmenty działają prawidłowo tylko wtedy, gdy tekst ogranicznika nie pojawia się wewnątrz wartości parametrów. Użycie wyrażenia regularnego, a następnie ręczne wyodrębnienie wartości jest wymagane w przypadku bardziej złożonych przypadków.
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.
Jest to podsumowanie kroków, które routing wykonuje przy użyciu szablonu /a{b}c{d}
i ścieżki /abcd
adresu URL . Służy |
do wizualizacji sposobu działania algorytmu:
c
. Dlatego /abcd
jest wyszukiwany z prawej strony i znajduje /ab|c|d
.d
) jest teraz dopasowane do parametru {d}
trasy .a
. Dlatego /ab|c|d
jest wyszukiwany, zaczynając od miejsca, w którym odeszliśmy, a następnie a
znajduje się /|a|b|c|d
.b
) jest teraz zgodna z parametrem {b}
trasy .Oto przykład negatywnego przypadku przy użyciu tego samego szablonu /a{b}c{d}
i ścieżki /aabcd
adresu URL . Służy |
do wizualizowania sposobu działania algorytmu. Ten przypadek nie jest zgodny, co zostało wyjaśnione przez ten sam algorytm:
c
. Dlatego /aabcd
jest wyszukiwany z prawej strony i znajduje /aab|c|d
.d
) jest teraz dopasowane do parametru {d}
trasy .a
. Dlatego /aab|c|d
jest wyszukiwany, zaczynając od miejsca, w którym odeszliśmy, a następnie a
znajduje się /a|a|b|c|d
.b
) jest teraz zgodna z parametrem {b}
trasy .a
, ale algorytm zabrakło szablonu trasy do przeanalizowania, więc nie jest to dopasowanie.Ponieważ pasujący algorytm nie jest chciwy:
Wyrażenia regularne zapewniają znacznie większą kontrolę nad ich zachowaniem dopasowania.
Zachłanne dopasowanie, znane również jako leniwe dopasowanie, pasuje do największego możliwego ciągu. Niechłanny pasuje do najmniejszego możliwego ciągu.
Ograniczenia trasy są wykonywane, gdy wystąpiło dopasowanie do przychodzącego adresu URL, a ścieżka adresu URL jest tokenizowana do wartości tras. Ograniczenia tras zazwyczaj sprawdzają wartość trasy skojarzoną za pośrednictwem szablonu trasy i podejmowania prawdziwej lub fałszywej decyzji o tym, czy wartość jest akceptowalna. Niektóre ograniczenia trasy używają danych poza wartością trasy, aby rozważyć, czy żądanie można kierować. Na przykład HttpMethodRouteConstraint może zaakceptować lub odrzucić żądanie na podstawie jego czasownika HTTP. Ograniczenia są używane w żądaniach routingu i generowaniu linków.
Ostrzeżenie
Nie używaj ograniczeń do walidacji danych wejściowych. Jeśli ograniczenia są używane do walidacji danych wejściowych, nieprawidłowe dane wejściowe będą skutkować odpowiedzią 404
Nie znaleziono. Nieprawidłowe dane wejściowe powinny spowodować wygenerowanie nieprawidłowego 400
żądania z odpowiednim komunikatem o błędzie. Ograniczenia trasy służą do uściślania podobnych tras, a nie sprawdzania poprawności danych wejściowych dla określonej trasy.
W poniższej tabeli przedstawiono przykładowe ograniczenia tras i ich oczekiwane zachowanie:
ograniczenie | Przykład | Przykładowe dopasowania | Uwagi |
---|---|---|---|
int |
{id:int} |
123456789 , -123456789 |
Pasuje do dowolnej liczby całkowitej |
bool |
{active:bool} |
true , FALSE |
Dopasuj lub true false . Bez uwzględniania wielkości liter |
datetime |
{dob:datetime} |
2016-12-31 , 2016-12-31 7:32pm |
Pasuje do prawidłowej DateTime wartości w niezmiennej kulturze. Zobacz poprzednie ostrzeżenie. |
decimal |
{price:decimal} |
49.99 , -1,000.01 |
Pasuje do prawidłowej decimal wartości w niezmiennej kulturze. Zobacz poprzednie ostrzeżenie. |
double |
{weight:double} |
1.234 , -1,001.01e8 |
Pasuje do prawidłowej double wartości w niezmiennej kulturze. Zobacz poprzednie ostrzeżenie. |
float |
{weight:float} |
1.234 , -1,001.01e8 |
Pasuje do prawidłowej float wartości w niezmiennej kulturze. Zobacz poprzednie ostrzeżenie. |
guid |
{id:guid} |
CD2C1638-1638-72D5-1638-DEADBEEF1638 |
Pasuje do prawidłowej Guid wartości |
long |
{ticks:long} |
123456789 , -123456789 |
Pasuje do prawidłowej long wartości |
minlength(value) |
{username:minlength(4)} |
Rick |
Ciąg musi mieć co najmniej 4 znaki |
maxlength(value) |
{filename:maxlength(8)} |
MyFile |
Ciąg nie może zawierać więcej niż 8 znaków |
length(length) |
{filename:length(12)} |
somefile.txt |
Ciąg musi mieć długość dokładnie 12 znaków |
length(min,max) |
{filename:length(8,16)} |
somefile.txt |
Ciąg musi mieć co najmniej 8 znaków i nie więcej niż 16 znaków |
min(value) |
{age:min(18)} |
19 |
Wartość całkowita musi być co najmniej 18 |
max(value) |
{age:max(120)} |
91 |
Wartość całkowita nie może być większa niż 120 |
range(min,max) |
{age:range(18,120)} |
91 |
Wartość całkowita musi być co najmniej 18, ale nie większa niż 120 |
alpha |
{name:alpha} |
Rick |
Ciąg musi składać się z co najmniej jednego znaku a -z alfabetycznego i bez uwzględniania wielkości liter. |
regex(expression) |
{ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)} |
123-45-6789 |
Ciąg musi być zgodny z wyrażeniem regularnym. Zobacz porady dotyczące definiowania wyrażenia regularnego. |
required |
{name:required} |
Rick |
Służy do wymuszania, że wartość nieparametrowa jest obecna podczas generowania adresu URL |
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 ograniczeń rozdzielonych dwukropkami można zastosować do jednego parametru. Na przykład następujące ograniczenie ogranicza parametr do wartości całkowitej 1 lub większej:
[Route("users/{id:int:min(1)}")]
public User GetUserById(int id) { }
Ostrzeżenie
Ograniczenia trasy, które weryfikują adres URL i są konwertowane na typ CLR, zawsze używają niezmiennej kultury. Na przykład konwersja na typ int
CLR lub DateTime
. Te ograniczenia zakładają, że adres URL nie jest lokalizowalny. Ograniczenia trasy dostarczone przez platformę nie modyfikują wartości przechowywanych w wartościach tras. Wszystkie wartości tras analizowane z adresu URL są przechowywane jako ciągi. Na przykład float
ograniczenie próbuje przekonwertować wartość trasy na zmiennoprzecinkowy, ale przekonwertowana wartość jest używana tylko do sprawdzania, czy można ją przekonwertować na zmiennoprzecinkowy.
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.
Wyrażenia regularne można określić jako ograniczenia wbudowane przy użyciu regex(...)
ograniczenia trasy. Metody w MapControllerRoute rodzinie akceptują również literał obiektów ograniczeń. Jeśli ten formularz jest używany, wartości ciągów są interpretowane jako wyrażenia regularne.
Poniższy kod używa wbudowanego ograniczenia wyrażeń regularnych:
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("{message:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)}",
context =>
{
return context.Response.WriteAsync("inline-constraint match");
});
});
Poniższy kod używa literału obiektu do określenia ograniczenia wyrażenia regularnego:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "people",
pattern: "People/{ssn}",
constraints: new { ssn = "^\\d{3}-\\d{2}-\\d{4}$", },
defaults: new { controller = "People", action = "List", });
});
Struktura ASP.NET Core dodaje RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant
do konstruktora wyrażeń regularnych. Zobacz RegexOptions opis tych elementów członkowskich.
Wyrażenia regularne używają ograniczników i tokenów podobnych do tych używanych przez routing i język C#. Tokeny wyrażeń regularnych muszą zostać uniknięci. Aby użyć wyrażenia ^\d{3}-\d{2}-\d{4}$
regularnego w ograniczeniu wbudowanym, użyj jednego z następujących elementów:
\
znaki podane w ciągu jako \\
znaki w pliku źródłowym języka C#, aby uniknąć \
znaku ucieczki ciągu.Aby uniknąć znaków {
ogranicznika parametru routingu , , }
[
, ]
, podwaja znaki w wyrażeniu, na przykład {{
, , }}
, [[
, ]]
. W poniższej tabeli przedstawiono wyrażenie regularne i jego wersję unikniętą:
Regular expression | Wyrażenie regularne o wartości ucieczki |
---|---|
^\d{3}-\d{2}-\d{4}$ |
^\\d{{3}}-\\d{{2}}-\\d{{4}}$ |
^[a-z]{2}$ |
^[[a-z]]{{2}}$ |
Wyrażenia regularne używane w routingu często zaczynają się od ^
znaku i pasują do pozycji początkowej ciągu. Wyrażenia często kończą się znakiem $
i pasują do końca ciągu. Znaki ^
i $
zapewniają, że wyrażenie regularne jest zgodne z całą wartością parametru trasy.
^
Bez znaków i $
wyrażenie regularne pasuje do dowolnego podciągu w ciągu, co jest często niepożądane. W poniższej tabeli przedstawiono przykłady i wyjaśniono, dlaczego są one zgodne lub nie są zgodne:
Wyrażenie | String | Match | Komentarz |
---|---|---|---|
[a-z]{2} |
hello | Tak | Dopasowania podciągów |
[a-z]{2} |
123abc456 | Tak | Dopasowania podciągów |
[a-z]{2} |
mz | Tak | Dopasuj wyrażenie |
[a-z]{2} |
MZ | Tak | Nie uwzględnia wielkości liter |
^[a-z]{2}$ |
hello | Nie. | Zobacz ^ i $ powyżej |
^[a-z]{2}$ |
123abc456 | Nie. | Zobacz ^ i $ powyżej |
Aby uzyskać więcej informacji na temat składni wyrażeń regularnych, zobacz .NET Framework Regular Expressions (Wyrażenia regularne programu .NET Framework).
Aby ograniczyć parametr do znanego zestawu możliwych wartości, użyj wyrażenia regularnego. Na przykład {action:regex(^(list|get|create)$)}
pasuje action
tylko do wartości trasy do list
, get
lub create
. Jeśli zostanie przekazany do słownika ograniczeń, ciąg ^(list|get|create)$
jest równoważny. Ograniczenia przekazywane w słowniku ograniczeń, które nie są zgodne z jednym ze znanych ograniczeń, są również traktowane jako wyrażenia regularne. Ograniczenia przekazywane w szablonie, które nie pasują do jednego ze znanych ograniczeń, nie są traktowane jako wyrażenia regularne.
Ograniczenia trasy niestandardowej można utworzyć przez zaimplementowanie interfejsu IRouteConstraint . Interfejs IRouteConstraint
zawiera Matchwartość , która zwraca true
wartość, jeśli ograniczenie jest spełnione i false
w przeciwnym razie.
Ograniczenia trasy niestandardowej są rzadko potrzebne. Przed zaimplementowaniem ograniczenia trasy niestandardowej rozważ alternatywy, takie jak powiązanie modelu.
Folder ASP.NET Core Constraints zawiera dobre przykłady tworzenia ograniczeń. Na przykład GuidRouteConstraint.
Aby użyć niestandardowego IRouteConstraint
, typ ograniczenia trasy musi być zarejestrowany w aplikacji ConstraintMap w kontenerze usługi. Jest ConstraintMap
to słownik, który mapuje klucze ograniczeń trasy na IRouteConstraint
implementacje, które weryfikują te ograniczenia. Aplikacje ConstraintMap
można zaktualizować w Startup.ConfigureServices
ramach usług. Dodaj wywołanie usługiRouting lub przez skonfigurowanie bezpośrednio za RouteOptions pomocą services.Configure<RouteOptions>
polecenia . Na przykład:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddRouting(options =>
{
options.ConstraintMap.Add("customName", typeof(MyCustomConstraint));
});
}
Powyższe ograniczenie jest stosowane w następującym kodzie:
[Route("api/[controller]")]
[ApiController]
public class TestController : ControllerBase
{
// GET /api/test/3
[HttpGet("{id:customName}")]
public IActionResult Get(string id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
// GET /api/test/my/3
[HttpGet("my/{id:customName}")]
public IActionResult Get(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
MyDisplayRouteInfo jest dostarczany przez pakiet NuGet Rick.Docs.Samples.RouteInfo i wyświetla informacje o trasie.
Implementacja elementu uniemożliwia MyCustomConstraint
zastosowanie do parametru 0
trasy:
class MyCustomConstraint : IRouteConstraint
{
private Regex _regex;
public MyCustomConstraint()
{
_regex = new Regex(@"^[1-9]*$",
RegexOptions.CultureInvariant | RegexOptions.IgnoreCase,
TimeSpan.FromMilliseconds(100));
}
public bool Match(HttpContext httpContext, IRouter route, string routeKey,
RouteValueDictionary values, RouteDirection routeDirection)
{
if (values.TryGetValue(routeKey, out object value))
{
var parameterValueString = Convert.ToString(value,
CultureInfo.InvariantCulture);
if (parameterValueString == null)
{
return false;
}
return _regex.IsMatch(parameterValueString);
}
return false;
}
}
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.
Powyższy kod ma następujące działanie:
0
w {id}
segmencie trasy.Poniższy kod jest lepszym podejściem do zapobiegania przetwarzaniu elementu zawierającego id
0
element:
[HttpGet("{id}")]
public IActionResult Get(string id)
{
if (id.Contains('0'))
{
return StatusCode(StatusCodes.Status406NotAcceptable);
}
return ControllerContext.MyDisplayRouteInfo(id);
}
Powyższy kod ma następujące zalety w porównaniu z podejściem MyCustomConstraint
:
0
wartość .Transformatory parametrów:
Na przykład funkcja przekształcania parametrów niestandardowych slugify
we wzorcu blog\{article:slugify}
trasy z poleceniem Url.Action(new { article = "MyTestArticle" })
generuje blog\my-test-article
wartość .
Rozważmy następującą IOutboundParameterTransformer
implementację:
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();
}
}
Aby użyć transformatora parametrów we wzorcu trasy, skonfiguruj go przy użyciu ConstraintMap polecenia w pliku Startup.ConfigureServices
:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddRouting(options =>
{
options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer);
});
}
Struktura ASP.NET Core używa funkcji przekształcania parametrów w celu przekształcenia identyfikatora URI, w którym punkt końcowy jest rozpoznawany. Na przykład transformatory parametrów przekształcają wartości tras używane do dopasowania area
wartości , , controller
action
i page
.
routes.MapControllerRoute(
name: "default",
template: "{controller:slugify=Home}/{action:slugify=Index}/{id?}");
W przypadku poprzedniego szablonu trasy akcja SubscriptionManagementController.GetAll
jest zgodna z identyfikatorem URI /subscription-management/get-all
. Funkcja przekształcania parametrów nie zmienia wartości tras używanych do generowania łącza. Na przykład Url.Action("GetAll", "SubscriptionManagement")
dane wyjściowe ./subscription-management/get-all
ASP.NET Core udostępnia konwencje interfejsu API dotyczące używania transformatorów parametrów z wygenerowanymi trasami:
Ta sekcja zawiera odwołanie do algorytmu zaimplementowanego przez generowanie adresów URL. W praktyce większość złożonych przykładów generowania adresów URL używa kontrolerów lub Razor stron. Aby uzyskać dodatkowe informacje, zobacz routing w kontrolerach .
Proces generowania adresu URL rozpoczyna się od wywołania metody LinkGenerator.GetPathByAddress lub podobnej metody. Metoda jest dostarczana z adresem, zestawem wartości tras i opcjonalnie informacjami o bieżącym żądaniu z .HttpContext
Pierwszym krokiem jest użycie adresu w celu rozpoznania zestawu punktów końcowych kandydata przy użyciu elementu zgodnego z typem IEndpointAddressScheme<TAddress>
adresu.
Po znalezieniu zestawu kandydatów przez schemat adresów punkty końcowe są uporządkowane i przetwarzane iteracyjnie, dopóki operacja generowania adresu URL nie powiedzie się. Generowanie adresu URL nie sprawdza niejednoznaczności. Zwracany pierwszy wynik jest wynikiem końcowym.
Pierwszym krokiem generowania adresu URL rozwiązywania problemów jest ustawienie poziomu rejestrowania Microsoft.AspNetCore.Routing
na TRACE
.
LinkGenerator
rejestruje wiele szczegółów dotyczących jego przetwarzania, które mogą być przydatne do rozwiązywania problemów.
Aby uzyskać szczegółowe informacje na temat generowania adresów URL, zobacz Dokumentację generowania adresów URL.
Adresy to koncepcja generowania adresów URL używana do powiązania wywołania z generatorem linków z zestawem kandydatów punktów końcowych.
Adresy to rozszerzalna koncepcja, która domyślnie zawiera dwie implementacje:
string
) jako adresu: IUrlHelper
pomocników tagów, pomocników HTML, wyniki akcji itp.Rolą schematu adresów jest skojarzenie adresu i pasujących punktów końcowych według dowolnych kryteriów:
Z bieżącego żądania routing uzyskuje dostęp do wartości tras bieżącego żądania HttpContext.Request.RouteValues
. Wartości skojarzone z bieżącym żądaniem są określane jako wartości otoczenia. W celu jasności dokumentacja odnosi się do wartości tras przekazywanych do metod jako jawnych wartości.
W poniższym przykładzie przedstawiono wartości otoczenia i jawne wartości. Zapewnia ona wartości otoczenia z bieżącego żądania i jawnych wartości: : { id = 17, }
public class WidgetController : Controller
{
private readonly LinkGenerator _linkGenerator;
public WidgetController(LinkGenerator linkGenerator)
{
_linkGenerator = linkGenerator;
}
public IActionResult Index()
{
var url = _linkGenerator.GetPathByAction(HttpContext,
null, null,
new { id = 17, });
return Content(url);
}
Powyższy kod ma następujące działanie:
/Widget/Index/17
Poniższy kod nie zawiera wartości otoczenia i jawnych wartości: : { controller = "Home", action = "Subscribe", id = 17, }
public IActionResult Index2()
{
var url = _linkGenerator.GetPathByAction("Subscribe", "Home",
new { id = 17, });
return Content(url);
}
Poprzednia metoda zwraca /Home/Subscribe/17
Poniższy kod w zwracaniu WidgetController
wartości /Widget/Subscribe/17
:
var url = _linkGenerator.GetPathByAction("Subscribe", null,
new { id = 17, });
Poniższy kod udostępnia kontrolerowi wartości otoczenia w bieżącym żądaniu i jawnych wartościach: : { action = "Edit", id = 17, }
public class GadgetController : Controller
{
public IActionResult Index()
{
var url = Url.Action("Edit", new { id = 17, });
return Content(url);
}
Powyższy kod:
/Gadget/Edit/17
jest zwracany.action
nazwę i route
wartości.Poniższy kod zawiera wartości otoczenia z bieżącego żądania i jawnych wartości: : { page = "./Edit, id = 17, }
public class IndexModel : PageModel
{
public void OnGet()
{
var url = Url.Page("./Edit", new { id = 17, });
ViewData["URL"] = url;
}
}
Powyższy kod ustawia wartość url
, /Edit/17
gdy strona edycji Razor zawiera następującą dyrektywę strony:
@page "{id:int}"
Jeśli strona Edycja nie zawiera szablonu "{id:int}"
trasy, url
to /Edit?id=17
.
Zachowanie wzorca MVC IUrlHelper dodaje warstwę złożoności oprócz reguł opisanych tutaj:
IUrlHelper
zawsze udostępnia wartości tras z bieżącego żądania jako wartości otoczenia.action
wartości i controller
trasy jako wartości jawne, chyba że zostanie zastąpione przez dewelopera.page
wartość trasy jako jawną wartość, chyba że zostanie zastąpiona. IUrlHelper.Page
zawsze zastępuje bieżącą handler
wartość trasy wartością null
jawną, chyba że zostanie zastąpiona.Użytkownicy są często zaskoczeni szczegółami zachowania wartości otoczenia, ponieważ MVC nie wydaje się przestrzegać własnych reguł. Ze względów historycznych i zgodności niektóre wartości tras, takie jak action
, controller
, page
i handler
mają własne zachowanie specjalne.
Równoważna funkcjonalność zapewniana przez LinkGenerator.GetPathByAction
program i LinkGenerator.GetPathByPage
duplikuje te anomalie w IUrlHelper
celu zapewnienia zgodności.
Po znalezieniu zestawu punktów końcowych kandydata algorytm generowania adresów URL:
Pierwszym krokiem w tym procesie jest unieważnienie wartości trasy. Unieważnienie wartości trasy to proces, za pomocą którego routing decyduje, które wartości tras z wartości otoczenia powinny być używane i które powinny być ignorowane. Każda wartość otoczenia jest uwzględniana i połączona z jawnymi wartościami lub ignorowana.
Najlepszym sposobem myślenia o roli wartości otoczenia jest to, że próbują zapisać deweloperów aplikacji wpisywanie, w niektórych typowych przypadkach. Tradycyjnie scenariusze, w których wartości otoczenia są przydatne, są związane z MVC:
Wywołania do LinkGenerator
lub IUrlHelper
tego zwracania null
są zwykle spowodowane przez niezrozumienie unieważnienia wartości trasy. Rozwiąż problemy z nieprawidłową wartością trasy, określając jawnie więcej wartości trasy, aby sprawdzić, czy rozwiązuje to problem.
Unieważnienie wartości trasy działa zgodnie z założeniem, że schemat adresu URL aplikacji jest hierarchiczny, z hierarchią utworzoną od lewej do prawej. Rozważmy podstawowy szablon {controller}/{action}/{id?}
trasy kontrolera, aby uzyskać intuicyjne zrozumienie sposobu działania tej metody w praktyce.
Zmiana wartości unieważnia wszystkie wartości trasy, które są wyświetlane po prawej stronie. Odzwierciedla to założenie dotyczące hierarchii. Jeśli aplikacja ma wartość otoczenia dla id
, a operacja określa inną wartość dla elementu controller
:
id
nie będzie ponownie używany, ponieważ {controller}
znajduje się po lewej {id?}
stronie .Kilka przykładów demonstrujących tę zasadę:
id
, wartość otoczenia dla id
elementu jest ignorowana. Wartości otoczenia i controller
action
mogą być używane.action
, każda wartość otoczenia dla action
elementu jest ignorowana. Można użyć wartości controller
otoczenia. Jeśli jawna wartość parametru action
jest inna niż wartość otoczenia dla action
wartości , id
wartość nie zostanie użyta. Jeśli jawna wartość parametru action
jest taka sama jak wartość otoczenia dla action
, id
można użyć wartości .controller
, każda wartość otoczenia dla controller
elementu jest ignorowana. Jeśli jawna wartość parametru controller
jest inna niż wartość otoczenia dla controller
, action
wartości i id
nie będą używane. Jeśli jawna wartość parametru controller
jest taka sama jak wartość otoczenia dla controller
, action
można użyć wartości i id
.Ten proces jest jeszcze bardziej skomplikowany przez istnienie tras atrybutów i dedykowanych tras konwencjonalnych. Kontroler konwencjonalnych tras, takich jak {controller}/{action}/{id?}
określanie hierarchii przy użyciu parametrów trasy. W przypadku dedykowanych tras konwencjonalnych i tras atrybutów do kontrolerów i Razor stron:
W takich przypadkach generowanie adresów URL definiuje koncepcję wymaganych wartości . Punkty końcowe utworzone przez kontrolery i Razor strony mają wymagane wartości określone, które umożliwiają działanie unieważnienia wartości trasy.
Algorytm unieważniania wartości trasy szczegółowo:
W tym momencie operacja generowania adresu URL jest gotowa do oceny ograniczeń trasy. Zestaw akceptowanych wartości jest połączony z wartościami domyślnymi parametrów, które są dostarczane do ograniczeń. Jeśli wszystkie ograniczenia przechodzą, operacja będzie kontynuowana.
Następnie akceptowane wartości mogą służyć do rozszerzania szablonu trasy. Szablon trasy jest przetwarzany:
Wartości jawnie podane, które nie pasują do segmentu trasy, są dodawane do ciągu zapytania. W poniższej tabeli przedstawiono wynik użycia szablonu {controller}/{action}/{id?}
trasy .
Wartości otoczenia | Jawne wartości | Result |
---|---|---|
controller = "Home" | action = "Informacje" | /Home/About |
controller = "Home" | controller = "Order", action = "About" | /Order/About |
controller = "Home", color = "Red" | action = "Informacje" | /Home/About |
controller = "Home" | action = "About", color = "Red" | /Home/About?color=Red |
Od wersji ASP.NET Core 3.0 niektóre schematy generowania adresów URL używane we wcześniejszych wersjach ASP.NET Core nie działają dobrze z generowaniem adresów URL. Zespół ASP.NET Core planuje dodać funkcje w celu zaspokojenia tych potrzeb w przyszłej wersji. Na razie najlepszym rozwiązaniem jest użycie starszego routingu.
Poniższy kod przedstawia przykład schematu generowania adresów URL, który nie jest obsługiwany przez routing.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute("default",
"{culture}/{controller=Home}/{action=Index}/{id?}");
endpoints.MapControllerRoute("blog", "{culture}/{**slug}",
new { controller = "Blog", action = "ReadPost", });
});
W poprzednim kodzie culture
parametr trasy jest używany do lokalizacji. Pragnieniem jest, culture
aby parametr zawsze był akceptowany jako wartość otoczenia.
culture
Jednak parametr nie jest akceptowany jako wartość otoczenia ze względu na sposób działania wymaganych wartości:
"default"
culture
trasy parametr trasy znajduje się po lewej stronie controller
elementu , więc zmiany controller
nie będą unieważniać culture
elementu ."blog"
culture
trasy parametr trasy jest uznawany za znajdujący się po prawej stronie controller
elementu , który jest wyświetlany w wymaganych wartościach.Poniższe linki zawierają informacje na temat konfigurowania metadanych punktu końcowego:
[MinimumAgeAuthorize]
RequireHost stosuje ograniczenie do trasy, która wymaga określonego hosta. Parametr RequireHost
lub [Host] może być:
www.domain.com
, pasuje www.domain.com
do dowolnego portu.*.domain.com
, pasuje www.domain.com
do , subdomain.domain.com
lub www.subdomain.domain.com
na dowolnym porcie.*:5000
, pasuje do portu 5000 z dowolnym hostem.www.domain.com:5000
lub *.domain.com:5000
, pasuje do hosta i portu.Można określić wiele parametrów przy użyciu polecenia RequireHost
lub [Host]
. Ograniczenie pasuje do hostów prawidłowych dla dowolnego z parametrów. Na przykład pasuje [Host("domain.com", "*.domain.com")]
do domain.com
, www.domain.com
i subdomain.domain.com
.
Poniższy kod używa RequireHost
metody , aby wymagać określonego hosta na trasie:
public void Configure(IApplicationBuilder app)
{
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", context => context.Response.WriteAsync("Hi Contoso!"))
.RequireHost("contoso.com");
endpoints.MapGet("/", context => context.Response.WriteAsync("AdventureWorks!"))
.RequireHost("adventure-works.com");
endpoints.MapHealthChecks("/healthz").RequireHost("*:8080");
});
}
Poniższy kod używa atrybutu [Host]
na kontrolerze, aby wymagać któregokolwiek z określonych hostów:
[Host("contoso.com", "adventure-works.com")]
public class ProductController : Controller
{
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
[Host("example.com:8080")]
public IActionResult Privacy()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Po zastosowaniu atrybutu [Host]
zarówno do kontrolera, jak i metody akcji:
Większość routingu została zaktualizowana w ASP.NET Core 3.0 w celu zwiększenia wydajności.
Gdy aplikacja ma problemy z wydajnością, routing jest często podejrzewany jako problem. Podejrzewa się, że routing polega na tym, że struktury, takie jak kontrolery i Razor strony, zgłaszają ilość czasu spędzonego w strukturze w komunikatach rejestrowania. W przypadku znacznej różnicy między czasem zgłoszonym przez kontrolery a łącznym czasem żądania:
Routing jest testowany pod kątem wydajności przy użyciu tysięcy punktów końcowych. Jest mało prawdopodobne, aby typowa aplikacja napotkała problem z wydajnością, ponieważ jest zbyt duża. Najczęstszą główną przyczyną niskiej wydajności routingu jest zwykle nieprawidłowe zachowanie niestandardowego oprogramowania pośredniczącego.
W poniższym przykładzie kodu przedstawiono podstawową technikę zawężania źródła opóźnienia:
public void Configure(IApplicationBuilder app, ILogger<Startup> logger)
{
app.Use(next => async context =>
{
var sw = Stopwatch.StartNew();
await next(context);
sw.Stop();
logger.LogInformation("Time 1: {ElapsedMilliseconds}ms", sw.ElapsedMilliseconds);
});
app.UseRouting();
app.Use(next => async context =>
{
var sw = Stopwatch.StartNew();
await next(context);
sw.Stop();
logger.LogInformation("Time 2: {ElapsedMilliseconds}ms", sw.ElapsedMilliseconds);
});
app.UseAuthorization();
app.Use(next => async context =>
{
var sw = Stopwatch.StartNew();
await next(context);
sw.Stop();
logger.LogInformation("Time 3: {ElapsedMilliseconds}ms", sw.ElapsedMilliseconds);
});
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Timing test.");
});
});
}
Routing czasowy:
Jest to podstawowy sposób zawężenia opóźnienia, gdy jest to znaczące, na przykład więcej niż 10ms
. Odejmowanie Time 2
z Time 1
raportów czasu spędzonego wewnątrz oprogramowania pośredniczącego UseRouting
.
Poniższy kod używa bardziej kompaktowego podejścia do poprzedniego kodu chronometrażu:
public sealed class MyStopwatch : IDisposable
{
ILogger<Startup> _logger;
string _message;
Stopwatch _sw;
public MyStopwatch(ILogger<Startup> logger, string message)
{
_logger = logger;
_message = message;
_sw = Stopwatch.StartNew();
}
private bool disposed = false;
public void Dispose()
{
if (!disposed)
{
_logger.LogInformation("{Message }: {ElapsedMilliseconds}ms",
_message, _sw.ElapsedMilliseconds);
disposed = true;
}
}
}
public void Configure(IApplicationBuilder app, ILogger<Startup> logger)
{
int count = 0;
app.Use(next => async context =>
{
using (new MyStopwatch(logger, $"Time {++count}"))
{
await next(context);
}
});
app.UseRouting();
app.Use(next => async context =>
{
using (new MyStopwatch(logger, $"Time {++count}"))
{
await next(context);
}
});
app.UseAuthorization();
app.Use(next => async context =>
{
using (new MyStopwatch(logger, $"Time {++count}"))
{
await next(context);
}
});
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Timing test.");
});
});
}
Poniższa lista zawiera wgląd w funkcje routingu, które są stosunkowo kosztowne w porównaniu z podstawowymi szablonami tras:
{x}-{y}-{z}
): Ta sekcja zawiera wskazówki dotyczące autorów bibliotek opartych na routingu. Te szczegóły mają na celu zapewnienie, że deweloperzy aplikacji mają dobre środowisko korzystania z bibliotek i struktur rozszerzających routing.
Aby utworzyć strukturę korzystającą z routingu do dopasowywania adresów URL, zacznij od zdefiniowania środowiska użytkownika, które opiera się na systemie UseEndpoints.
Kompilacja DO na podstawie funkcji IEndpointRouteBuilder. Dzięki temu użytkownicy mogą tworzyć struktury przy użyciu innych funkcji ASP.NET Core bez pomyłek. Każdy szablon ASP.NET Core zawiera routing. Załóżmy, że routing jest obecny i znany użytkownikom.
app.UseEndpoints(endpoints =>
{
// Your framework
endpoints.MapMyFramework(...);
endpoints.MapHealthChecks("/healthz");
});
NALEŻY zwrócić zapieczętowany typ betonowy z wywołania, do MapMyFramework(...)
którego implementuje IEndpointConventionBuilderelement . Większość metod struktury Map...
jest zgodne z tym wzorcem. Interfejs IEndpointConventionBuilder
:
Deklarowanie własnego typu umożliwia dodanie do konstruktora własnych funkcji specyficznych dla platformy. Można zawinąć konstruktora zadeklarowanego przez platformę i przekazać do niego wywołania.
app.UseEndpoints(endpoints =>
{
// Your framework
endpoints.MapMyFramework(...).RequireAuthorization()
.WithMyFrameworkFeature(awesome: true);
endpoints.MapHealthChecks("/healthz");
});
ROZWAŻ napisanie własnego EndpointDataSourcepliku .
EndpointDataSource
jest elementem pierwotnym niskiego poziomu do deklarowania i aktualizowania kolekcji punktów końcowych.
EndpointDataSource
to zaawansowany interfejs API używany przez kontrolery i Razor strony.
Testy routingu mają podstawowy przykład nieakcyjnego źródła danych.
NIE próbuj domyślnie rejestrować konta EndpointDataSource
. Wymagaj od użytkowników zarejestrowania struktury w programie UseEndpoints. Filozofią routingu jest to, że nic nie jest dołączane domyślnie i UseEndpoints
jest to miejsce do rejestrowania punktów końcowych.
ROZWAŻ zdefiniowanie typów metadanych jako interfejsu.
DO umożliwia używanie typów metadanych jako atrybutu w klasach i metodach.
public interface ICoolMetadata
{
bool IsCool { get; }
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CoolMetadataAttribute : Attribute, ICoolMetadata
{
public bool IsCool => true;
}
Struktury, takie jak kontrolery i Razor strony, obsługują stosowanie atrybutów metadanych do typów i metod. W przypadku deklarowania typów metadanych:
Deklarowanie typu metadanych jako interfejsu zwiększa kolejną warstwę elastyczności:
NIE umożliwia przesłaniania metadanych, jak pokazano w poniższym przykładzie:
public interface ICoolMetadata
{
bool IsCool { get; }
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CoolMetadataAttribute : Attribute, ICoolMetadata
{
public bool IsCool => true;
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class SuppressCoolMetadataAttribute : Attribute, ICoolMetadata
{
public bool IsCool => false;
}
[CoolMetadata]
public class MyController : Controller
{
public void MyCool() { }
[SuppressCoolMetadata]
public void Uncool() { }
}
Najlepszym sposobem przestrzegania tych wytycznych jest unikanie definiowania metadanych znaczników:
Kolekcja metadanych jest uporządkowana i obsługuje zastępowanie według priorytetu. W przypadku kontrolerów metadane metody akcji są najbardziej specyficzne.
DO sprawia, że oprogramowanie pośredniczące jest przydatne z routingiem i bez tego routingu.
app.UseRouting();
app.UseAuthorization(new AuthorizationPolicy() { ... });
app.UseEndpoints(endpoints =>
{
// Your framework
endpoints.MapMyFramework(...).RequireAuthorization();
});
Jako przykład tej wskazówki należy wziąć pod uwagę UseAuthorization
oprogramowanie pośredniczące. Oprogramowanie pośredniczące autoryzacji umożliwia przekazanie zasad rezerwowych.
Zasady rezerwowe, jeśli zostały określone, mają zastosowanie do obu następujących elementów:
Dzięki temu oprogramowanie pośredniczące autoryzacji jest przydatne poza kontekstem routingu. Oprogramowanie pośredniczące autoryzacji może służyć do tradycyjnego programowania oprogramowania pośredniczącego.
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"
}
}
}
Opinia o produkcie ASP.NET Core
ASP.NET Core to projekt typu open source. Wybierz link, aby przekazać opinię:
Zdarzenia
Power BI DataViz World Championships
14 lut, 16 - 31 mar, 16
Z 4 szans na wejście, można wygrać pakiet konferencji i zrobić go do LIVE Grand Finale w Las Vegas
Dowiedz się więcejSzkolenie
Moduł
Use pages, routing, and layouts to improve Blazor navigation - Training
Learn how to optimize your app's navigation, use parameters from the URL, and create reusable layouts in a Blazor web app.
Dokumentacja
Routing to controller actions in ASP.NET Core
Learn how ASP.NET Core MVC uses Routing Middleware to match URLs of incoming requests and map them to actions.
Learn about ASP.NET Core middleware and the request pipeline.
ASP.NET Core Series: Endpoint Routing
ASP.NET Core is a cross-platform, high-performance, open-source framework for building modern, cloud ready, connected applications.In this episode, Ryan Nowak from the ASP.NET team comes on to talk to us about Endpoint routing. What is it? What can it do? How can we use it?[01:10] - What is endpoint routing?[09:28] - What is the routing zone?[13:50] - Inspecting endpoints through middleware[19:18] - Retrieving the authorization policy metadata from an endpoint[23:08] - Turning middleware into &quo
Learn how model binding in ASP.NET Core works and how to customize its behavior.