Hilangkan Redundansi di RAML dengan Jenis dan Sifat Sumber Daya

Artikel ini adalah bagian dari serial: • Pengantar RAML - Bahasa Pemodelan API RESTful

• Hilangkan Redundansi dalam RAML dengan Jenis dan Sifat Sumber Daya (artikel saat ini) • Modular RAML Menggunakan Includes, Library, Overlay, dan Ekstensi

• Tentukan Properti RAML Kustom Menggunakan Anotasi

1. Ikhtisar

Dalam artikel tutorial RAML kami, kami memperkenalkan RESTful API Modeling Language dan membuat definisi API sederhana berdasarkan satu entitas yang disebut Foo . Sekarang bayangkan API dunia nyata di mana Anda memiliki beberapa sumber daya tipe entitas, semuanya memiliki operasi GET, POST, PUT, dan DELETE yang sama atau serupa. Anda dapat melihat bagaimana dokumentasi API Anda dapat dengan cepat menjadi membosankan dan berulang.

Dalam artikel ini, kami menunjukkan bagaimana penggunaan fitur tipe dan sifat sumber daya di RAML dapat menghilangkan redundansi dalam definisi sumber daya dan metode dengan mengekstrak dan membuat parameter bagian umum, sehingga menghilangkan kesalahan salin dan tempel sambil membuat definisi API Anda lebih ringkas.

2. API kami

Untuk mendemonstrasikan manfaat tipe sumber daya dan sifat , kami akan memperluas API asli kami dengan menambahkan sumber daya untuk tipe entitas kedua yang disebut Bar . Berikut adalah sumber daya yang akan membentuk API kami yang direvisi:

  • GET / api / v1 / foos
  • POST / api / v1 / foos
  • DAPATKAN / api / v1 / foos / {fooId}
  • PUT / api / v1 / foos / {fooId}
  • HAPUS / api / v1 / foos / {fooId}
  • DAPATKAN / api / v1 / foos / nama / {nama}
  • GET / api / v1 / foos? Name = {name} & ownerName = {ownerName}
  • GET / api / v1 / bar
  • POST / api / v1 / bar
  • DAPATKAN / api / v1 / bar / {barId}
  • PUT / api / v1 / bar / {barId}
  • HAPUS / api / v1 / bar / {barId}
  • DAPATKAN / api / v1 / bar / fooId / {fooId}

3. Mengenali Pola

Saat kami membaca daftar sumber daya di API kami, kami mulai melihat beberapa pola muncul. Misalnya, ada pola untuk URI dan metode yang digunakan untuk membuat, membaca, memperbarui, dan menghapus entitas tunggal, dan ada pola untuk URI dan metode yang digunakan untuk mengambil kumpulan entitas. Pola collection dan collection-item adalah salah satu pola yang lebih umum digunakan untuk mengekstrak jenis resource dalam definisi RAML.

Mari kita lihat beberapa bagian dari API kami:

[Catatan: Dalam potongan kode di bawah ini, garis yang hanya berisi tiga titik (…) menunjukkan bahwa beberapa baris dilewati agar lebih singkat.]

/foos: get: description: | List all foos matching query criteria, if provided; otherwise list all foos queryParameters: name?: string ownerName?: string responses: 200: body: application/json: type: Foo[] post: description: Create a new foo body: application/json: type: Foo responses: 201: body: application/json: type: Foo ... /bars: get: description: | List all bars matching query criteria, if provided; otherwise list all bars queryParameters: name?: string ownerName?: string responses: 200: body: application/json: type: Bar[] post: description: Create a new bar body: application/json: type: Bar responses: 201: body: application/json: type: Bar

Ketika kita membandingkan definisi RAML dari sumber daya / foos dan / bar , termasuk metode HTTP yang digunakan, kita dapat melihat beberapa redundansi di antara berbagai properti masing-masing, dan kita kembali melihat pola mulai muncul.

Di mana pun ada pola baik dalam definisi sumber daya atau metode, ada peluang untuk menggunakan jenis atau sifat sumber daya RAML .

4. Jenis Sumber Daya

Untuk mengimplementasikan pola yang ditemukan di API, jenis sumber daya menggunakan parameter yang dicadangkan dan ditentukan pengguna yang dikelilingi oleh tanda kurung sudut ganda (<>).

