このガイドでは、Spring で「Hello, World」ハイパーメディア駆動型 REST Web サービスを作成するプロセスを順を追って説明します。

ハイパーメディア (英語) は REST の重要な側面です。これにより、クライアントとサーバーを大幅に分離し、独立して進化させるサービスを構築できます。REST リソースに対して返される表現には、データだけでなく、関連リソースへのリンクも含まれます。表現の設計はサービス全体の設計にとって重要です。

構築するもの

Spring HATEOAS でハイパーメディア駆動型 REST サービスを構築します:Spring MVC コントローラーを指すリンクを作成し、リソース表現を構築し、サポートされるハイパーメディア形式(HAL など)にレンダリングする方法を制御するために使用できる API のライブラリ)。

サービスは http://localhost:8080/greeting で HTTP GET リクエストを受け入れます。

これは、可能な限り単純なハイパーメディア要素、リソース自体を指すリンクで強化された挨拶の JSON 表現で応答します。次のリストに出力を示します。

{
  "content":"Hello, World!",
  "_links":{
    "self":{
      "href":"http://localhost:8080/greeting?name=World"
    }
  }
}

次のリストに示すように、レスポンスはクエリ文字列にオプションの name パラメーターを使用してグリーティングをカスタマイズできることをすでに示しています。

http://localhost:8080/greeting?name=User

name パラメーター値は、次のリストに示すように、World のデフォルト値をオーバーライドし、レスポンスに反映されます。

{
  "content":"Hello, User!",
  "_links":{
    "self":{
      "href":"http://localhost:8080/greeting?name=User"
    }
  }
}

必要なもの

このガイドを完了する方法

ほとんどの Spring 入門ガイドと同様に、最初から始めて各ステップを完了するか、すでに慣れている場合は基本的なセットアップステップをバイパスできます。いずれにしても、最終的に動作するコードになります。

最初から始めるには、Spring Initializr から開始に進みます。

基本スキップするには、次の手順を実行します。

完了したときは、gs-rest-hateoas/complete のコードに対して結果を確認できます。

Spring Initializr から開始

すべての Spring アプリケーションでは、Spring Initializr (英語) から始める必要があります。Initializr は、アプリケーションに必要なすべての依存関係をすばやく取り込む方法を提供し、多くの設定を行います。この例では、Spring HATEOAS 依存関係が必要です。

次のリストは、Maven を選択したときに作成される pom.xml ファイルを示しています。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.2.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>rest-hateoas</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>rest-hateoas</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-hateoas</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

次のリストは、Gradle を選択したときに作成される build.gradle ファイルを示しています。

plugins {
	id 'org.springframework.boot' version '2.3.2.RELEASE'
	id 'io.spring.dependency-management' version '1.0.8.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-hateoas'
	testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
}

test {
	useJUnitPlatform()
}

JSON ライブラリの追加

JSON を使用して情報を送受信するため、JSON ライブラリが必要です。このガイドでは、Jayway JsonPath ライブラリを使用します。

ライブラリを Maven ビルドに含めるには、pom.xml ファイルに次の依存関係を追加します。

<dependency>
  <groupId>com.jayway.jsonpath</groupId>
  <artifactId>json-path</artifactId>
  <scope>test</scope>
</dependency>

次のリストは、完成した pom.xml ファイルを示しています。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.2.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>rest-hateoas</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>rest-hateoas</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-hateoas</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

ライブラリを Gradle ビルドに含めるには、build.gradle ファイルに次の依存関係を追加します。

testCompile 'com.jayway.jsonpath:json-path'

次のリストは、完成した build.gradle ファイルを示しています。

plugins {
	id 'org.springframework.boot' version '2.3.2.RELEASE'
	id 'io.spring.dependency-management' version '1.0.8.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-hateoas'
	testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
}

test {
	useJUnitPlatform()
}

リソース表現クラスを作成する

プロジェクトとビルドシステムをセットアップしたため、Web サービスを作成できます。

サービスの相互作用について考えることでプロセスを開始します。

