Panduan Protokol OData

1. Perkenalan

Dalam tutorial ini, kita akan menjelajahi OData, protokol standar yang memungkinkan akses mudah ke kumpulan data menggunakan RESTFul API.

2. Apa Itu OData ?

OData adalah Standar OASIS dan ISO / IEC untuk mengakses data menggunakan RESTful API. Dengan demikian, ini memungkinkan konsumen untuk menemukan dan menavigasi melalui kumpulan data menggunakan panggilan HTTP standar.

Misalnya, kita dapat mengakses salah satu layanan OData yang tersedia untuk umum dengan curl one-liner :

curl -s //services.odata.org/V2/Northwind/Northwind.svc/Regions   Regions //services.odata.org/V2/Northwind/Northwind.svc/Regions ... rest of xml response omitted

Pada tulisan ini, protokol OData ada di versi ke-4 - 4.01 lebih tepatnya. OData V4 mencapai level standar OASIS pada tahun 2014, tetapi memiliki sejarah yang lebih panjang. Kami dapat menelusuri akarnya ke proyek Microsoft bernama Astoria, yang diubah namanya menjadi ADO.Net Data Services pada tahun 2007. Entri blog asli yang mengumumkan proyek ini masih tersedia di blog OData Microsoft.

Memiliki protokol berbasis standar untuk mengakses kumpulan data membawa beberapa manfaat dibandingkan API standar seperti JDBC atau ODBC. Sebagai konsumen tingkat pengguna akhir, kami dapat menggunakan alat populer seperti Excel untuk mengambil data dari penyedia yang kompatibel. Pemrograman juga difasilitasi oleh sejumlah besar pustaka klien REST yang tersedia.

Sebagai penyedia, mengadopsi OData juga memiliki keuntungan: setelah kami membuat layanan yang kompatibel, kami dapat fokus pada penyediaan kumpulan data yang berharga, yang dapat dikonsumsi oleh pengguna akhir menggunakan alat pilihan mereka. Karena ini adalah protokol berbasis HTTP, kami juga dapat memanfaatkan aspek seperti mekanisme keamanan, pemantauan, dan pencatatan.

Karakteristik tersebut membuat OData menjadi pilihan yang populer oleh instansi pemerintah saat mengimplementasikan layanan data publik, seperti yang dapat kita periksa dengan melihat direktori ini.

3. Konsep OData

Inti dari protokol OData adalah konsep Model Data Entitas - atau disingkat EDM. EDM menjelaskan data yang diekspos oleh penyedia OData melalui dokumen metadata yang berisi sejumlah meta-entitas:

  • Jenis entitas dan propertinya (mis. Orang , Pelanggan , Pesanan , dll) dan kuncinya
  • Hubungan antar entitas
  • Tipe kompleks yang digunakan untuk mendeskripsikan tipe terstruktur yang disematkan ke entitas (katakanlah, tipe alamat yang merupakan bagian dari tipe Pelanggan )
  • Kumpulan Entitas, yang menggabungkan entitas dari jenis tertentu

Spesifikasi mengamanatkan bahwa dokumen metadata ini harus tersedia di lokasi standar $ metadata di URL root yang digunakan untuk mengakses layanan. Misalnya, jika kita memiliki layanan OData yang tersedia di //example.org/odata.svc/ , maka dokumen metadatanya akan tersedia di //example.org/odata.svc/$metadata .

Dokumen yang dikembalikan berisi sekumpulan XML yang menjelaskan skema yang didukung oleh server ini:

   ... schema elements omitted  

Mari kita hancurkan dokumen ini menjadi bagian utamanya.

Elemen tingkat atas, hanya dapat memiliki satu anak, yaitu elemen .Hal penting yang perlu diperhatikan di sini adalah URI namespace karena memungkinkan kita untuk mengidentifikasi versi OData mana yang digunakan server. Dalam kasus ini, namespace menunjukkan bahwa kami memiliki server OData V2, yang menggunakan pengenal Microsoft.

Sebuah DataServices elemen dapat memiliki satu atau lebih skema elemen, masing-masing menggambarkan sebuah dataset yang tersedia. Karena deskripsi lengkap tentang elemen yang tersedia dalam Skema berada di luar cakupan artikel ini, kami akan fokus pada yang paling penting: EntityTypes, Associations, dan EntitySets .

3.1. Elemen EntityType

Elemen ini mendefinisikan properti yang tersedia dari entitas tertentu, termasuk kunci utamanya. Ini mungkin juga berisi informasi tentang hubungan dengan jenis skema lain dan, dengan melihat contoh - CarMaker - kita akan dapat melihat bahwa itu tidak jauh berbeda dari deskripsi yang ditemukan dalam teknologi ORM lainnya, seperti JPA:

Di sini, Pembuat Mobil kami hanya memiliki dua properti - Id dan Nama - dan asosiasi ke EntityType lain . The Key s ub-elemen mendefinisikan kunci primer entitas menjadi nya Id properti, dan masing-masing Properti elemen berisi data tentang properti entitas seperti namanya, jenis atau nullability.

Sebuah NavigationProperty adalah jenis khusus dari properti yang menggambarkan sebuah “titik akses” ke entitas yang terkait.

3.2. Elemen Asosiasi

Sebuah Asosiasi elemen menggambarkan hubungan antara dua entitas, yang mencakup banyaknya pada setiap akhir dan opsional kendala integritas referensial:

Di sini, Asosiasi elemen mendefinisikan hubungan satu-ke-banyak antara CarModel dan produsen mobil entitas, di mana mantan bertindak sebagai pihak yang tergantung.

3.3. Elemen EntitySet

Konsep skema terakhir yang akan kita jelajahi adalah elemen EntitySet , yang merepresentasikan kumpulan entitas dari tipe tertentu. Meskipun mudah untuk menganggapnya sebagai analogi tabel - dan dalam banyak kasus, mereka hanya itu - analogi yang lebih baik adalah pandangan. Alasannya adalah kita dapat memiliki beberapa elemen EntitySet untuk EntityType yang sama , masing-masing mewakili subset berbeda dari data yang tersedia.

The EntityContainer elemen, yang merupakan skema elemen tingkat atas, kelompok semua tersedia EntitySet s:

Dalam contoh sederhana kami, kami hanya memiliki dua EntitySet , tetapi kami juga dapat menambahkan tampilan tambahan, seperti ForeignCarMakers atau HistoricCarMakers .

4. URL dan Metode OData

Untuk mengakses data yang diekspos oleh layanan OData, kami menggunakan kata kerja HTTP biasa:

  • GET mengembalikan satu atau lebih entitas
  • POST menambahkan entitas baru ke Kumpulan Entitas yang sudah ada
  • PUT menggantikan entitas tertentu
  • PATCH menggantikan properti tertentu dari entitas tertentu
  • DELETE menghapus entitas tertentu

Semua operasi tersebut membutuhkan jalur sumber daya untuk ditindaklanjuti. Jalur sumber daya dapat menentukan kumpulan entitas, entitas atau bahkan properti dalam suatu entitas.

Mari kita lihat contoh URL yang digunakan untuk mengakses layanan OData kami sebelumnya:

//example.org/odata/CarMakers 

Bagian pertama dari URL ini, dimulai dengan protokol hingga segmen odata / jalur, dikenal sebagai URL root layanan dan sama untuk semua jalur sumber daya layanan ini. Karena root layanan selalu sama, kami akan menggantinya dalam contoh URL berikut dengan elipsis (“…”) .

CarMakers , dalam hal ini, merujuk ke salah satu EntitySets yang dinyatakan dalam metadata layanan. Kita dapat menggunakan browser biasa untuk mengakses URL ini, yang kemudian akan mengembalikan dokumen yang berisi semua entitas yang ada dari jenis ini:

  //localhost:8080/odata/CarMakers CarMakers 2019-04-06T17:51:33.588-03:00      //localhost:8080/odata/CarMakers(1L) CarMakers 2019-04-06T17:51:33.589-03:00      1 Special Motors    ... other entries omitted 

Dokumen yang dikembalikan berisi elemen entri untuk setiap instance CarMaker .

Mari kita lihat lebih dekat informasi apa yang kami miliki untuk kami:

  • id : tautan ke entitas khusus ini
  • judul / penulis / diperbarui : metadata tentang entri ini
  • elemen tautan : Tautan yang digunakan untuk menunjuk ke sumber daya yang digunakan untuk mengedit entitas ( rel = ”edit” ) atau ke entitas terkait. Dalam hal ini, kami memiliki tautan yang membawa kami ke kumpulan entitas CarModel yang terkait dengan Pembuat Mobil ini .
  • konten : nilai properti entitas CarModel

An important point to notice here is the use of the key-value pair to identify a particular entity within an entity set. In our example, the key is numeric so a resource path like CarMaker(1L) refers to the entity with a primary key value equal to 1 – the “L” here just denotes a long value and could be omitted.

5. Query Options

