M12i.

学術書・マンガ・アニメ・映画の消費活動とプログラミングについて

Spring BootとAngularJSでREST APIアプリを実装してみる

例によって直近の必要に迫られ、あれこれ調べつつ、サーバ側にSpring BootベースのREST API/クライアント側にAngularJSベースのMVC(MVW)という構成を持つアプリを実装してみました。

以前、Spring BootとMybatisの連携方法などを確認する(そしてそうした連携の必要なアプリのためのテンプレートを用意する)ためにつくったサンプルに手を加えるかたちにしました。実装したコードはこちらのリポジトリにコミットしてあります。

f:id:m12i:20150308114928p:plain

前提事項

使用したフレームワークは以下のとおりです:

Spring Boot
言わずもがな
MyBatis
同上
PostgreSQL
同上(こちらの記事でも紹介したDVDレンタルショップのサンプルDBを利用)
AngularJS
クライアントサイドの画面制御とAPIコールのために使用
Bootstrap
画面部品のスタイル制御のために使用(ただしCSSのみ)
UI Bootstrap
Bootstrapベースの画面部品をAngularJSを通じて制御するために使用

静的リソース追加

静的リソースとして一連のライブラリのリソースを追加しました。ThymeleafやAngularJSの利点である「アプリを動かさなくても静的リソースは表示できる」という点を活かすため、Webjarsは使用せず、リソースの調達は手作業で行いました:

  • angular.js
  • angular.min.js
  • angular.min.js.map
  • ui-bootstrap-tpls-0.12.1.min.js
  • bootstrap-theme.css
  • bootstrap-theme.css.map
  • bootstrap-theme.min.css
  • bootstrap.css
  • bootstrap.css.map
  • bootstrap.min.css

このアプリ固有のスタイルやクライアント側のコントローラを定義するために次のファイルを作成しました:

さらに、REST APIに対してリクエストを送る画面のためのThymeleafのテンプレートを作成。実のところ現状ではサーバ側で設定すべき情報はなにもないので、JSやCSS同様にテンプレートではなく単なる静的リソースとして用意しても良かったのですが…:

コントローラ追加

コントローラとしてMyBatisRestSampleController.javaを用意。クラス・レベルのアノテーションとして@RestControllerを指定することで、コントローラ・メソッドの戻り値がレスポンスの本文として利用されるようにしました:

@RestController
@RequestMapping("/mybatis-rest")
public class MyBatisRestSampleController {...}

指定されたリソースが見つからなかった場合のための例外を定義。アノテーションにより、コントローラ・メソッドがこの例外をスローした場合、クライアント側にはHTTPステータスとして404が返されるようにしました:

@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Specified resource is not found.")
public static class NotFoundException extends RuntimeException {
	private static final long serialVersionUID = 6241322176053451224L;
	public NotFoundException() {
		super();
	}
	public NotFoundException(String message) {
		super(message);
	}
}

コントローラ・メソッドを定義。リクエストのパスから取得対象レコードのIDを読み取り、サービスを経由してデータを取得(サービスはMyBatisのマッパーを通じてPostgreSQLにアクセスしている)、結果をメソッドの戻り値として返すことで自動的にJSON形式に変換されてレスポンス本文にしました:

@RequestMapping("/actor/{id}")
public ActorEntity actor(@PathVariable int id) {
	return checkThenReturn(sampleService.getActorById(id));
}
private<T> T checkThenReturn(T data) {
	if (data == null) {
		throw new NotFoundException();
	}
	return data;
}

リクエスト本文にJSON形式のデータを持つリクエストを受け取るコントローラ・メソッドも定義。引数に@RequestBodyアノテーションを付与することで、クライアントから送信されてきたJSON形式文字列をJavaオブジェクトへと自動変換させるようにしています:

@RequestMapping(value = "/to-upper-case", method = RequestMethod.POST)
public ActorEntity toUpperCase(
		@RequestBody ActorEntity actor,
		BindingResult result,
		Model model) {
	...
	return upperCased;
}

コントローラのコードの全文はこちらで参照できます。

加えてJSON形式とJavaオブジェクトの相互変換の対象になるバリュー・オブジェクトについては、java.util.Date型のフィールドにフォーマット文字列を指定。JSONの仕様には日付型は存在しないのでこのような方法を取ることにしました:

@JsonFormat(pattern="yyyy/MM/dd HH:mm:ss.SSS")
private Date lastUpdate;

上記のようにフィールドに直接アノテーションを付与するだけで、Javaオブジェクト→JSON形式文字列/JSON形式文字列→Javaオブジェクトのいずれの方向でも適切に自動変換されるようになります。