Bagikan melalui


Konvensi Perutean di ASP.NET Web API 2 Odata

Artikel ini menjelaskan konvensi perutean yang digunakan Web API 2 di ASP.NET 4.x untuk titik akhir OData.

Ketika Web API mendapatkan permintaan OData, api memetakan permintaan ke nama pengontrol dan nama tindakan. Pemetaan didasarkan pada metode HTTP dan URI. Misalnya, GET /odata/Products(1) memetakan ke ProductsController.GetProduct.

Di bagian 1 artikel ini, saya menjelaskan konvensi perutean OData bawaan. Konvensi ini dirancang khusus untuk titik akhir OData, dan menggantikan sistem perutean API Web default. (Penggantian terjadi saat Anda memanggil MapODataRoute.)

Di bagian 2, saya menunjukkan cara menambahkan konvensi perutean kustom. Saat ini konvensi bawaan tidak mencakup seluruh rentang URI OData, tetapi Anda dapat memperluasnya untuk menangani kasus tambahan.

Konvensi Perutean Bawaan

Sebelum saya menjelaskan konvensi perutean OData di Api Web, sangat berguna untuk memahami URI OData. URI OData terdiri dari:

  • Akar layanan
  • Jalur sumber daya
  • Opsi kueri

Cuplikan layar untuk memperlihatkan seperti apa konvensi perutean Data O, menampilkan akar layanan, jalur sumber daya, dan opsi kueri dari kiri ke kanan.

Untuk perutean, bagian pentingnya adalah jalur sumber daya. Jalur sumber daya dibagi menjadi beberapa segmen. Misalnya, /Products(1)/Supplier memiliki tiga segmen:

  • Products mengacu pada set entitas bernama "Produk".
  • 1 adalah kunci entitas, memilih satu entitas dari set.
  • Supplier adalah properti navigasi yang memilih entitas terkait.

Jadi jalur ini memilih pemasok produk 1.

Catatan

Segmen jalur OData tidak selalu sesuai dengan segmen URI. Misalnya, "1" dianggap sebagai segmen jalur.

Nama Pengontrol. Nama pengontrol selalu berasal dari entitas yang ditetapkan di akar jalur sumber daya. Misalnya, jika jalur sumber daya adalah /Products(1)/Supplier, API Web mencari pengontrol bernama ProductsController.

Nama Tindakan. Nama tindakan berasal dari segmen jalur ditambah model data entitas (EDM), seperti yang tercantum dalam tabel berikut. Dalam beberapa kasus, Anda memiliki dua pilihan untuk nama tindakan. Misalnya, "Dapatkan" atau "GetProducts".

Mengkueri Entitas

Minta Contoh URI Nama Tindakan Contoh Tindakan
GET /entityset /Produk GetEntitySet atau Get GetProducts
GET /entityset(key) /Products(1) GetEntityType atau Get GetProduct
GET /entityset(key)/cast /Products(1)/Models.Book GetEntityType atau Get GetBook

Untuk informasi selengkapnya, lihat Membuat titik akhir OData Read-Only.

Membuat, Memperbarui, dan Menghapus Entitas

Minta Contoh URI Nama Tindakan Contoh Tindakan
POST /entityset /Produk PostEntityType atau Post PostProduct
PUT /entityset(key) /Products(1) PutEntityType atau Put PutProduct
PUT /entityset(key)/cast /Products(1)/Models.Book PutEntityType atau Put PutBook
PATCH /entityset(key) /Products(1) PatchEntityType atau Patch PatchProduct
PATCH /entityset(key)/cast /Products(1)/Models.Book PatchEntityType atau Patch PatchBook
DELETE /entityset(key) /Products(1) DeleteEntityType atau Delete DeleteProduct
DELETE /entityset(key)/cast /Products(1)/Models.Book DeleteEntityType atau Delete DeleteBook

Mengkueri Properti Navigasi

Minta Contoh URI Nama Tindakan Contoh Tindakan
GET /entityset(key)/navigation /Products(1)/Pemasok GetNavigationFromEntityType atau GetNavigation GetSupplierFromProduct
GET /entityset(key)/cast/navigation /Products(1)/Models.Book/Author GetNavigationFromEntityType atau GetNavigation GetAuthorFromBook

Untuk informasi selengkapnya, lihat Bekerja dengan Hubungan Entitas.

Membuat dan Menghapus Tautan

Minta Contoh URI Nama Tindakan
POST /entityset(key)/$links/navigation /Products(1)/$links/Pemasok CreateLink
PUT /entityset(key)/$links/navigation /Products(1)/$links/Pemasok CreateLink
DELETE /entityset(key)/$links/navigation /Products(1)/$links/Pemasok DeleteLink
DELETE /entityset(key)/$links/navigation(relatedKey) /Products/(1)/$links/Pemasok(1) DeleteLink

Untuk informasi selengkapnya, lihat Bekerja dengan Hubungan Entitas.

Properti

Memerlukan Web API 2

