Směrování a výběr akcí ve webovém rozhraní API ASP.NET

Tento článek popisuje, jak ASP.NET webové rozhraní API směruje požadavek HTTP na konkrétní akci na kontroleru.

Poznámka

Základní přehled směrování najdete v tématu Směrování ve webovém rozhraní API ASP.NET.

Tento článek se zabývá podrobnostmi procesu směrování. Pokud vytvoříte projekt webového rozhraní API a zjistíte, že některé požadavky se nesměrují očekávaným způsobem, snad vám pomůže tento článek.

Směrování má tři hlavní fáze:

  1. Porovnávání identifikátoru URI se šablonou trasy.
  2. Výběr kontroleru
  3. Výběr akce

Některé části procesu můžete nahradit vlastním chováním. V tomto článku popíšu výchozí chování. Na konci si povšimnu míst, kde můžete přizpůsobit chování.

Šablony tras

Šablona trasy vypadá podobně jako cesta URI, ale může obsahovat zástupné hodnoty označené složenými závorkami:

"api/{controller}/public/{category}/{id}"

Při vytváření trasy můžete zadat výchozí hodnoty pro některé nebo všechny zástupné symboly:

defaults: new { category = "all" }

Můžete také zadat omezení, která omezují, jak se segment identifikátoru URI může shodovat se zástupným symbolem:

constraints: new { id = @"\d+" }   // Only matches if "id" is one or more digits.

Rozhraní se pokusí spárovat segmenty v cestě identifikátoru URI k šabloně. Literály v šabloně se musí přesně shodovat. Zástupný symbol odpovídá libovolné hodnotě, pokud nezadáte omezení. Architektura neodpovídá jiným částem identifikátoru URI, jako je název hostitele nebo parametry dotazu. Architektura vybere první trasu ve směrovací tabulce, která odpovídá identifikátoru URI.

Existují dva speciální zástupné symboly: {controller} a {action}.

  • {controller} poskytuje název kontroleru.
  • {action} obsahuje název akce. Ve webovém rozhraní API je obvyklou konvencí vynechat {action}.

Ve výchozím nastavení

Pokud zadáte výchozí hodnoty, trasa bude odpovídat identifikátoru URI, kterému tyto segmenty chybí. Příklad:

routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{category}",
    defaults: new { category = "all" }
);

Identifikátory http://localhost/api/products/all URI a http://localhost/api/products odpovídají předchozí trase. V druhém identifikátoru URI je chybějícímu {category} segmentu přiřazena výchozí hodnota all.

Slovník tras

Pokud rozhraní najde shodu pro identifikátor URI, vytvoří slovník, který obsahuje hodnotu pro každý zástupný symbol. Klávesy jsou zástupné názvy, bez složených závorek. Hodnoty jsou převzaty z cesty identifikátoru URI nebo z výchozích hodnot. Slovník je uložen v objektu IHttpRouteData .

Během této fáze porovnávání tras se se speciálními zástupnými symboly {controller} a {action} zachází stejně jako s ostatními zástupnými symboly. Jsou jednoduše uloženy ve slovníku s ostatními hodnotami.

Výchozí hodnota může mít speciální hodnotu RouteParameter.Optional. Pokud se této hodnotě přiřadí zástupný symbol, tato hodnota se do slovníku tras nepřidá. Příklad:

routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{category}/{id}",
    defaults: new { category = "all", id = RouteParameter.Optional }
);

Pro cestu identifikátoru URI api/products bude slovník tras obsahovat:

  • kontroler: "products"
  • category: "all"

Pro "api/products/toys/123" však bude slovník trasy obsahovat:

  • kontroler: "products"
  • kategorie: "hračky"
  • ID: "123"

Výchozí hodnoty můžou obsahovat také hodnotu, která se v šabloně trasy nezobrazuje. Pokud se trasa shoduje, je tato hodnota uložena ve slovníku. Příklad:

routes.MapHttpRoute(
    name: "Root",
    routeTemplate: "api/root/{id}",
    defaults: new { controller = "customers", id = RouteParameter.Optional }
);

Pokud je cesta identifikátoru URI "api/root/8", slovník bude obsahovat dvě hodnoty:

  • kontroler: "customers"
  • ID: "8"

Výběr kontroleru

Výběr kontroleru zpracovává metoda IHttpControllerSelector.SelectController . Tato metoda přebírá instanci HttpRequestMessage a vrací HttpControllerDescriptor. Výchozí implementaci poskytuje Třída DefaultHttpControllerSelector . Tato třída používá jednoduchý algoritmus:

  1. Ve slovníku tras vyhledejte klíč "controller".
  2. Vezměte hodnotu tohoto klíče a připojte řetězec Controller, abyste získali název typu kontroleru.
  3. Vyhledejte kontroler webového rozhraní API s tímto názvem typu.