4.1 Parameter yang Dicadangkan

Dua parameter yang dicadangkan dapat digunakan dalam definisi jenis sumber daya:

  • <> mewakili seluruh URI (mengikuti baseURI ), dan
  • <> mewakili bagian URI setelah garis miring paling kanan (/), mengabaikan tanda kurung apa pun {}.

Saat diproses di dalam definisi sumber daya, nilainya dihitung berdasarkan sumber daya yang ditentukan.

Dengan mempertimbangkan resource / foos , misalnya, <> akan dievaluasi menjadi "/ foos" dan <> akan dievaluasi menjadi "foos".

Dengan mempertimbangkan sumber daya / foos / {fooId} , <> akan dievaluasi menjadi “/ foos / {fooId}” dan <> akan dievaluasi menjadi “foos”.

4.2 Parameter yang Ditentukan Pengguna

Sebuah tipe sumber daya definisi juga mungkin berisi parameter yang ditetapkan pengguna. Tidak seperti parameter yang dicadangkan, yang nilainya ditentukan secara dinamis berdasarkan sumber daya yang ditentukan, parameter yang ditentukan pengguna harus diberi nilai di mana pun jenis sumber daya yang memuatnya digunakan, dan nilai tersebut tidak berubah.

Parameter yang ditentukan pengguna dapat dideklarasikan di awal definisi tipe sumber daya , meskipun melakukan hal itu tidak diperlukan dan bukan praktik umum, karena pembaca biasanya dapat mengetahui penggunaan yang dimaksudkan berdasarkan nama dan konteks penggunaannya.

4.3 Fungsi Parameter

Sejumlah fungsi teks yang berguna tersedia untuk digunakan di mana pun parameter digunakan untuk mengubah nilai parameter yang diperluas saat diproses dalam definisi sumber daya.

Berikut adalah fungsi yang tersedia untuk transformasi parameter:

  • ! singularisasi
  • ! menjamakkan
  • ! huruf besar
  • ! huruf kecil
  • ! huruf besar
  • ! huruf kecil
  • ! upperunderscorecase
  • ! lowerunderscorecase
  • ! upperhyphencase
  • ! lowerhyphencase

Fungsi diterapkan ke parameter menggunakan konstruksi berikut:

<< parameterName | ! functionName >>

Jika Anda perlu menggunakan lebih dari satu fungsi untuk mencapai transformasi yang diinginkan, Anda harus memisahkan setiap nama fungsi dengan simbol pipa ("|") dan menambahkan tanda seru (!) Sebelum setiap fungsi digunakan.

Misalnya, dalam resource / foos , di mana << resourcePathName >> terevaluasi menjadi "foos":

  • << resourcePathName | ! singularisasi >> ==> “foo”
  • << resourcePathName | ! huruf besar >> ==> “FOOS”
  • << resourcePathName | ! singularisasi | ! huruf besar >> ==> “FOO”

Dan berdasarkan resource / bars / {barId} , di mana << resourcePathName >> terevaluasi menjadi “bar”:

  • << resourcePathName | ! huruf besar >> ==> “BARS”
  • << resourcePathName | ! uppercamelcase >> ==> “Bar”

5. Mengekstrak Jenis Sumber Daya untuk Koleksi

Mari kita refactor definisi resource / foos dan / bar yang ditampilkan di atas, menggunakan jenis resource untuk menangkap properti umum. Kami akan menggunakan parameter yang dipesan <> , dan parameter yang ditentukan pengguna <> untuk mewakili tipe data yang digunakan.

5.1 Definisi

Berikut adalah definisi tipe sumber daya yang mewakili kumpulan item:

resourceTypes: collection: usage: Use this resourceType to represent any collection of items description: A collection of <> get: description: Get all <>, optionally filtered responses: 200: body: application/json: type: <>[] post: description: Create a new <> responses: 201: body: application/json: type: <>

Perhatikan bahwa di API kami, karena tipe data kami hanya menggunakan huruf besar, versi tunggal dari nama sumber daya dasar kami, kami dapat menerapkan fungsi ke parameter << resourcePathName >>, alih-alih memperkenalkan parameter << typeName >> yang ditentukan pengguna , untuk mencapai hasil yang sama untuk bagian API ini:

