SpringでRESTful Web Serviceを構築

投稿者: | 2020年2月7日

Springの公式サイト、Getting Started での記述に倣って、RESTful Web Serviceを構築してみます。

https://spring.io/guides/gs/rest-service/

 

ブラウザでhttp://localhost:8080/greeting にアクセスすると、”id”と”content”がJSON形式で表示されます。

 

URLにパラメータを入れることで、”content”表記を変えることもできます。この例ではパラメータ”name”として”Space”という文字列を入力し、出力結果に反映させています。

 

パッケージのダウンロード

Spring Initializrというサイトを使うのが便利です。ここに必要事項を入力するだけで、依存関係も含めた、必要なモノ一式をパッケージ形式で作成してくれます。

今回のケースでは特に変更するフィールドはありません。初期画面のまま、”Generate”ボタンを押してダウンロードします。

 

“demo.zip”というzipファイルがダウンロードされますので、解凍し適当な場所へ配置します。今回はCドライブ直下。

 

この中で設定ファイルを編集したり、必要なクラスを追加したりしていきます。

 

pom.xmlの編集

まずはpom.xmlの編集をします。GradleでもBuild可能ですが、今回はMavenを使用します。

情報の授受にJSONを使うので、Jayway JasonPathというライブラリを追加します。

 

フォルダ直下にあるpom.xmlをテキストエディタで開き、

 

公式サイト通り、以下の内容を追記します。

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

 

リソース・クラスの作成

次にリソース・クラスを作成します。”id”と”content”の値を保持するクラスです。

公式ページに従い、com.example.restservice を java package として、Greeting クラスを作成します。

package com.example.restservice;

public class Greeting {

	private final long id;
	private final String content;

	public Greeting(long id, String content) {
		this.id = id;
		this.content = content;
	}

	public long getId() {
		return id;
	}

	public String getContent() {
		return content;
	}
}

 

コントローラ・クラスの作成

HTTPリクエストを取り扱う為のコントローラ・クラスを作成します。このクラスは@RestControllerにより識別されます。

公式ページに従い、com.example.restservice を java package として、GreetingController クラスを作成します。

package com.example.restservice;

import java.util.concurrent.atomic.AtomicLong;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class GreetingController {

	private static final String template = "Hello, %s!";
	private final AtomicLong counter = new AtomicLong();

	@GetMapping("/greeting")
	public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) {
		return new Greeting(counter.incrementAndGet(), String.format(template, name));
	}
}

@GetMappingがHTTP GETリクエストを /greeting にマップする働きをします。

(余談ですが、POSTリクエストに対応する@PostMappingも存在します)

また、greetingメソッド中の@RequestParamは、”name”パラメータを使用してcontent中の変数を変更する役割を持ちます。

 

JAVA_HOMEの設定

ここまでできたら実行してみます。コマンドプロンプトを開き、”demo”をカレント・ディレクトリにしてから、次のコマンドを実行。

mvnw spring-boot:run

c:\demo>mvnw spring-boot:run

Error: JAVA_HOME not found in your environment.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation.

おおっと。

Error: JAVA_HOME not found in your environment.

が出てしまったので、環境変数JAVA_HOMEを設定します。

設定画面の開き方は色々ありますが、今回はWindows10デフォルトの検索フィールドからページに飛びます。

 

pom.xmlの再編集

サイド実行してみます。

c:\demo>mvnw spring-boot:run
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------------< com.example:demo >--------------------------
[INFO] Building demo 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] >>> spring-boot-maven-plugin:2.2.4.RELEASE:run (default-cli) > test-compile @ demo >>>
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ demo ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 3 source files to c:\demo\target\classes
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] /c:/demo/src/main/java/com/example/restservice/GreetingController.java:[5,47] パッケージorg.springframework.web.bind.annotationは存在しません
[ERROR] /c:/demo/src/main/java/com/example/restservice/GreetingController.java:[6,47] パッケージorg.springframework.web.bind.annotationは存在しません
[ERROR] /c:/demo/src/main/java/com/example/restservice/GreetingController.java:[7,47] パッケージorg.springframework.web.bind.annotationは存在しません
[ERROR] /c:/demo/src/main/java/com/example/restservice/GreetingController.java:[9,2] シンボルを見つけられません
  シンボル: クラス RestController
[ERROR] /c:/demo/src/main/java/com/example/restservice/GreetingController.java:[16,35] シンボルを見つけられません
  シンボル:   クラス RequestParam
  場所: クラス com.example.restservice.GreetingController
[ERROR] /c:/demo/src/main/java/com/example/restservice/GreetingController.java:[15,10] シンボルを見つけられません
  シンボル:   クラス GetMapping
  場所: クラス com.example.restservice.GreetingController