Pokud například slovník trasy obsahuje dvojici klíč-hodnota "controller" = "products", pak typ kontroleru je "ProductsController". Pokud neexistuje žádný odpovídající typ nebo více shod, vrátí rozhraní klientovi chybu.

Pro krok 3 používá DefaultHttpControllerSelector rozhraní IHttpControllerTypeResolver k získání seznamu typů kontrolerů webového rozhraní API. Výchozí implementace IHttpControllerTypeResolver vrátí všechny veřejné třídy, které (a) implementují IHttpController, (b) nejsou abstraktní a (c) mají název, který končí na "Controller".

Výběr akce

Po výběru kontroleru rozhraní vybere akci voláním metody IHttpActionSelector.SelectAction . Tato metoda přebírá HttpControllerContext a vrací HttpActionDescriptor.

Výchozí implementace je poskytována ApiControllerActionSelector třída. Pokud chcete vybrat akci, podívejte se na následující:

  • Metoda HTTP požadavku.
  • Zástupný symbol {action} v šabloně trasy, pokud je k dispozici.
  • Parametry akcí na kontroleru.

Než se podíváme na algoritmus výběru, musíme porozumět některým věcem o akcích kontroleru.

Které metody na kontroleru se považují za "akce"? Při výběru akce se architektura dívá pouze na metody veřejné instance na kontroleru. Také vylučuje metody "special name" (konstruktory, události, přetížení operátoru atd.) a metody zděděné z třídy ApiController .

Metody HTTP. Architektura vybírá pouze akce, které odpovídají metodě HTTP požadavku, a to takto:

  1. Metodu HTTP můžete zadat pomocí atributu: AcceptVerbs, HttpDelete, HttpGet, HttpHead, HttpOptions, HttpPatch, HttpPost nebo HttpPut.
  2. Jinak pokud název metody kontroleru začíná na "Get", "Post", "Put", "Delete", "Head", "Options" nebo "Patch", pak akce podle konvence podporuje tuto metodu HTTP.
  3. Pokud žádná z výše uvedených možností, metoda podporuje POST.

Vazby parametrů. Vazba parametru je způsob, jakým webové rozhraní API vytvoří hodnotu parametru. Tady je výchozí pravidlo pro vazbu parametrů:

  • Jednoduché typy jsou převzaty z identifikátoru URI.
  • Komplexní typy jsou převzaty z textu požadavku.

Mezi jednoduché typy patří všechny primitivní typy rozhraní .NET Framework plus DateTime, Decimal, Guid, String a TimeSpan. Pro každou akci může text požadavku číst maximálně jeden parametr.

Poznámka

Výchozí pravidla vazeb je možné přepsat. Viz Vazba parametrů WebAPI pod pokličkou.

S tímto pozadím je zde algoritmus výběru akcí.

  1. Vytvořte seznam všech akcí na kontroleru, které odpovídají metodě požadavku HTTP.

  2. Pokud slovník trasy obsahuje položku "action", odeberte akce, jejichž název neodpovídá této hodnotě.

  3. Zkuste přiřadit parametry akce identifikátoru URI následujícím způsobem:

    1. Pro každou akci získejte seznam parametrů jednoduchého typu, kde vazba získá parametr z identifikátoru URI. Vylučte volitelné parametry.
    2. V tomto seznamu zkuste najít shodu pro každý název parametru, a to buď ve slovníku tras, nebo v řetězci dotazu URI. Shody nerozlišují malá a velká písmena a nezávisí na pořadí parametrů.
    3. Vyberte akci, ve které má každý parametr v seznamu shodu v identifikátoru URI.
    4. Pokud tato kritéria splňuje více akcí, vyberte akci s nejvíce odpovídajícími parametry.
  4. Ignorujte akce s atributem [NonAction].

Krok 3 je pravděpodobně nejvíce matoucí. Základní myšlenkou je, že parametr může získat svou hodnotu buď z identifikátoru URI, z textu požadavku, nebo z vlastní vazby. U parametrů, které pocházejí z identifikátoru URI, chceme zajistit, aby identifikátor URI skutečně obsahoval hodnotu daného parametru, a to buď v cestě (prostřednictvím slovníku trasy), nebo v řetězci dotazu.

Zvažte například následující akci:

public void Get(int id)

