Spring HATEOASの紹介

Spring HATEOASの紹介

1. 概要

この記事では、Spring HATEOASプロジェクトを使用してハイパーメディア駆動型REST Webサービスを作成するプロセスについて説明します。

2. 春-HATEOAS

Spring HATEOASプロジェクトは、HATEOAS(アプリケーション状態のエンジンとしてのハイパーテキスト)の原則に従うREST表現を簡単に作成するために使用できるAPIのライブラリです。

一般的に言えば、原則は、APIが各応答とともに、次の潜在的なステップに関する関連情報を返すことによって、アプリケーションを通じてクライアントをガイドする必要があることを意味します。

この記事では、クライアントとサーバーを分離し、理論的にはAPIがクライアントを壊すことなくURIスキームを変更できるようにすることを目的として、SpringHATEOASを使用した例を作成します。

3. 準備

まず、SpringHATEOASの依存関係を追加しましょう。


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

Spring Bootを使用していない場合は、次のライブラリをプロジェクトに追加できます。


    org.springframework.hateoas
    spring-hateoas
    0.25.1.RELEASE


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

いつものように、Maven Centralのstarter HATEOASspring-hateoas、およびspring-plugin-core依存関係の最新バージョンを検索できます。

次に、Spring HATEOASをサポートしないCustomerリソースがあります。

public class Customer {

    private String customerId;
    private String customerName;
    private String companyName;

    // standard getters and setters
}

そして、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);
    }
}

最後に、Customerのリソース表現:

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

4. HATEOASサポートの追加

Spring HATEOASプロジェクトでは、サーブレットコンテキストを検索したり、パス変数をベースURIに連結したりする必要はありません。

代わりに、Spring HATEOAS offers three abstractions for creating the URI – ResourceSupport, Link, and ControllerLinkBuilder。 これらを使用してメタデータを作成し、それをリソース表現に関連付けることができます。

4.1. リソースへのハイパーメディアサポートの追加

プロジェクトは、リソース表現を作成するときに継承するResourceSupportという基本クラスを提供します。

public class Customer extends ResourceSupport {
    private String customerId;
    private String customerName;
    private String companyName;

    // standard getters and setters
}

The Customer resource extends from the ResourceSupport class to inherit the add() method。 したがって、リンクを作成したら、新しいフィールドを追加せずに、その値をリソース表現に簡単に設定できます。

Spring HATEOASは、メタデータ(リソースの場所またはURI)を格納するためのLinkオブジェクトを提供します。

まず、簡単なリンクを手動で作成します。

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

Linkオブジェクトは、https://en.wikipedia.org/wiki/Atom(standard)[Atom] _リンク構文に従い、リソースとの関係を識別するrelと、実際のリンクであるhref属性で構成されます。自体。

新しいリンクが含まれているため、Customerリソースは次のようになります。

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

応答に関連付けられたURIは、selfリンクとして修飾されます。 self関係のセマンティクスは明確です。これは、リソースにアクセスできる正規の場所にすぎません。

ライブラリによって提供されるもう1つの非常に重要な抽象化は、リンクのハードコーディングを回避することによるthe ControllerLinkBuilder – which simplifies building URIsです。

次のスニペットは、ControllerLinkBuilderクラスを使用して顧客の自己リンクを構築する方法を示しています。

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

みてみましょう:

  • linkTo()メソッドは、コントローラークラスを検査し、そのルートマッピングを取得します

  • slash()メソッドは、リンクのパス変数としてcustomerId値を追加します

  • 最後に、withSelfMethod()は関係を自己リンクとして修飾します

5. 関係

前のセクションでは、自己参照関係を示しました。 ただし、より複雑なシステムには他の関係も含まれる場合があります。

たとえば、customerは注文と関係を持つことができます。 Orderクラスもリソースとしてモデル化しましょう。

public class Order extends ResourceSupport {
    private String orderId;
    private double price;
    private int quantity;

    // standard getters and setters
}

この時点で、特定の顧客のすべての注文を返すメソッドを使用してCustomerControllerを拡張できます。

@GetMapping(value = "/{customerId}/orders", produces = { "application/hal+json" })
public Resources 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();
    Resources result = new Resources(orders, link);
    return result;
}

このメソッドは、HALの戻り値の型に準拠するためにResourcesオブジェクトを返し、各注文と完全なリストの「_self”」リンクを返します。

ここで注意すべき重要な点は、顧客の注文のハイパーリンクがgetOrdersForCustomer()メソッドのマッピングに依存していることです。 これらのタイプのリンクをメソッドリンクと呼び、ControllerLinkBuilderがそれらの作成をどのように支援できるかを示します。

ControllerLinkBuilderは、SpringMVCコントローラーの豊富なサポートを提供します。 次の例は、CustomerControllerクラスのgetOrdersForCustomer()メソッドに基づいてHATEOASハイパーリンクを構築する方法を示しています。

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

プロキシコントローラのThe methodOn() obtains the method mapping by making dummy invocation of the target methodは、customerIdをURIのパス変数として設定します。

7. 春のHATEOASの実行

セルフリンクとメソッドリンクの作成をすべてgetAllCustomers()メソッドにまとめましょう。

@GetMapping(produces = { "application/hal+json" })
public Resources 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();
    Resources result = new Resources(allCustomers, link);
    return result;
}

次に、getAllCustomers()メソッドを呼び出しましょう。

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

そして結果を調べます:

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

各リソース表現内には、顧客のすべての注文を抽出するためのselfリンクとallOrdersリンクがあります。 顧客が注文を持っていない場合、注文のリンクは表示されません。

この例は、Spring HATEOASがREST WebサービスでAPIの発見可能性を促進する方法を示しています。 If the link exists, the client can follow it and get all orders for a customer:

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

8. 結論

このチュートリアルでは、build a hypermedia-driven Spring REST web service using the Spring HATEOAS projectを実行する方法について説明しました。

この例では、クライアントがアプリケーションへの単一のエントリポイントを持つことができ、応答表現のメタデータに基づいてさらにアクションを実行できることがわかります。

これにより、サーバーはクライアントを中断せずにURIスキームを変更できます。 また、アプリケーションは、表現に新しいリンクまたはURIを配置することにより、新しい機能をアドバタイズできます。

最後に、この記事の完全な実装はthe GitHub projectにあります。