[INFO] 6 errors
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  5.097 s
[INFO] Finished at: 2020-01-28T11:44:08+09:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project demo: Compilation failure: Compilation failure:
[ERROR] /c:/demo/src/main/java/com/example/restservice/GreetingController.java:[5,47] パッケージorg.springframework.web.bind.annotationは存在しません
[ERROR] /c:/demo/src/main/java/com/example/restservice/GreetingController.java:[6,47] パッケージorg.springframework.web.bind.annotationは存在しません
[ERROR] /c:/demo/src/main/java/com/example/restservice/GreetingController.java:[7,47] パッケージorg.springframework.web.bind.annotationは存在しません
[ERROR] /c:/demo/src/main/java/com/example/restservice/GreetingController.java:[9,2] シンボルを見つけられません
[ERROR]   シンボル: クラス RestController
[ERROR] /c:/demo/src/main/java/com/example/restservice/GreetingController.java:[16,35] シンボルを見つけられません
[ERROR]   シンボル:   クラス RequestParam
[ERROR]   場所: クラス com.example.restservice.GreetingController
[ERROR] /c:/demo/src/main/java/com/example/restservice/GreetingController.java:[15,10] シンボルを見つけられません
[ERROR]   シンボル:   クラス GetMapping
[ERROR]   場所: クラス com.example.restservice.GreetingController
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException

 

コンパイルエラーが発生している・・・。アノテーションからして見つけられていないようです。

パッケージorg.springframework.web.bind.annotationは存在しません

 

実はデフォルトのpom.xmlの記載が正しくなかった模様。

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

artifactIdは、spring-boot-starter-web が正しいので、そのように修正します。

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

 

java package見直し

改めて同じコマンドを実行。

c:\demo>mvnw spring-boot:run
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------------< com.example:demo >--------------------------
[INFO] Building demo 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] >>> spring-boot-maven-plugin:2.2.4.RELEASE:run (default-cli) > test-compile @ demo >>>
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ demo ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 3 source files to c:\demo\target\classes
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:testResources (default-testResources) @ demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory c:\demo\src\test\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ demo ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to c:\demo\target\test-classes
[INFO]
[INFO] <<< spring-boot-maven-plugin:2.2.4.RELEASE:run (default-cli) < test-compile @ demo <<<
[INFO]
[INFO]
[INFO] --- spring-boot-maven-plugin:2.2.4.RELEASE:run (default-cli) @ demo ---
[INFO] Attaching agents: []

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.4.RELEASE)

2020-01-28 11:54:34.265  INFO 11392 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication on MyComputer with PID 11392 (C:\demo\target\classes started by       in c:\demo)
2020-01-28 11:54:34.267  INFO 11392 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
2020-01-28 11:54:35.149  INFO 11392 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-01-28 11:54:35.157  INFO 11392 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-01-28 11:54:35.158  INFO 11392 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.30]
2020-01-28 11:54:35.216  INFO 11392 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-01-28 11:54:35.216  INFO 11392 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 906 ms
2020-01-28 11:54:35.341  INFO 11392 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-01-28 11:54:35.448  INFO 11392 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-01-28 11:54:35.451  INFO 11392 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 1.487 seconds (JVM running for 1.846)

 

今度は無事、サービスが立ち上がった様です。

http://localhost:8080/greeting にアクセスしてみると・・・

 

Errorページが出てしまいます。このページ自体はデフォルトのエラー画面なので問題ないのですが、何故かコントローラにアクセスできていないようです。

で、これも調べていくうちに、先に追加したクラスのパッケージに誤りがありました。

 

これまで作成したクラスは、@SpringBootApplicationと同じパッケージか、そのサブパッケージに作成する必要があったのです・・・

 

“com.example.restservice”パッケージを・・・

 

“com.example.demo.restservice”となるように移動。

 

当然、Greeting.java と GreetingController.java に記載の package も、それに合わせて変更します。

package com.example.demo.restservice;

public class Greeting {

	private final long id;
	private final String content;

	public Greeting(long id, String content) {
		this.id = id;
		this.content = content;
	}

	public long getId() {
		return id;
	}

	public String getContent() {
		return content;
	}
}

 

package com.example.demo.restservice;

import java.util.concurrent.atomic.AtomicLong;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class GreetingController {

	private static final String template = "Hello, %s!";
	private final AtomicLong counter = new AtomicLong();

	@GetMapping("/greeting")
	public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) {
		return new Greeting(counter.incrementAndGet(), String.format(template, name));
	}
}

 

実行結果

改めて同じコマンドを実行。起動後、ブラウザで http://localhost:8080/greeting にアクセスします。

 

今度は無事動きました! “content” パラメータには、デフォルト設定した “Hello, World!” が表示されています。

今度はパラメータを変更してみます。name パラメータで hogehoge を指定してみると・・

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

“content” パラメータが “hogehoge” になっています。また、”id” パラメータがインクリメントされて 2 になっています。