resourceTypes: collection: ... get: ... type: <>[] post: ... type: <>

5.2 Penerapan

Menggunakan definisi di atas yang menggabungkan parameter << typeName >>, berikut adalah bagaimana Anda akan menerapkan tipe sumber daya "collection" ke resources / foos dan / bar :

/foos: type: { collection: { "typeName": "Foo" } } get: queryParameters: name?: string ownerName?: string ... /bars: type: { collection: { "typeName": "Bar" } }

Perhatikan bahwa kita masih dapat menggabungkan perbedaan antara kedua sumber daya - dalam hal ini, bagian queryParameters - sambil tetap memanfaatkan semua yang ditawarkan definisi jenis sumber daya .

6. Mengekstrak Jenis Sumber Daya untuk Item Tunggal Koleksi

Mari kita fokus sekarang pada porsi API kita yang menangani item tunggal dari sebuah koleksi: resource / foos / {fooId} dan / bar / {barId} . Ini kode untuk / foos / {fooId} :

/foos: ... /{fooId}: get: description: Get a Foo responses: 200: body: application/json: type: Foo 404: body: application/json: type: Error example: !include examples/Error.json put: description: Update a Foo body: application/json: type: Foo responses: 200: body: application/json: type: Foo 404: body: application/json: type: Error example: !include examples/Error.json delete: description: Delete a Foo responses: 204: 404: body: application/json: type: Error example: !include examples/Error.json

The / bar / {Barid} definisi sumber daya juga memiliki GET, PUT, dan metode DELETE dan identik dengan / foos / {fooId} definisi, selain kejadian dari string “foo” dan “bar” (dan masing-pluralized mereka dan / atau huruf besar).

6.1 Definisi

Mengekstrak pola yang baru saja kita identifikasi, berikut adalah cara kita mendefinisikan tipe sumber daya untuk satu item koleksi:

resourceTypes: ... item: usage: Use this resourceType to represent any single item description: A single <> get: description: Get a <> responses: 200: body: application/json: type: <> 404: body: application/json: type: Error example: !include examples/Error.json put: description: Update a <> body: application/json: type: <> responses: 200: body: application/json: type: <> 404: body: application/json: type: Error example: !include examples/Error.json delete: description: Delete a <> responses: 204: 404: body: application/json: type: Error example: !include examples/Error.json

6.2 Penerapan

Dan berikut adalah cara kami menerapkan jenis sumber daya "item" :

/foos: ... /{fooId}: type: { item: { "typeName": "Foo" } }
... /bars: ... /{barId}: type: { item: { "typeName": "Bar" } }

7. Sifat

Sedangkan tipe sumber daya digunakan untuk mengekstrak pola dari definisi sumber daya, sifat digunakan untuk mengekstrak pola dari definisi metode yang umum di seluruh sumber daya.

7.1 Parameter

Bersama dengan << resourcePath >> dan << resourcePathName >>, satu parameter cadangan tambahan tersedia untuk digunakan dalam definisi sifat: << methodName >> mengevaluasi ke metode HTTP (GET, POST, PUT, DELETE, dll) yang sifat didefinisikan. Parameter yang ditentukan pengguna juga dapat muncul dalam definisi sifat, dan jika diterapkan, mengambil nilai sumber daya tempat parameter tersebut diterapkan.

7.2 Definisi

Perhatikan bahwa jenis sumber daya "item" masih penuh dengan redundansi. Mari kita lihat bagaimana sifat dapat membantu menghilangkannya. Kami akan mulai dengan mengekstraksi sifat untuk metode apa pun yang berisi badan permintaan:

traits: hasRequestItem: body: application/json: type: <>

Sekarang mari kita ekstrak ciri untuk metode yang tanggapan normalnya mengandung tubuh:

 hasResponseItem: responses: 200: body: application/json: type: <> hasResponseCollection: responses: 200: body: application/json: type: <>[]

Terakhir, inilah ciri untuk metode apa pun yang dapat mengembalikan respons kesalahan 404:

 hasNotFound: responses: 404: body: application/json: type: Error example: !include examples/Error.json

7.3 Penerapan

