ASP.NET Web API 2 Odata'da Yönlendirme Kuralları

Bu makalede, ASP.NET 4.x'teki Web API 2'nin OData uç noktaları için kullandığı yönlendirme kuralları açıklanmaktadır.

Web API'si bir OData isteği aldığında, isteği bir denetleyici adı ve bir eylem adıyla eşler. Eşleme, HTTP yöntemini ve URI'yi temel alır. Örneğin, GET /odata/Products(1)ProductsController.GetProduct'e karşılık gelir.

Bu makalenin 1. bölümünde, yerleşik OData yönlendirme kurallarını açıkliyorum. Bu kurallar, OData uç noktaları için özel olarak tasarlanmıştır ve varsayılan Web API'sinin yönlendirme sisteminin yerini alır. ( MapODataRoute'u çağırdığınızda değiştirme gerçekleşir.)

2. bölümde özel yönlendirme kurallarının nasıl ekleneceğini gösteriyorum. Şu anda yerleşik kurallar tüm OData URI'lerini kapsamaz, ancak bunları ek durumları işleyecek şekilde genişletebilirsiniz.

Dahili Yönlendirme Kuralları

Web API'sindeki OData yönlendirme kurallarını açıklamadan önce OData URI'lerini anlamak yararlı olur. OData URI'leri şunlardan oluşur:

  • Hizmet köken noktası
  • Kaynak yolu
  • Sorgu seçenekleri

Hizmet kökünü, kaynak yolunu ve sorgu seçeneklerini soldan sağa görüntüleyerek O Veri yönlendirme kurallarının nasıl göründüğünü gösteren ekran görüntüsü.

Yönlendirmede önemli olan kaynak yoludur. Kaynak yolu segmentlere ayrılır. Örneğin, /Products(1)/Supplier üç segment vardır:

  • Products "Products" adlı bir varlık kümesine atıfta bulunur.
  • 1 bir varlık anahtarıdır ve kümeden tek bir varlık seçer.
  • Supplier , ilgili bir varlığı seçen bir gezinti özelliğidir.

Bu nedenle bu yol, 1. ürünün sağlayıcısını seçer.

Uyarı

OData yol kesimleri her zaman URI segmentlerine karşılık gelmez. Örneğin, "1" bir yol kesimi olarak kabul edilir.

Denetleyici Adları. Denetleyici adı her zaman kaynak yolunun kökündeki varlık kümesinden türetilir. Örneğin, kaynak yolu /Products(1)/Supplier ise, Web API ProductsController bir denetleyici arar.

Eylem Adları. Eylem adları, aşağıdaki tablolarda listelendiği gibi yol kesimlerinden ve varlık veri modelinden (EDM) türetilir. Bazı durumlarda eylem adı için iki seçeneğiniz vardır. Örneğin, "Get" veya "GetProducts".

Varlıkları Sorgulama

İstek Örnek URI Eylem Adı Örnek Eylem
GET /entityset /Ürünler GetEntitySet veya Get GetProducts
GET /entityset(kilit) /Ürünler(1) GetEntityType veya Get ÜrünAl
GET /entityset(key)/cast /Products(1)/Models.Book GetEntityType veya Get Kitap Al

Daha fazla bilgi için bkz. Read-Only OData Uç Noktası oluşturma.

Varlık Oluşturma, Güncelleştirme ve Silme

İstek Örnek URI Eylem Adı Örnek Eylem
POST /entityset /Ürünler PostEntityType veya Post PostProduct
PUT /entityset(key) /Ürünler(1) PutEntityType veya Put PutProduct
PUT /entityset(key)/cast /Products(1)/Models.Book PutEntityType veya Put PutBook
PATCH /entityset(key) /Ürünler(1) PatchEntityType veya Patch PatchProduct
PATCH /entityset(key)/cast /Products(1)/Models.Book PatchEntityType veya Patch PatchBook
DELETE /entityset(key) /Ürünler(1) DeleteEntityType veya Delete Ürünü Sil
DELETE /entityset(key)/cast /Products(1)/Models.Book DeleteEntityType veya Delete Kitap Sil

Gezinti Özelliğini Sorgulama

İstek Örnek URI Eylem Adı Örnek Eylem
GET /entityset(key)/navigation /Products(1)/Tedarikçi GetNavigationFromEntityType veya GetNavigation GetSupplierFromProduct
GET /entityset(key)/cast/navigation /Products(1)/Models.Book/Author GetNavigationFromEntityType veya GetNavigation GetAuthorFromBook

Daha fazla bilgi için bkz. Varlık İlişkileriyle Çalışma.

Bağlantı Oluşturma ve Silme

İstek Örnek URI Eylem Adı
POST /entityset(key)/$links/navigation /Products(1)/$links/Supplier Bağlantı Oluştur
PUT /entityset(key)/$links/navigation /Products(1)/$links/Supplier Bağlantı Oluştur
DELETE /entityset(key)/$links/navigation /Products(1)/$links/Supplier Bağlantıyı Sil
DELETE /entityset(key)/$links/navigation(relatedKey) /Products/(1)/$links/Suppliers(1) Bağlantıyı Sil

Daha fazla bilgi için bkz. Varlık İlişkileriyle Çalışma.

Özellikleri

Web API 2 gerektirir

