Konvence směrování ve službě ASP.NET Web API 2 Odata

Tento článek popisuje konvence směrování, které webové rozhraní API 2 v ASP.NET 4.x používá pro koncové body OData.

Když webové rozhraní API obdrží požadavek OData, namapuje požadavek na název kontroleru a název akce. Mapování je založené na metodě HTTP a identifikátoru URI. Například mapuje GET /odata/Products(1) na ProductsController.GetProduct.

V 1. části tohoto článku popíšu integrované konvence směrování OData. Tyto konvence jsou navržené speciálně pro koncové body OData a nahrazují výchozí systém směrování webového rozhraní API. (K nahrazení dojde při volání MapODataRoute.)

V části 2 vám ukážu, jak přidat vlastní konvence směrování. Předdefinované konvence v současné době nepokrývají celý rozsah identifikátorů URI OData, ale můžete je rozšířit o další případy.

Integrované konvence směrování

Než popíšeme konvence směrování OData ve webovém rozhraní API, je užitečné pochopit identifikátory URI OData. Identifikátor URI OData se skládá z těchto součástí:

  • Kořenový adresář služby
  • Cesta k prostředku
  • Možnosti proxy

Snímek obrazovky znázorňující konvence směrování O Data, který zobrazuje kořen služby, cestu k prostředku a možnosti dotazu zleva doprava

Důležitou součástí směrování je cesta k prostředku. Cesta k prostředku je rozdělená na segmenty. Například /Products(1)/Supplier má tři segmenty:

  • Products odkazuje na sadu entit s názvem "Produkty".
  • 1 je klíč entity, který ze sady vybírá jednu entitu.
  • Supplier je navigační vlastnost, která vybere související entitu.

Tato cesta tedy vybere dodavatele produktu 1.

Poznámka

Segmenty cest OData nemusí vždy odpovídat segmentům identifikátoru URI. Například "1" se považuje za segment cesty.

Názvy kontroleru. Název kontroleru je vždy odvozený od entity nastavené v kořenovém adresáři cesty k prostředku. Pokud je /Products(1)/Suppliernapříklad cesta k prostředku , webové rozhraní API vyhledá kontroler s názvem ProductsController.

Názvy akcí. Názvy akcí jsou odvozené ze segmentů cesty a modelu EDM (Entity Data Model), jak je uvedeno v následujících tabulkách. V některých případech máte pro název akce dvě možnosti. Například "Get" nebo "GetProducts".

Dotazování entit

Žádost Příklad identifikátoru URI Název akce Příklad akce
GET /entityset /Produkty GetEntitySet nebo Get GetProducts
GET /entityset(klíč) /Produkty(1) GetEntityType nebo Get GetProduct
GET /entityset(klíč)/cast /Products(1)/Models.Book GetEntityType nebo Get GetBook

Další informace najdete v tématu Vytvoření koncového bodu Read-Only OData.

Vytváření, aktualizace a odstraňování entit

Žádost Příklad identifikátoru URI Název akce Příklad akce
POST /entityset /Produkty PostEntityType nebo Post Produktová náčiní
PUT /entityset(klíč) /Produkty(1) PutEntityType nebo Put PutProduct
PUT /entityset(klíč)/cast /Products(1)/Models.Book PutEntityType nebo Put PutBook
PATCH /entityset(klíč) /Produkty(1) PatchEntityType nebo Patch PatchProduct
PATCH /entityset(klíč)/cast /Products(1)/Models.Book PatchEntityType nebo Patch PatchBook
DELETE /entityset(klíč) /Produkty(1) DeleteEntityType nebo Delete DeleteProduct
DELETE /entityset(klíč)/cast /Products(1)/Models.Book DeleteEntityType nebo Delete Odstranitbook

Dotazování navigační vlastnosti

Žádost Příklad identifikátoru URI Název akce Příklad akce
GET /entityset(klíč)/navigation /Products(1)/Supplier GetNavigationFromEntityType nebo GetNavigation GetSupplierFromProduct
GET /entityset(klíč)/cast/navigation /Products(1)/Models.Book/Author GetNavigationFromEntityType nebo GetNavigation GetAuthorFromBook

Další informace najdete v tématu Práce s relacemi entit.

Vytváření a odstraňování odkazů

Žádost Příklad identifikátoru URI Název akce
POST /entityset(key)/$links/navigation /Products(1)/$links/Supplier Vytvořit odkaz
PUT /entityset(key)/$links/navigation /Products(1)/$links/Supplier Vytvořit odkaz
DELETE /entityset(key)/$links/navigation /Products(1)/$links/Supplier Odstranit odkaz
DELETE /entityset(key)/$links/navigation(relatedKey) /Products/(1)/$links/Suppliers(1) Odstranit odkaz