サービスは、/greeting でリソースを公開し、GET リクエストを処理します。オプションで、クエリ文字列に name パラメーターを使用します。GET リクエストは、挨拶を表すために本文に JSON を含む 200 OK レスポンスを返す必要があります。

さらに、リソースの JSON 表現は、_links プロパティのハイパーメディア要素のリストで強化されます。これの最も基本的な形式は、リソース自体を指すリンクです。表現は次のリストのようになります。

{
  "content":"Hello, World!",
  "_links":{
    "self":{
      "href":"http://localhost:8080/greeting?name=World"
    }
  }
}

content は、挨拶のテキスト表現です。_links エレメントには、リンクのリストが含まれます(この場合、リレーションタイプが rel で、アクセスされたリソースを指す href 属性を持つものだけです)。

挨拶表現をモデル化するには、リソース表現クラスを作成します。_links プロパティはリプレゼンテーションモデルの基本的なプロパティであるため、Spring HATEOAS には、Link のインスタンスを追加し、それらが前述のようにレンダリングされることを保証する基本クラス(RepresentationModel と呼ばれます)が付属しています。

次のリスト(src/main/java/com/example/resthateoas/Greeting.java から)が示すように、RepresentationModel を継承し、コンテンツのフィールドとアクセッサ、およびコンストラクターを追加するプレーンな古い java オブジェクトを作成します。

package com.example.resthateoas;

import org.springframework.hateoas.RepresentationModel;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

public class Greeting extends RepresentationModel<Greeting> {

	private final String content;

	@JsonCreator
	public Greeting(@JsonProperty("content") String content) {
		this.content = content;
	}

	public String getContent() {
		return content;
	}
}
  • @JsonCreator: Jackson がこの POJO のインスタンスを作成する方法を通知します。

  • @JsonProperty: Jackson がこのコンストラクター引数を配置するフィールドをマークします。

このガイドで後述するように、Spring は Jackson JSON ライブラリを使用して、タイプ Greeting のインスタンスを JSON に自動的にマーシャリングします。

次に、これらのグリーティングを提供するリソースコントローラーを作成します。

REST コントローラーを作成する

RESTful Web サービスを構築する Spring のアプローチでは、HTTP リクエストはコントローラーによって処理されます。コンポーネントは、@RestController(Javadoc) アノテーションによって識別されます。これは、@Controller(Javadoc) アノテーションと @ResponseBody(Javadoc) アノテーションを組み合わせたものです。次の GreetingController (src/main/java/com/example/resthateoas/GreetingController.java から)は、Greeting クラスの新しいインスタンスを返すことにより、/greeting に対する GET リクエストを処理します。

package com.example.resthateoas;

import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@RestController
public class GreetingController {

	private static final String TEMPLATE = "Hello, %s!";

	@RequestMapping("/greeting")
	public HttpEntity<Greeting> greeting(
		@RequestParam(value = "name", defaultValue = "World") String name) {

		Greeting greeting = new Greeting(String.format(TEMPLATE, name));
		greeting.add(linkTo(methodOn(GreetingController.class).greeting(name)).withSelfRel());

		return new ResponseEntity<>(greeting, HttpStatus.OK);
	}
}

このコントローラーは簡潔でシンプルですが、多くのことを示しています。段階的に分解していきましょう。

@RequestMapping アノテーションは、/greeting への HTTP リクエストが greeting() メソッドにマップされることを保証します。

上記の例では、GET と PUTPOST などを指定していません。これは、@RequestMapping がデフォルトですべての HTTP 操作をマップするためです。@GetMapping("/greeting") を使用して、このマッピングを絞り込みます。その場合は、import org.springframework.web.bind.annotation.GetMapping; も使用します。

@RequestParam は、クエリ文字列パラメーター name の値を greeting() メソッドの name パラメーターにバインドします。defaultValue 属性を使用しているため、このクエリ文字列パラメーターは暗黙的に required ではありません。リクエストにない場合、World の defaultValue が使用されます。

@RestController アノテーションがクラスに存在するため、暗黙的な @ResponseBody(Javadoc) アノテーションが greeting メソッドに追加されます。これにより、Spring MVC は返された HttpEntity とそのペイロード(Greeting)を直接レスポンスにレンダリングします。

