1概要

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

** 2春の憎しみ

**

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

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

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


3準備

まず、Spring HATEOAS依存関係を追加しましょう。

<dependency>
    <groupId>org.springframework.hateoas</groupId>
    <artifactId>spring-hateoas</artifactId>
    <version>0.19.0.RELEASE</version>
</dependency>

次に、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;

    @RequestMapping(value = "/{customerId}", method = RequestMethod.GET)
    public Customer getCustomerById(@PathVariable String customerId) {
        return customerService.getCustomerDetail(customerId);
    }
}

最後に、顧客リソースの表現は次のとおりです。

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


4 HATEOASサポートを追加する

Spring HATEOASプロジェクトでは、サーブレットコンテキストをルックアップする必要も、path変数をベースURIに連結する必要もありません。 Spring HATEOASは、URIを作成するための3つの抽象化(

ResourceSupport、Link

、および

ControllerLinkBuilder

)を提供します。これらはメタデータを作成し、それをリソース表現に関連付けるために使用されます。

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

**

Spring HATEOASプロジェクトは、リソース表現を作成するときに継承する

ResourceSupport

という基本クラスを提供します。

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

   //standard getters and setters
}


Customer

リソースは、

ResourceSupport

クラスから

add()

メソッドを継承するように拡張されています。したがって、一度リンクを作成すれば、新しいフィールドを追加することなく、その値をリソース表現に簡単に設定できます。

Spring HATEOASはメタデータ(リソースの場所またはURI)を格納するための

Link

オブジェクトを提供します。

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

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


Link

オブジェクトは


https://en.wikipedia.org/wiki/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

リレーションのセマンティクスは明らかです – それは単にリソースがアクセスできる標準的な場所です。

ライブラリによって提供されるもう一つの非常に重要な抽象化は

ControllerLinkBuilder

です – これはハードコードされたリンクを避けることによってURIの構築を単純化します。

次のスニペットは、

ControllerLinkBuilder

クラスを使用して顧客のセルフリンクを構築する方法を示しています。

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

みてみましょう:


  • linkTo()

    メソッドはコントローラクラスを調べてそのクラスを取得します。

ルートマッピング
**

slash()

メソッドは

customerId

値をパス変数として追加します

リンクの
** 最後に、

withSelfMethod()

は関係を自己リンクとして修飾します


5関係

前のセクションでは、自己参照関係について説明しました。より複雑なシステムは他の関係も含むかもしれません。

たとえば、

顧客

は注文と関係を持つことができます。

Order

クラスも同様にリソースとしてモデル化されます。

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

   //standard getters and setters
}

この時点で、

CustomerController

コントローラーは、特定の顧客のすべての注文を返すメソッドで拡張できます。

@RequestMapping(value = "/{customerId}/orders", method = RequestMethod.GET ,
  produces = {"application/hal+json"})
public Resources<Order> getOrdersForCustomer(@PathVariable final String customerId) {
    List<Order> 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<Order> result = new Resources<Order>(orders, link);
    return result;
}

このメソッドは、HAL戻り型に準拠するための

Resources

オブジェクトと、注文ごとの「

__self」

リンクおよび全リストを返します。

ここで注意すべき重要なことは、顧客注文のハイパーリンクは

getOrdersForCustomer()

メソッドのマッピングに依存するということです。このタイプのリンクをメソッドリンクと呼び、

ControllerLinkBuilder

がそれらの作成をどのように支援できるかを示します。

**


ControllerLinkBuilder

は、Spring MVCコントローラを豊富にサポートしています。次の例は、

CustomerController

クラスの

getOrdersForCustomer()

メソッドに基づいてHATEOASハイパーリンクを構築する方法を示しています。

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


methodOn()

は、プロキシコントローラ上でターゲットメソッドをダミー呼び出しすることによってメソッドマッピングを取得し、

customerId

をURIのパス変数として設定します。


7. 春のHATEOAS in Action

セルフリンクとメソッドリンクの作成をまとめて

getAllCustomers()

メソッドにまとめましょう。

@RequestMapping(method = RequestMethod.GET, produces = { "application/hal+json" })
public Resources<Customer> getAllCustomers() {
    List<Customer> 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<Customer> result = new Resources<Customer>(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が休止中のWebサービスでAPIの発見可能性をどのように高めているかを示します。リンクが存在する場合、クライアントはそれをたどり、顧客に対するすべての注文を受け取ることができます。

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結論

このチュートリアルでは、Spring HATEOASプロジェクトを使用してハイパーメディア駆動のSpring REST Webサービスを構築する方法を説明しました。

この例では、クライアントがアプリケーションへの単一のエントリポイントを持つことができ、応答表現内のメタデータに基づいてさらにアクションを実行できることがわかります。これにより、サーバーはクライアントを壊すことなくURIスキームを変更できます。また、アプリケーションは新しいリンクまたはURIを表現に入れることで新しい機能を宣伝できます。

この記事の完全な実装はhttps://github.com/eugenp/tutorials/tree/master/spring-security-rest[the GitHub project]にあります – これはEclipseベースのプロジェクトなので、簡単にできます。そのままインポートして実行します。