Další informace najdete v tématu Práce s relacemi entit.

Vlastnosti

Vyžaduje webové rozhraní API 2.

Žádost Příklad identifikátoru URI Název akce Příklad akce
GET /entityset(klíč)/property /Products(1)/Name GetPropertyFromEntityType nebo GetProperty GetNameFromProduct
GET /entityset(key)/cast/property /Products(1)/Models.Book/Author GetPropertyFromEntityType nebo GetProperty GetTitleFromBook

Akce

Žádost Příklad identifikátoru URI Název akce Příklad akce
POST /entityset(key)/action /Products(1)/Rate ActionNameOnEntityType nebo ActionName RateOnProduct
POST /entityset(key)/cast/action /Products(1)/Models.Book/CheckOut ActionNameOnEntityType nebo ActionName CheckOutOnBook

Další informace najdete v tématu Akce OData.

Signatury metod

Tady jsou některá pravidla pro podpisy metod:

  • Pokud cesta obsahuje klíč, měla by mít akce parametr s názvem key.
  • Pokud cesta obsahuje klíč do navigační vlastnosti, měla by mít akce parametr relatedKey.
  • Parametry klíče a relatedKey můžete zdobit parametrem [FromODataUri].
  • Požadavky POST a PUT přebírají parametr typu entity.
  • Požadavky PATCH přebírají parametr typu Delta<T>, kde T je typ entity.

Tady je příklad, který ukazuje podpisy metod pro každou integrovanou konvenci směrování OData.

public class ProductsController : ODataController
{
    // GET /odata/Products
    public IQueryable<Product> Get()

    // GET /odata/Products(1)
    public Product Get([FromODataUri] int key)

    // GET /odata/Products(1)/ODataRouting.Models.Book
    public Book GetBook([FromODataUri] int key)

    // POST /odata/Products 
    public HttpResponseMessage Post(Product item)

    // PUT /odata/Products(1)
    public HttpResponseMessage Put([FromODataUri] int key, Product item)

    // PATCH /odata/Products(1)
    public HttpResponseMessage Patch([FromODataUri] int key, Delta<Product> item)

    // DELETE /odata/Products(1)
    public HttpResponseMessage Delete([FromODataUri] int key)

    // PUT /odata/Products(1)/ODataRouting.Models.Book
    public HttpResponseMessage PutBook([FromODataUri] int key, Book item)

    // PATCH /odata/Products(1)/ODataRouting.Models.Book
    public HttpResponseMessage PatchBook([FromODataUri] int key, Delta<Book> item)

    // DELETE /odata/Products(1)/ODataRouting.Models.Book
    public HttpResponseMessage DeleteBook([FromODataUri] int key)

    //  GET /odata/Products(1)/Supplier
    public Supplier GetSupplierFromProduct([FromODataUri] int key)

    // GET /odata/Products(1)/ODataRouting.Models.Book/Author
    public Author GetAuthorFromBook([FromODataUri] int key)

    // POST /odata/Products(1)/$links/Supplier
    public HttpResponseMessage CreateLink([FromODataUri] int key, 
        string navigationProperty, [FromBody] Uri link)

    // DELETE /odata/Products(1)/$links/Supplier
    public HttpResponseMessage DeleteLink([FromODataUri] int key, 
        string navigationProperty, [FromBody] Uri link)

    // DELETE /odata/Products(1)/$links/Parts(1)
    public HttpResponseMessage DeleteLink([FromODataUri] int key, string relatedKey, string navigationProperty)

    // GET odata/Products(1)/Name
    // GET odata/Products(1)/Name/$value
    public HttpResponseMessage GetNameFromProduct([FromODataUri] int key)

    // GET /odata/Products(1)/ODataRouting.Models.Book/Title
    // GET /odata/Products(1)/ODataRouting.Models.Book/Title/$value
    public HttpResponseMessage GetTitleFromBook([FromODataUri] int key)
}

Vlastní konvence směrování

Předdefinované konvence v současné době nepokrývají všechny možné identifikátory URI OData. Nové konvence můžete přidat implementací rozhraní IODataRoutingConvention . Toto rozhraní má dvě metody:

string SelectController(ODataPath odataPath, HttpRequestMessage request);
string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, 
    ILookup<string, HttpActionDescriptor> actionMap);
  • SelectController vrátí název kontroleru.
  • SelectAction vrátí název akce.

Pokud se pro obě metody konvence nevztahuje na tento požadavek, měla by metoda vrátit hodnotu null.

Parametr ODataPath představuje analyzovanou cestu k prostředku OData. Obsahuje seznam instancí ODataPathSegment , jeden pro každý segment cesty k prostředku. ODataPathSegment je abstraktní třída; každý typ segmentu je reprezentován třídou, která je odvozena z ODataPathSegment.

