Pengantar HATEOAS Musim Semi

REST Top

Saya baru saja mengumumkan kursus Learn Spring baru , yang berfokus pada dasar-dasar Spring 5 dan Spring Boot 2:

>> LIHAT KURSUSnya

1. Ikhtisar

Artikel ini menjelaskan proses pembuatan layanan web REST berbasis hypermedia menggunakan proyek Spring HATEOAS.

2. Musim Semi-HATEOAS

Proyek Spring HATEOAS adalah pustaka API yang dapat kita gunakan untuk membuat representasi REST dengan mudah yang mengikuti prinsip HATEOAS (Hypertext sebagai Engine of Application State).

Secara umum, prinsip tersebut menyiratkan bahwa API harus memandu klien melalui aplikasi dengan mengembalikan informasi yang relevan tentang langkah potensial berikutnya, bersama dengan setiap respons.

Pada artikel ini, kita akan membuat contoh menggunakan Spring HATEOAS dengan tujuan memisahkan klien dan server, dan secara teoritis memungkinkan API untuk mengubah skema URI tanpa merusak klien.

3. Persiapan

Pertama, mari tambahkan dependensi Spring HATEOAS:

 org.springframework.boot spring-boot-starter-hateoas 2.1.4.RELEASE 

Jika kami tidak menggunakan Spring Boot, kami dapat menambahkan pustaka berikut ke proyek kami:

 org.springframework.hateoas spring-hateoas 0.25.1.RELEASE   org.springframework.plugin spring-plugin-core 1.2.0.RELEASE 

Seperti biasa, kami dapat mencari versi terbaru dari HATEOAS starter, dependensi spring-hateoas dan spring-plugin-core di Maven Central.

Selanjutnya, kami memiliki sumber daya Pelanggan tanpa dukungan Spring HATEOAS:

public class Customer { private String customerId; private String customerName; private String companyName; // standard getters and setters } 

Dan kami memiliki kelas pengontrol tanpa dukungan Spring HATEOAS:

@RestController @RequestMapping(value = "/customers") public class CustomerController { @Autowired private CustomerService customerService; @GetMapping("/{customerId}") public Customer getCustomerById(@PathVariable String customerId) { return customerService.getCustomerDetail(customerId); } } 

Akhirnya, representasi sumber daya Pelanggan :

{ "customerId": "10A", "customerName": "Jane", "customerCompany": "ABC Company" } 

4. Menambahkan Dukungan HATEOAS

Dalam proyek Spring HATEOAS, kita tidak perlu mencari konteks Servlet atau menggabungkan variabel jalur ke URI dasar.

Sebaliknya, Spring HATEOAS menawarkan tiga abstraksi untuk membuat URI - RepresentationModel, Link, dan WebMvcLinkBuilder . Kita bisa menggunakan ini untuk membuat metadata dan mengaitkannya dengan representasi sumber daya.

4.1. Menambahkan Dukungan Hypermedia ke Resource

Proyek menyediakan kelas dasar yang disebut RepresentationModel untuk diwarisi dari saat membuat representasi sumber daya:

public class Customer extends RepresentationModel { private String customerId; private String customerName; private String companyName; // standard getters and setters } 

The Pelanggan sumber daya memanjang dari RepresentationModel kelas untuk mewarisi add () metode . Jadi, begitu kita membuat tautan, kita dapat dengan mudah menetapkan nilai itu ke representasi sumber daya tanpa menambahkan bidang baru ke dalamnya.

4.2. Membuat Tautan

Spring HATEOAS menyediakan objek Link untuk menyimpan metadata (lokasi atau URI resource).

Pertama, kami akan membuat tautan sederhana secara manual:

Link link = new Link("//localhost:8080/spring-security-rest/api/customers/10A"); 

The link objek mengikuti Atom sintaks tautan dan terdiri dari rel yang mengidentifikasi kaitannya dengan sumber daya dan href atribut yang merupakan link yang sebenarnya itu sendiri.

Berikut tampilan sumber daya Pelanggan sekarang yang berisi tautan baru:

{ "customerId": "10A", "customerName": "Jane", "customerCompany": "ABC Company", "_links":{ "self":{ "href":"//localhost:8080/spring-security-rest/api/customers/10A" } } } 

URI yang terkait dengan respons dikualifikasikan sebagai tautan mandiri . Semantik relasi diri jelas - ini hanyalah lokasi kanonik tempat sumber daya dapat diakses.

4.3. Membuat Tautan yang Lebih Baik

Abstraksi yang sangat penting lain yang ditawarkan oleh perpustakaan adalah yang WebMvcLinkBuilder - yang menyederhanakan membangun URI dengan menghindari keras-kode link.

Cuplikan berikut menunjukkan membangun tautan mandiri pelanggan menggunakan kelas WebMvcLinkBuilder :

linkTo(CustomerController.class).slash(customer.getCustomerId()).withSelfRel(); 

Mari kita lihat:

  • metode linkTo () memeriksa kelas pengontrol dan mendapatkan pemetaan akarnya
  • metode slash () menambahkan nilai customerId sebagai variabel jalur dari link
  • terakhir, withSelfMethod () mengkualifikasi relasi sebagai tautan-mandiri

5. Hubungan

Di bagian sebelumnya, kami telah menunjukkan hubungan referensi mandiri. Namun, sistem yang lebih kompleks mungkin melibatkan relasi lain juga.

Misalnya, pelanggan dapat memiliki hubungan dengan pesanan. Mari kita modelkan kelas Order sebagai sumber daya juga:

public class Order extends RepresentationModel { private String orderId; private double price; private int quantity; // standard getters and setters } 

Pada titik ini, kita dapat memperluas CustomerController dengan metode yang mengembalikan semua pesanan pelanggan tertentu:

@GetMapping(value = "/{customerId}/orders", produces = { "application/hal+json" }) public CollectionModel getOrdersForCustomer(@PathVariable final String customerId) { List orders = orderService.getAllOrdersForCustomer(customerId); for (final Order order : orders) { Link selfLink = linkTo(methodOn(CustomerController.class) .getOrderById(customerId, order.getOrderId())).withSelfRel(); order.add(selfLink); } Link link = linkTo(methodOn(CustomerController.class) .getOrdersForCustomer(customerId)).withSelfRel(); CollectionModel result = new CollectionModel(orders, link); return result; } 

Our method returns a CollectionModel object to comply with the HAL return type, as well as a “_self” link for each of the orders and the full list.

An important thing to notice here is that the hyperlink for the customer orders depends on the mapping of getOrdersForCustomer() method. We'll refer to these types of links as method links and show how the WebMvcLinkBuilder can assist in their creation.

6. Links to Controller Methods

The WebMvcLinkBuilder offers rich support for Spring MVC Controllers. The following example shows how to build HATEOAS hyperlinks based on the getOrdersForCustomer() method of the CustomerController class:

Link ordersLink = linkTo(methodOn(CustomerController.class) .getOrdersForCustomer(customerId)).withRel("allOrders"); 

The methodOn() obtains the method mapping by making dummy invocation of the target method on the proxy controller and sets the customerId as the path variable of the URI.

7. Spring HATEOAS in Action

Let's put the self-link and method link creation all together in a getAllCustomers() method:

@GetMapping(produces = { "application/hal+json" }) public CollectionModel getAllCustomers() { List allCustomers = customerService.allCustomers(); for (Customer customer : allCustomers) { String customerId = customer.getCustomerId(); Link selfLink = linkTo(CustomerController.class).slash(customerId).withSelfRel(); customer.add(selfLink); if (orderService.getAllOrdersForCustomer(customerId).size() > 0) { Link ordersLink = linkTo(methodOn(CustomerController.class) .getOrdersForCustomer(customerId)).withRel("allOrders"); customer.add(ordersLink); } } Link link = linkTo(CustomerController.class).withSelfRel(); CollectionModel result = new CollectionModel(allCustomers, link); return result; }

Next, let's invoke the getAllCustomers() method:

curl //localhost:8080/spring-security-rest/api/customers 

And examine the result:

{ "_embedded": { "customerList": [{ "customerId": "10A", "customerName": "Jane", "companyName": "ABC Company", "_links": { "self": { "href": "//localhost:8080/spring-security-rest/api/customers/10A" }, "allOrders": { "href": "//localhost:8080/spring-security-rest/api/customers/10A/orders" } } },{ "customerId": "20B", "customerName": "Bob", "companyName": "XYZ Company", "_links": { "self": { "href": "//localhost:8080/spring-security-rest/api/customers/20B" }, "allOrders": { "href": "//localhost:8080/spring-security-rest/api/customers/20B/orders" } } },{ "customerId": "30C", "customerName": "Tim", "companyName": "CKV Company", "_links": { "self": { "href": "//localhost:8080/spring-security-rest/api/customers/30C" } } }] }, "_links": { "self": { "href": "//localhost:8080/spring-security-rest/api/customers" } } }

Within each resource representation, there is a self link and the allOrders link to extract all orders of a customer. If a customer doesn't have orders, then the link for orders won't appear.

Contoh ini mendemonstrasikan bagaimana Spring HATEOAS mendorong kemampuan API untuk ditemukan di layanan web lainnya. Jika tautan tersebut ada, klien dapat mengikutinya dan mendapatkan semua pesanan untuk pelanggan:

curl //localhost:8080/spring-security-rest/api/customers/10A/orders 
{ "_embedded": { "orderList": [{ "orderId": "001A", "price": 150, "quantity": 25, "_links": { "self": { "href": "//localhost:8080/spring-security-rest/api/customers/10A/001A" } } },{ "orderId": "002A", "price": 250, "quantity": 15, "_links": { "self": { "href": "//localhost:8080/spring-security-rest/api/customers/10A/002A" } } }] }, "_links": { "self": { "href": "//localhost:8080/spring-security-rest/api/customers/10A/orders" } } }

8. Kesimpulan

Dalam tutorial ini, kita telah membahas cara membangun layanan web Spring REST berbasis hypermedia menggunakan proyek Spring HATEOAS .

Dalam contoh, kita melihat bahwa klien dapat memiliki satu titik masuk ke aplikasi dan tindakan selanjutnya dapat diambil berdasarkan metadata dalam representasi respons.

Ini memungkinkan server untuk mengubah skema URI-nya tanpa merusak klien. Selain itu, aplikasi dapat mengiklankan kemampuan baru dengan meletakkan tautan atau URI baru dalam representasi.

Terakhir, implementasi lengkap artikel ini dapat ditemukan di proyek GitHub.

REST bawah

Saya baru saja mengumumkan kursus Learn Spring baru , yang berfokus pada dasar-dasar Spring 5 dan Spring Boot 2:

>> LIHAT KURSUSnya