メソッド実装の最も興味深い部分は、コントローラーメソッドを指すリンクを作成する方法と、それを表現モデルに追加する方法です。linkTo(…) と methodOn(…) はどちらも ControllerLinkBuilder の静的メソッドであり、コントローラーでメソッド呼び出しを偽装できます。返された LinkBuilder は、コントローラーメソッドのマッピングアノテーションをインスペクションして、メソッドがマッピングされる URI を正確に構築します。

Spring HATEOAS は、さまざまな X-FORWARDED- ヘッダーを考慮します。Spring HATEOAS サービスをプロキシの背後に配置し、X-FORWARDED-HOST ヘッダーで適切に構成すると、結果のリンクは適切にフォーマットされます。

withSelfRel() を呼び出すと、Link インスタンスが作成され、Greeting 表現モデルに追加されます。

@SpringBootApplication は、次のすべてを追加する便利なアノテーションです。

  • @Configuration: アプリケーションコンテキストの Bean 定義のソースとしてクラスにタグを付けます。

  • @EnableAutoConfiguration: クラスパス設定、他の Bean、およびさまざまなプロパティ設定に基づいて Bean の追加を開始するよう Spring Boot に指示します。例: spring-webmvc がクラスパスにある場合、このアノテーションはアプリケーションに Web アプリケーションとしてフラグを立て、DispatcherServlet のセットアップなどの主要な動作をアクティブにします。

  • @ComponentScan: Spring に、com/example パッケージ内の他のコンポーネント、構成、およびサービスを探して、コントローラーを検出させるように指示します。

main() メソッドは、Spring Boot の SpringApplication.run() メソッドを使用してアプリケーションを起動します。XML が 1 行もないことに気付きましたか? web.xml ファイルもありません。この Web アプリケーションは 100% 純粋な Java であり、接続機能やインフラストラクチャの構成に対処する必要はありませんでした。

実行可能 JAR を構築する

コマンドラインから Gradle または Maven を使用してアプリケーションを実行できます。必要なすべての依存関係、クラス、およびリソースを含む単一の実行可能 JAR ファイルを構築して実行することもできます。実行可能な jar を構築すると、開発ライフサイクル全体、さまざまな環境などで、アプリケーションとしてサービスを簡単に提供、バージョン管理、およびデプロイできます。

Gradle を使用する場合、./gradlew bootRun を使用してアプリケーションを実行できます。または、次のように、./gradlew build を使用して JAR ファイルをビルドしてから、JAR ファイルを実行できます。

java -jar build/libs/gs-rest-hateoas-0.1.0.jar

Maven を使用する場合、./mvnw spring-boot:run を使用してアプリケーションを実行できます。または、次のように、./mvnw clean package で JAR ファイルをビルドしてから、JAR ファイルを実行できます。

java -jar target/gs-rest-hateoas-0.1.0.jar
ここで説明する手順は、実行可能な JAR を作成します。クラシック WAR ファイルを作成することもできます。

ロギング出力が表示されます。サービスは数秒以内に起動して実行されるはずです。

サービスをテストする

サービスが起動したら、http://localhost:8080/greeting にアクセスして、次のコンテンツを確認します。

{
  "content":"Hello, World!",
  "_links":{
    "self":{
      "href":"http://localhost:8080/greeting?name=World"
    }
  }
}

次の URL にアクセスして、name クエリ文字列パラメーターを指定します: http://localhost:8080/greeting?name=User 次のリストに示すように、content 属性の値が Hello, World! から Hello, User! にどのように変化し、self リンクの href 属性にもその変化が反映されることに注意してください。

{
  "content":"Hello, User!",
  "_links":{
    "self":{
      "href":"http://localhost:8080/greeting?name=User"
    }
  }
}

この変更は、GreetingController の @RequestParam 配置が期待どおりに機能することを示しています。name パラメーターには World のデフォルト値が指定されていますが、照会ストリングを介して常に明示的にオーバーライドできます。

要約

おめでとう! Spring HATEOAS でハイパーメディア駆動型 RESTful Web サービスを開発しました。