Vlastnost ODataPath.TemplatePath je řetězec, který představuje zřetězení všech segmentů cesty. Pokud je /Products(1)/Suppliernapříklad identifikátor URI , šablona cesty je ~/entityset/key/navigation. Všimněte si, že segmenty přímo neodpovídají segmentům identifikátoru URI. Například klíč entity (1) je reprezentován jako vlastní ODataPathSegment.

Implementace IODataRoutingConvention obvykle provede následující:

  1. Porovnejte šablonu cesty a zjistěte, jestli se tato konvence vztahuje na aktuální požadavek. Pokud se nepoužije, vrátí hodnotu null.
  2. Pokud platí konvence, použijte vlastnosti instancí ODataPathSegment k odvození názvů kontrolerů a akcí.
  3. U akcí přidejte do slovníku tras všechny hodnoty, které by se měly svázat s parametry akce (obvykle klíče entit).

Podívejme se na konkrétní příklad. Integrované konvence směrování nepodporují indexování do navigační kolekce. Jinými slovy, neexistuje žádná konvence pro identifikátory URI, jako je následující:

/odata/Products(1)/Suppliers(1)

Tady je vlastní konvence směrování pro zpracování tohoto typu dotazu.

using Microsoft.Data.Edm;
using System.Linq;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.OData.Routing;
using System.Web.Http.OData.Routing.Conventions;

namespace ODataRouting
{
    public class NavigationIndexRoutingConvention : EntitySetRoutingConvention
    {
        public override string SelectAction(ODataPath odataPath, HttpControllerContext context, 
            ILookup<string, HttpActionDescriptor> actionMap)
        {
            if (context.Request.Method == HttpMethod.Get && 
                odataPath.PathTemplate == "~/entityset/key/navigation/key")
            {
                NavigationPathSegment navigationSegment = odataPath.Segments[2] as NavigationPathSegment;
                IEdmNavigationProperty navigationProperty = navigationSegment.NavigationProperty.Partner;
                IEdmEntityType declaringType = navigationProperty.DeclaringType as IEdmEntityType;

                string actionName = "Get" + declaringType.Name;
                if (actionMap.Contains(actionName))
                {
                    // Add keys to route data, so they will bind to action parameters.
                    KeyValuePathSegment keyValueSegment = odataPath.Segments[1] as KeyValuePathSegment;
                    context.RouteData.Values[ODataRouteConstants.Key] = keyValueSegment.Value;

                    KeyValuePathSegment relatedKeySegment = odataPath.Segments[3] as KeyValuePathSegment;
                    context.RouteData.Values[ODataRouteConstants.RelatedKey] = relatedKeySegment.Value;

                    return actionName;
                }
            }
            // Not a match.
            return null;
        }
    }
}

Poznámky:

  1. Odvozeno z EntitySetRoutingConvention, protože SelectController metoda v této třídě je vhodná pro tuto novou konvenci směrování. To znamená, že nemusím znovu implementovat SelectController.
  2. Konvence se vztahuje pouze na požadavky GET a pouze v případě, že je šablona cesty "~/entityset/key/navigation/key".
  3. Název akce je Get{EntityType}, kde {EntityType} je typ navigační kolekce. Například GetSupplier. Můžete použít libovolnou konvenci vytváření názvů, které se vám líbí – stačí se ujistit, že akce kontroleru odpovídají.
  4. Akce přijímá dva parametry s názvem key a relatedKey. (Seznam některých předdefinovaných názvů parametrů najdete v tématu ODataRouteConstants.)

Dalším krokem je přidání nové konvence do seznamu konvencí směrování. K tomu dochází během konfigurace, jak je znázorněno v následujícím kódu:

using ODataRouting.Models;
using System.Web.Http;
using System.Web.Http.OData.Builder;
using System.Web.Http.OData.Routing;
using System.Web.Http.OData.Routing.Conventions;

namespace ODataRouting
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
            // Create EDM (not shown).

            // Create the default collection of built-in conventions.
            var conventions = ODataRoutingConventions.CreateDefault();
            // Insert the custom convention at the start of the collection.
            conventions.Insert(0, new NavigationIndexRoutingConvention());

            config.Routes.MapODataRoute(routeName: "ODataRoute",
                routePrefix: "odata",
                model: modelBuilder.GetEdmModel(),
                pathHandler: new DefaultODataPathHandler(),
                routingConventions: conventions);

        }
    }
}

Tady jsou některé další ukázkové konvence směrování, které je užitečné studovat:

A samotné webové rozhraní API je samozřejmě opensourcové, takže můžete vidět zdrojový kód pro integrované konvence směrování. Jsou definovány v oboru názvů System.Web.Http.OData.Routing.Conventions .