Parametr id se váže na identifikátor URI. Proto se tato akce může shodovat pouze s identifikátorem URI, který obsahuje hodnotu id, a to buď ve slovníku tras, nebo v řetězci dotazu.

Volitelné parametry jsou výjimkou, protože jsou volitelné. U volitelného parametru je to v pořádku, pokud vazba nemůže získat hodnotu z identifikátoru URI.

Složité typy jsou výjimkou z jiného důvodu. Komplexní typ může vytvořit vazbu na identifikátor URI pouze prostřednictvím vlastní vazby. V takovém případě ale architektura nemůže předem vědět, jestli by se parametr naváže na konkrétní identifikátor URI. Aby to bylo možné zjistit, bude potřeba vyvolat vazbu. Cílem algoritmu výběru je vybrat akci ze statického popisu před vyvoláním jakýchkoli vazeb. Proto jsou komplexní typy vyloučeny z odpovídajícího algoritmu.

Po výběru akce jsou vyvolány všechny vazby parametrů.

Souhrn:

  • Akce musí odpovídat metodě HTTP požadavku.
  • Název akce se musí shodovat s položkou "action" ve slovníku tras, pokud existuje.
  • Pokud je parametr převzatý z identifikátoru URI pro každý parametr akce, musí být název parametru nalezen buď ve slovníku tras, nebo v řetězci dotazu URI. (Volitelné parametry a parametry se složitými typy jsou vyloučené.)
  • Pokuste se spárovat s největším počtem parametrů. Nejlepší shodou může být metoda bez parametrů.

Rozšířený příklad

Trasy:

routes.MapHttpRoute(
    name: "ApiRoot",
    routeTemplate: "api/root/{id}",
    defaults: new { controller = "products", id = RouteParameter.Optional }
);
routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

Kontroler:

public class ProductsController : ApiController
{
    public IEnumerable<Product> GetAll() {}
    public Product GetById(int id, double version = 1.0) {}
    [HttpGet]
    public void FindProductsByName(string name) {}
    public void Post(Product value) {}
    public void Put(int id, Product value) {}
}

Požadavek HTTP:

GET http://localhost:34701/api/products/1?version=1.5&details=1

Párování tras

Identifikátor URI odpovídá trase s názvem DefaultApi. Slovník tras obsahuje následující položky:

  • controller: "products"
  • ID: "1"

Slovník tras neobsahuje parametry řetězce dotazu, "version" a "details", ale budou se stále zvažovat při výběru akce.

Výběr kontroleru

V položce "controller" ve slovníku tras je ProductsControllertyp kontroleru .

Výběr akce

Požadavek HTTP je požadavek GET. Akce kontroleru, které podporují get, jsou GetAll, GetByIda FindProductsByName. Slovník tras neobsahuje záznam pro "action", takže se nemusíme shodovat s názvem akce.

Dále se pokusíme spárovat názvy parametrů pro akce a podíváme se pouze na akce GET.

Akce Parametry, které se mají shodovat
GetAll žádné
GetById "id"
FindProductsByName "name"

Všimněte si, že parametr verze parametru GetById není brán v úvahu, protože se jedná o volitelný parametr.

Metoda se GetAll triviálně shoduje. Metoda také GetById odpovídá, protože slovník tras obsahuje "id". Metoda FindProductsByName se neshoduje.

Metoda GetById vyhrává, protože odpovídá jednomu parametru, oproti žádnému parametru pro GetAll. Metoda je vyvolána s následujícími hodnotami parametrů:

  • ID = 1
  • version = 1.5

Všimněte si, že i když se verze v algoritmu výběru nepoužila, hodnota parametru pochází z řetězce dotazu URI.

Rozšiřující body

Webové rozhraní API poskytuje body rozšíření pro některé části procesu směrování.

Rozhraní Description
IHttpControllerSelector Vybere kontroler.
IHttpControllerTypeResolver Získá seznam typů kontroleru. DefaultHttpControllerSelector zvolí typ kontroleru z tohoto seznamu.
IAssembliesResolver Získá seznam sestavení projektu. Rozhraní IHttpControllerTypeResolver používá tento seznam k vyhledání typů kontrolerů.
IHttpControllerActivator Vytvoří nové instance kontroleru.
IHttpActionSelector Vybere akci.
IHttpActionInvoker Vyvolá akci.

Pokud chcete poskytnout vlastní implementaci pro některé z těchto rozhraní, použijte kolekci Services u objektu HttpConfiguration :

var config = GlobalConfiguration.Configuration;
config.Services.Replace(typeof(IHttpControllerSelector), new MyControllerSelector(config));