Minta Contoh URI Nama Tindakan Contoh Tindakan
GET /entityset(key)/property /Products(1)/Name GetPropertyFromEntityType atau GetProperty GetNameFromProduct
GET /entityset(key)/cast/property /Products(1)/Models.Book/Author GetPropertyFromEntityType atau GetProperty GetTitleFromBook

Tindakan

Minta Contoh URI Nama Tindakan Contoh Tindakan
POST /entityset(key)/action /Products(1)/Rate ActionNameOnEntityType atau ActionName RateOnProduct
POST /entityset(key)/cast/action /Products(1)/Models.Book/CheckOut ActionNameOnEntityType atau ActionName CheckOutOnBook

Untuk informasi selengkapnya, lihat Tindakan OData.

Tanda Tangan Metode

Berikut adalah beberapa aturan untuk tanda tangan metode:

  • Jika jalur berisi kunci, tindakan harus memiliki parameter bernama kunci.
  • Jika jalur berisi kunci ke dalam properti navigasi, tindakan harus memiliki parameter bernama relatedKey.
  • Hiasi parameter key dan relatedKey dengan parameter [FromODataUri] .
  • Permintaan POST dan PUT mengambil parameter jenis entitas.
  • Permintaan PATCH mengambil parameter jenis Delta<T>, di mana T adalah jenis entitas.

Sebagai referensi, berikut adalah contoh yang menunjukkan tanda tangan metode untuk setiap konvensi perutean OData bawaan.

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

Konvensi Perutean Kustom

Saat ini konvensi bawaan tidak mencakup semua kemungkinan URI OData. Anda dapat menambahkan konvensi baru dengan menerapkan antarmuka IODataRoutingConvention . Antarmuka ini memiliki dua metode:

string SelectController(ODataPath odataPath, HttpRequestMessage request);
string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, 
    ILookup<string, HttpActionDescriptor> actionMap);
  • PilihKontroler mengembalikan nama pengontrol.
  • SelectAction mengembalikan nama tindakan.

Untuk kedua metode, jika konvensi tidak berlaku untuk permintaan tersebut, metode harus mengembalikan null.

Parameter ODataPath mewakili jalur sumber daya OData yang diurai. Ini berisi daftar instans ODataPathSegment , satu untuk setiap segmen jalur sumber daya. ODataPathSegment adalah kelas abstrak; setiap jenis segmen diwakili oleh kelas yang berasal dari ODataPathSegment.

Properti ODataPath.TemplatePath adalah string yang mewakili perangkaian semua segmen jalur. Misalnya, jika URI adalah /Products(1)/Supplier, templat jalurnya adalah "~/entityset/key/navigation". Perhatikan bahwa segmen tidak sesuai langsung dengan segmen URI. Misalnya, kunci entitas (1) direpresentasikan sebagai ODataPathSegment sendiri.

Biasanya, implementasi IODataRoutingConvention melakukan hal berikut:

  1. Bandingkan templat jalur untuk melihat apakah konvensi ini berlaku untuk permintaan saat ini. Jika tidak berlaku, kembalikan null.
  2. Jika konvensi berlaku, gunakan properti instans ODataPathSegment untuk mendapatkan pengontrol dan nama tindakan.
  3. Untuk tindakan, tambahkan nilai apa pun ke kamus rute yang harus mengikat parameter tindakan (biasanya kunci entitas).

Mari kita lihat contoh tertentu. Konvensi perutean bawaan tidak mendukung pengindeksan ke dalam koleksi navigasi. Dengan kata lain, tidak ada konvensi untuk URI seperti berikut:

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

Berikut adalah konvensi perutean kustom untuk menangani jenis kueri ini.

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

Catatan:

  1. Saya berasal dari EntitySetRoutingConvention, karena metode SelectController di kelas tersebut sesuai untuk konvensi perutean baru ini. Itu berarti saya tidak perlu mengimplementasikan ulang SelectController.
  2. Konvensi ini hanya berlaku untuk permintaan GET, dan hanya ketika templat jalur adalah "~/entityset/key/navigation/key".
  3. Nama tindakan adalah "Get{EntityType}", di mana {EntityType} adalah tipe koleksi navigasi. Misalnya, "GetSupplier". Anda dapat menggunakan konvensi penamaan apa pun yang Anda suka — pastikan tindakan pengontrol Anda cocok.
  4. Tindakan ini mengambil dua parameter bernama key dan relatedKey. (Untuk daftar beberapa nama parameter yang telah ditentukan sebelumnya, lihat ODataRouteConstants.)

Langkah selanjutnya adalah menambahkan konvensi baru ke daftar konvensi perutean. Ini terjadi selama konfigurasi, seperti yang ditunjukkan dalam kode berikut:

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

        }
    }
}

Berikut adalah beberapa contoh konvensi perutean lainnya yang berguna untuk ditelaah:

Dan tentu saja Web API itu sendiri adalah sumber terbuka, sehingga Anda dapat melihat kode sumber untuk konvensi perutean bawaan. Ini didefinisikan dalam namespace System.Web.Http.OData.Routing.Conventions .