Kami kemudian menerapkan sifat ini ke jenis sumber daya kami :

resourceTypes: collection: usage: Use this resourceType to represent any collection of items description: A collection of <> get: description: | Get all <>, optionally filtered is: [ hasResponseCollection: { typeName: <> } ] post: description: Create a new <> is: [ hasRequestItem: { typeName: <> } ] item: usage: Use this resourceType to represent any single item description: A single <> get: description: Get a <> is: [ hasResponseItem: { typeName: <> }, hasNotFound ] put: description: Update a <> is: | [ hasRequestItem: { typeName: <> }, hasResponseItem: { typeName: <> }, hasNotFound ] delete: description: Delete a <> is: [ hasNotFound ] responses: 204:

Kita juga dapat menerapkan sifat pada metode yang ditentukan dalam sumber daya. Ini sangat berguna terutama untuk skenario "satu kali" di mana kombinasi metode sumber daya cocok dengan satu atau beberapa sifat tetapi tidak cocok dengan jenis sumber daya yang ditentukan :

/foos: ... /name/{name}: get: description: List all foos with a certain name is: [ hasResponseCollection: { typeName: Foo } ]

8. Kesimpulan

Dalam tutorial ini, kami telah menunjukkan cara mengurangi secara signifikan atau, dalam beberapa kasus, menghilangkan redundansi dari definisi API RAML.

First, we identified the redundant sections of our resources, recognized their patterns, and extracted resource types. Then we did the same for the methods that were common across resources to extract traits. Then we were able to eliminate further redundancies by applying traits to our resource types and to “one-off” resource-method combinations that did not strictly match one of our defined resource types.

As a result, our simple API with resources for only two entities, was reduced from 177 to just over 100 lines of code. To learn more about RAML resource types and traits, visit the RAML.org 1.0 spec.

The implementasi penuh dari tutorial ini dapat ditemukan dalam proyek github.

Berikut adalah API RAML terakhir kami secara keseluruhan:

#%RAML 1.0 title: Baeldung Foo REST Services API version: v1 protocols: [ HTTPS ] baseUri: //rest-api.baeldung.com/api/{version} mediaType: application/json securedBy: basicAuth securitySchemes: basicAuth: description: | Each request must contain the headers necessary for basic authentication type: Basic Authentication describedBy: headers: Authorization: description: | Used to send the Base64 encoded "username:password" credentials type: string responses: 401: description: | Unauthorized. Either the provided username and password combination is invalid, or the user is not allowed to access the content provided by the requested URL. types: Foo: !include types/Foo.raml Bar: !include types/Bar.raml Error: !include types/Error.raml resourceTypes: collection: usage: Use this resourceType to represent a collection of items description: A collection of <> get: description: | Get all <>, optionally filtered is: [ hasResponseCollection: { typeName: <> } ] post: description: | Create a new <> is: [ hasRequestItem: { typeName: <> } ] item: usage: Use this resourceType to represent any single item description: A single <> get: description: Get a <> is: [ hasResponseItem: { typeName: <> }, hasNotFound ] put: description: Update a <> is: [ hasRequestItem: { typeName: <> }, hasResponseItem: { typeName: <> }, hasNotFound ] delete: description: Delete a <> is: [ hasNotFound ] responses: 204: traits: hasRequestItem: body: application/json: type: <> hasResponseItem: responses: 200: body: application/json: type: <> hasResponseCollection: responses: 200: body: application/json: type: <>[] hasNotFound: responses: 404: body: application/json: type: Error example: !include examples/Error.json /foos: type: { collection: { typeName: Foo } } get: queryParameters: name?: string ownerName?: string /{fooId}: type: { item: { typeName: Foo } } /name/{name}: get: description: List all foos with a certain name is: [ hasResponseCollection: { typeName: Foo } ] /bars: type: { collection: { typeName: Bar } } /{barId}: type: { item: { typeName: Bar } } /fooId/{fooId}: get: description: Get all bars for the matching fooId is: [ hasResponseCollection: { typeName: Bar } ]
Berikutnya » RAML Modular Menggunakan Includes, Library, Overlay, dan Ekstensi « Sebelumnya Pengantar RAML - Bahasa Pemodelan RESTful API