We can pass query options to a resource URL in order to modify a number of aspects of the returned data, such as to limit the size of the returned set or its ordering. The OData spec defines a rich set of options, but here we'll focus on the most common ones.

As a general rule, query options can be combined with each other, thus allowing clients to easily implement common functionalities such as paging, filtering and ordering result lists.

5.1. $top and $skip

We can navigate through a large dataset using the $top an $skip query options:

.../CarMakers?$top=10&$skip=10 

$top tells the service that we want only the first 10 records of the CarMakers entity set. A $skip, which is applied before the $top, tells the server to skip the first 10 records.

It's usually useful to know the size of a given Entity Set and, for this purpose, we can use the $count sub-resource:

.../CarMakers/$count 

This resource produces a text/plain document containing the size of the corresponding set. Here, we must pay attention to the specific OData version supported by a provider. While OData V2 supports $count as a sub-resource from a collection, V4 allows it to be used as a query parameter. In this case, $count is a Boolean, so we need to change the URL accordingly:

.../CarMakers?$count=true 

5.2. $filter

We use the $filter query option to limit the returned entities from a given Entity Set to those matching given criteria. The value for the $filter is a logical expression that supports basic operators, grouping and a number of useful functions. For instance, let's build a query that returns all CarMaker instances where its Name attribute starts with the letter ‘B':

.../CarMakers?$filter=startswith(Name,'B') 

Now, let's combine a few logical operators to search for CarModels of a particular Year and Maker:

.../CarModels?$filter=Year eq 2008 and CarMakerDetails/Name eq 'BWM' 

Here, we've used the equality operator eq to specify values for the properties. We can also see how to use properties from a related entity in the expression.

5.3. $expand

By default, an OData query does not return data for related entities, which is usually OK. We can use the $expand query option to request that data from a given related entity be included inline with the main content.

Using our sample domain, let's build an URL that returns data from a given model and its maker, thus avoiding an additional round-trip to the server:

.../CarModels(1L)?$expand=CarMakerDetails 

The returned document now includes the CarMaker data as part of the related entity:

  //example.org/odata/CarModels(1L) CarModels 2019-04-07T11:33:38.467-03:00      //example.org/odata/CarMakers(1L) CarMakers 2019-04-07T11:33:38.492-03:00      1 Special Motors        1 1 Muze SM001 2018   

5.4. $select

We use the $select query option to inform the OData service that it should only return the values for the given properties. This is useful in scenarios where our entities have a large number of properties, but we're only interested in some of them.

Let's use this option in a query that returns only the Name and Sku properties:

.../CarModels(1L)?$select=Name,Sku 

The resulting document now has only the requested properties:

... xml omitted   Muze SM001   ... xml omitted

We can also see that even related entities were omitted. In order to include them, we'd need to include the name of the relation in the $select option.

5.5. $orderBy

The $orderBy option works pretty much as its SQL counterpart. We use it to specify the order in which we want the server to return a given set of entities. In its simpler form, its value is just a list of property names from the selected entity, optionally informing the order direction:

.../CarModels?$orderBy=Name asc,Sku desc 

This query will result in a list of CarModels ordered by their names and SKUs, in ascending and descending directions, respectively.

An important detail here is the case used with the direction part of a given property: while the spec mandates that server must support any combination of upper- and lower-case letters for the keywords asc and desc, it also mandates that client use only lowercase.

5.6. $format

This option defines the data representation format that the server should use, which takes precedence over any HTTP content-negotiation header, such as Accept. Its value must be a full MIME-Type or a format-specific short form.

For instance, we can use json as an abbreviation for application/json:

.../CarModels?$format=json 

This URL instructs our service to return data using JSON format, instead of XML, as we've seen before. When this option is not present, the server will use the value of the Accept header, if present. When neither is available, the server is free to choose any representation – usually XML or JSON.

Mengenai JSON secara khusus, itu pada dasarnya tanpa skema. Namun, OData 4.01 juga mendefinisikan skema JSON untuk titik akhir metadata. Ini berarti bahwa sekarang kita dapat menulis klien yang dapat sepenuhnya menyingkirkan pemrosesan XML jika mereka memilih untuk melakukannya.

6. Kesimpulan

Dalam pengantar singkat tentang OData ini, kita telah membahas semantik dasarnya dan cara melakukan navigasi kumpulan data sederhana. Artikel tindak lanjut kami akan melanjutkan dari tempat kami pergi dan langsung masuk ke perpustakaan Olingo. Kami kemudian akan melihat cara mengimplementasikan layanan sampel menggunakan pustaka ini.

Contoh kode, seperti biasa, tersedia di GitHub.