İstek Örnek URI Eylem Adı Örnek Eylem
GET /entityset(key)/property /Ürünler(1)/Name GetPropertyFromEntityType veya GetProperty GetNameFromProduct
GET /entityset(key)/cast/property /Products(1)/Models.Book/Author GetPropertyFromEntityType veya GetProperty KitaptanBaşlıkAl

Eylemler

İstek Örnek URI Eylem Adı Örnek Eylem
POST /entityset(key)/action /Products(1)/Değerlendir ActionNameOnEntityType veya ActionName RateOnProduct
POST /entityset(key)/cast/action /Products(1)/Models.Book/CheckOut ActionNameOnEntityType veya ActionName CheckOutOnBook

Daha fazla bilgi için bkz. OData Actions.

Yöntem İmzaları

Yöntem imzaları için bazı kurallar şunlardır:

  • Yol bir anahtar içeriyorsa, eylemin anahtar adlı bir parametresi olmalıdır.
  • Yol bir gezinti özelliğine anahtar içeriyorsa, eylemin relatedKey adlı bir parametresi olmalıdır.
  • anahtar ve relatedKey parametrelerini [FromODataUri] parametresiyle işaretleyin.
  • POST ve PUT istekleri varlık türünün bir parametresini alır.
  • PATCH istekleri Delta<T> türünde bir parametre alır ve burada T , varlık türüdür.

Referans olarak, her bir yerleşik OData yönlendirme kuralı için yöntem imzalarını gösteren bir örnek aşağıda verilmiştir.

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)
}

Özel Yönlendirme Kuralları

Şu anda yerleşik kurallar tüm olası OData URI'lerini kapsamaz. IODataRoutingConvention arabirimini uygulayarak yeni kurallar ekleyebilirsiniz. Bu arabirimin iki yöntemi vardır:

string SelectController(ODataPath odataPath, HttpRequestMessage request);
string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, 
    ILookup<string, HttpActionDescriptor> actionMap);
  • SelectController denetleyicinin adını döndürür.
  • SelectAction eylemin adını döndürür.

Her iki yöntem için de kural bu istek için geçerli değilse yöntemi null döndürmelidir.

ODataPath parametresi ayrıştırılmış OData kaynak yolunu temsil eder. Kaynak yolunun her kesimi için bir tane olmak üzere ODataPathSegment örneklerinin listesini içerir. ODataPathSegment soyut bir sınıftır; her segment türü, ODataPathSegment'ten türetilen bir sınıfla temsil edilir.

ODataPath.TemplatePath özelliği, yol kesimlerinin tümünü birleştirmeyi temsil eden bir dizedir. Örneğin, URI "/Products(1)/Supplier" ise, yol şablonu "~/entityset/key/navigation" şeklindedir. Dikkat edin ki segmentler doğrudan URI segmentlerine uyuşmaz. Örneğin, varlık anahtarı (1) kendi ODataPathSegment olarak temsil edilir.

Genellikle, IODataRoutingConvention uygulaması aşağıdakileri yapar:

  1. Bu kuralın geçerli istek için geçerli olup olmadığını görmek için yol şablonunu karşılaştırın. Geçerli değilse null döndür.
  2. Kural geçerliyse, denetleyici ve eylem adlarını türetmek için ODataPathSegment örneklerinin özelliklerini kullanın.
  3. Eylemler için, yol sözlüğüne eylem parametrelerine (genellikle varlık anahtarları) bağlanması gereken tüm değerleri ekleyin.

Şimdi belirli bir örneğe bakalım. Yerleşik yönlendirme kuralları, bir gezinti koleksiyonunda dizin oluşturmayı desteklemez. Başka bir deyişle, URI'ler için aşağıdaki gibi bir kural yoktur:

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

Bu sorgu türünü işlemek için özel bir yönlendirme kuralı aşağıdadır.

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;
        }
    }
}

Notlar:

  1. Bu sınıftaki SelectController yöntemi bu yeni yönlendirme kuralı için uygun olduğundan EntitySetRoutingConvention'dan türetiyorum. Bu, SelectController'ı yeniden uygulamama gerek olmadığı anlamına gelir.
  2. Kural yalnızca GET istekleri için ve yalnızca yol şablonu "~/entityset/key/navigation/key" olduğunda geçerlidir.
  3. Eylem adı "Get{EntityType}", burada {EntityType} gezinti koleksiyonunun türüdür. Örneğin, "GetSupplier". İstediğiniz adlandırma kuralını kullanabilirsiniz; yalnızca denetleyici eylemlerinizin eşleştiğinden emin olun.
  4. Eylem , key ve relatedKey adlı iki parametre alır. (Önceden tanımlanmış bazı parametre adlarının listesi için bkz . ODataRouteConstants.)

Sonraki adım, yeni kuralı yönlendirme kuralları listesine eklemektir. Bu, aşağıdaki kodda gösterildiği gibi yapılandırma sırasında gerçekleşir:

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);

        }
    }
}

Aşağıda, çalışmak için yararlı olabilecek bazı örnek yönlendirme kuralları verilmiştir:

Web API'sinin kendisi de açık kaynak olduğundan, yerleşik yönlendirme kurallarının kaynak kodunu görebilirsiniz. Bunlar System.Web.Http.OData.Routing.Conventions ad alanında tanımlanır.