Spring BootとAngularJSでREST APIアプリを実装してみる
例によって直近の必要に迫られ、あれこれ調べつつ、サーバ側にSpring BootベースのREST API/クライアント側にAngularJSベースのMVC(MVW)という構成を持つアプリを実装してみました。
以前、Spring BootとMybatisの連携方法などを確認する(そしてそうした連携の必要なアプリのためのテンプレートを用意する)ためにつくったサンプルに手を加えるかたちにしました。実装したコードはこちらのリポジトリにコミットしてあります。
前提事項
使用したフレームワークは以下のとおりです:
- 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オブジェクトのいずれの方向でも適切に自動変換されるようになります。