mmts1007’s diary

プログラミング関連の技術系ブログです。

Node EasyMock で API モックサーバを作る

Node EasyMock を使って、API モックを作ってみた。

github.com

フロントエンド:Angular JS
バックエンド:Spring Boot
という感じでアプリを作っているのですが、フロントエンドから作りたくなったので使ってみました。

インストール

基本的には README の通り

$ npm install -g easymock
$ easymock

で動作しました。

ただし、node のバージョンの指定があるようで v5.0.0 で実行したときに下記のメッセージが表示されました。

wanted: {"node":">= 0.10.0 < 0.11.0 || >= 0.11.13 < 0.13.0 || >= 1.0.0 < 2.0.0"} (current: {"node":"5.0.0","npm":"3.3.6"})

ということで、今は v0.12.7 を使用しています。

API モック

Node EasyMock は URL、HTTP Method と対応する JSON ファイルの内容を返却します。
例えば、GET /users をリクエストした場合 users_get.json ファイルの内容を返却します。 [URL]_[HTTP Method].json の形式でファイルを用意するだけで、API モックを作ることができます。

また、/users/1/ などのネストした URL の場合は users ディレクトリ配下に 1_get.json を配置します。

ログとドキュメント

Node EasyMock はログとドキュメントを表示する機能もあります。 /_documentation/ にアクセスすると API ドキュメントを表示することができます。

ドキュメントでは API の一覧を確認することができます。

f:id:mmts1007:20151121235050p:plain

エンドポイントをクリックすると、対象のエンドポイントのレスポンスを確認することができます。

f:id:mmts1007:20151121235250p:plain

同様に /_logs/ にアクセスすると、アクセスログを確認することができます。

f:id:mmts1007:20151122000101p:plain

まとめ

フロントエンドの開発とバックエンドの開発が並行して進む場合など、バックエンドがまだ出来ていないときに利用してみてはいかがでしょうか? JSON ファイルのみ用意できれば使用できるので、学習コストも低くて良いと思います。

GitHub を使ったプロジェクト管理ツール Waffle を使ってみた

先日面白い記事を見つけました。

tech-startup.hatenablog.com

GitHub の issue、Pull Request を使ってプロジェクト管理を行うためのツールです。

この中でも、Waffle が使いやすいというコメントをいただいたので、早速使ってみました。

使い方

画面はこんな感じです。

f:id:mmts1007:20151022232713p:plain

Backlog, Ready, In Progress, Done にカラムが分かれていて、 ドラッグ&ドロップで issue を移動させることができます。

また Waffle はコミットコメント、Pull Request のコメントを使って issue を移動させることができます。
close #xx、fix #xx のようにキーワードと issue 番号を記述した Pull Request がマージされると、対象の issue をクローズ してくれます。

サンプルとして「ワッフル テスト issue」という issue を作成しました。

f:id:mmts1007:20151022232718p:plain

この issue を自動的にクローズさせます。

自動的に移動させるには前述のとおり キーワードと issue 番号 をコミットコメントに含める必要があります。

キーワードは下記のとおりです。

  • close, closes, closed
  • fix, fixes, fixed
  • resolve, resolves, resolved

「ワッフル テスト issue」は #17 のため

$ git commit -m "fix #17"

とコミットコメントに記載することで自動的に issue をクローズします。

コミットしたブランチを Push し、Pull Request を作成します。

f:id:mmts1007:20151022232731p:plain

すると、issue と Pull Request が紐づけられ、自動的に issue が In Progress に移動します。

f:id:mmts1007:20151022232727p:plain

最後に Pull Request をマージすると issue, Pull Request が Done に移動します。

f:id:mmts1007:20151022232735p:plain

これで issue を自動的にクローズさせることができました。

issue と Pull Request の紐付き

この記事を書くために Waffle を触っていたのですが、
前述のとおり issue と Pull Request が 1つに合わさったような見た目になる場合と、ならない場合がありました。

結果として Pull Request のタイトルもしくはコメントにキーワードと issue 番号を含めた場合 1つに合わさったような見た目になることがわかりました。

コミットコメントに issue 番号を入れれば、クローズすることはできるので、
最低限コミットコメントにキーワードと issue 番号を含め、
余力があれば Pull Request のコメントに書くことで
どの Pull Request でどの issue が対応されたのか さらに分かりやすくなるのではと思いました。

まとめ

コミットコメントにキーワードと issue 番号を書くだけで、ステータス管理をしてくれるのはとても楽だと感じました。
SCM、チケット管理 etc... 様々なツールを使うとあちこち見なければいけないので、1つにまとまってるのは良いですね。

Lombok でコード量を削減!

前回の記事でも書きましたが、Spring Boot のサンプルではあえて Lombok を使用しませんでした。
理由はまだ Lombok の解説をしていなかったので。

ということで、Lombok の解説をしようと思います。

What's Lombok

公式:Project Lombok

簡単にいうと、Java のお決まりのコードを排除するためのライブラリです。 Getter/Setter とかコンストラクタとかお決まりで書いているコードをアノテーションを付けることで排除できるようになります。(すごい!)

導入

Lombok は依存関係に追加する他に、IDE にも設定する必要があります。

  1. 依存関係の解決 公式にも記載されていますが、 Gradle を使う場合は下記の通り
provided "org.projectlombok:lombok:1.16.6"

ちなみに providedapply plugin: 'war' を指定しないと使えないので、指定していない場合は compile に変更してください。

  1. IDE への追加 Eclipse の場合は公式から jar をダウンロード、ダブルクリックし、Lombok をインストールしたい IDE を選択すれば完了です。

サンプル

サンプルに Spring Boot の時に使用したエンティティをリファクタします。

元ソース

package hello.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "tasks")
public class Task {

    @Id
    @GeneratedValue
    private Integer id;

    @Column(nullable = false)
    private String context;

    public Task() {
    }

    public Task(Integer id, String context) {
        this.id = id;
        this.context = context;
    }

    public Integer getId() {
        return id;
    }

    public String getContext() {
        return context;
    }
}

まずはコンストラクタを削除
今回、引数なしのコンストラクタと、すべてのフィールドを持つコンストラクタが定義されているので、この 2 つを Lombok で生成します。 引数なしのコンストラクタ@NoArgsConstructor、すべてのフィールドを持つコンストラクタ@AllArgsConstructor で生成できます。

package hello.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "tasks")
@NoArgsConstructor
@AllArgsConstructor
public class Task {

    @Id
    @GeneratedValue
    private Integer id;

    @Column(nullable = false)
    private String context;

    public Integer getId() {
        return id;
    }

    public String getContext() {
        return context;
    }
}

同様に Getter/Setter も削除します。
@Getter@Setter アノテーションもあるのですが、データを管理するクラスのためのアノテーション @Data を付与します。
@Data@Getter@Setter アノテーションを含んでいるので、付与されます。

package hello.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Entity
@Table(name = "tasks")
@NoArgsConstructor
@AllArgsConstructor
public class Task {

    @Id
    @GeneratedValue
    private Integer id;

    @Column(nullable = false)
    private String context;
}

コード量がかなり少なくなりました。

f:id:mmts1007:20151012172542p:plain

Outline を見ると、ちゃんとコンストラクタ、Getter/Setter が定義されていますね。

その他のアノテーション

Lombok には他にも様々なアノテーションがあります。

Lombok feature overview

@Data, @Value, @Builder 辺りはすぐに導入できると思います。

@Data

上でも解説しましたが、POJO や Beans に使用するアノテーションです。 また、POJO や Beans に使用するアノテーション @ToString, @EqualsAndHashCode, @Getter, @Setter, @RequiredArgsConstructor も自動で付与されます。

@ToString

デバッグがしやすいように、クラスのフィールド名、フィールドの値を出力するように toString メソッドをオーバーライドします。

@EqualsAndHashCode

オブジェクトに定義されているフィールドの値で equails, hashCode を算出できるように equails, hashCode メソッドをオーバーライドします。
プレーンな Java だと Bean クラスのフィールドの値が一緒でも、equals は false になってしまうので、開発者がいちいち再定義するのは面倒なのでありがたいです。

@Value

イミュータブル(不変の値)を持つクラスを簡単に生成するアノテーションです。
イミュータブルなので値の変更ができないため、フィールドはデフォルトで private, final になります。setter は生成されません。

@Builder

ビルダーパターンを簡単に生成するアノテーションです。

以上です。
Lombok はデファクトスタンダードになると思います。すでにデファクトスタンダードかもしれません。
メリットの方が大きいと思うので、積極的に導入しはいかがでしょうか。

Java Spring Boot その8

7回に渡って Spring Boot について記事を書いてきたので、Spring Boot を触ってみた感想を書いてみようと思います。

良かった点

設定ファイル(XML) が少ない

今までのサンプルを通して、設定ファイルは 1 回も書きませんでした。
Spring というと XML というイメージがあったのですが、全く書かなくてここまで出来るとは思っていませんでした。

実装が揃っている

リポジトリクラスを作成した時に驚いたのですが、よく使う実装を用意してくれているのがすごく有り難いですね。
DRY に繋がると思います。

starter が良い

使いたい機能の starter を設定すれば、必要な依存が自動的に追加される。依存関係の解決がとても楽でした。
Java というとライブラリ依存が激しいので、この機能は開発者にとって楽だと思います。

アノテーションが理解しやすい

こういう意味のアノテーションなんだろうな。というのが名前から推測しやすかったです。

記述量が少ない

実装が揃っている、starterが良い と繋がると思うのですが、
これらのお陰で記述量がかなり削減できていると思います。

組み込み Tomcat が良い

Java の Web アプリケーションというと Tomcat を用意して、EclipseTomcat プラグイン入れて…と色々面倒ですが、
Tomcat が組み込まれていることで、プレーンの Java を実行すればよいのはとても楽でした。
リリース時等も楽なのだろうなと思いながら、実装していました。

Spring Loaded が良い

Spring Boot の利点とは少し違いますが、Spring Loaded が結構効いてくれて、アプリケーションサーバの再起動回数が大幅に削減できました。 再起動自体も組み込み Tomcat のお陰で楽ですし。

気になった点

記述量が多い

上記の記述量が少ないと全く反対のことを書いていますが、Spring Boot として記述量が多いのではなく、Java として。
やっぱり Ruby と比べてると記述量が多いなって感じます。
今回あえて lombok を使わなかったのですが、lombok 使わないとこんなに書かなきゃいけないのかと思いました。
静的型付け言語なので、安心感は大きく得られるのですけどね。

以上です。
次回からは Spring Boot から少し離れようと思います。

Java Spring Boot その7

今回は DB 実装部分のソースを紹介します。

エンティティ

package hello.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "tasks")
public class Task {

    @Id
    @GeneratedValue
    private Integer id;

    @Column(nullable = false)
    private String context;

    public Task() {
    }

    public Task(Integer id, String context) {
        this.id = id;
        this.context = context;
    }

    public Integer getId() {
        return id;
    }

    public String getContext() {
        return context;
    }
}

@Entity

エンティティクラスを表すためのアノテーション

@Table(name = "tasks")

エンティティに紐づくテーブルを指定するアノテーション。デフォルトではエンティティ名がクラス名となります。

@Id

主キーのフィールドを表すアノテーション

@GeneratedValue

主キーが自動採番されることを表す。生成方法を指定することもできる。

@Column(nullable = false)

カラムに対する設定を行うアノテーション
nullable = false と設定し、Not Null であることを設定。

リポジトリ

package hello.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import hello.domain.Task;

public interface TaskRepository extends JpaRepository<Task, Integer> {

}

JpaRepository を継承したインターフェースを作成するだけで、取得、登録、更新、削除ができるようになります。(すごい!)
JpaRepository<Task, Integer> のようにジェネリクスに対象のエンティティ、IDカラムのデータ型を宣言し、終了です。

サービス

package hello.service;

import java.util.List;

import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import hello.domain.Task;
import hello.repository.TaskRepository;

@Service
@Transactional
public class TaskService {
    @Autowired
    TaskRepository taskRepository;

    public List<Task> findAll() {
        return taskRepository.findAll();
    }

    public Task findOne(Integer id) {
        return taskRepository.findOne(id);
    }

    public Task create(Task task) {
        return taskRepository.save(task);
    }

    public Task update(Task task) {
        return taskRepository.save(task);
    }

    public void delete(Integer id) {
        taskRepository.delete(id);
    }
}

@Service

サービスクラスを表すアノテーション

@Transactional

トランザクション境界を設定するアノテーション
サービスに設定したため、トランザクション境界はサービスとなります。

@Autowired

DI (依存性注入) を行うためのアノテーション
Autowired アノテーションを記述すると、自動的に DI を解決してくれます。

サービスクラス自体はリポジトリクラスの内容を呼び出す形なので特に説明はいらないと思います。

コントローラ

package hello;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import hello.domain.Task;
import hello.service.TaskService;

@RestController
@RequestMapping("api/tasks")
@EnableAutoConfiguration
@ComponentScan
public class RestSampleController {

    @Autowired
    TaskService taskService;

    @RequestMapping(method = RequestMethod.GET)
    List<Task> getTasks() {
        // GET api/tasks で実行されるメソッド
        return taskService.findAll();
    }

    @RequestMapping(value = "{id}", method = RequestMethod.GET)
    Task getTask(@PathVariable Integer id) {
        // GET api/tasks/{id} で実行されるメソッド
        return taskService.findOne(id);
    }

    @RequestMapping(method = RequestMethod.POST)
    @ResponseStatus(HttpStatus.CREATED)
    Task createTask(@RequestBody Task task) {
        // POST api/tasks で実行されるメソッド
        return taskService.create(task);
    }

    @RequestMapping(value = "{id}", method = RequestMethod.PUT)
    Task updateTask(@PathVariable Integer id, @RequestBody Task task) {
        // PUT api/tasks/{id} で実行されるメソッド
        return taskService.update(task);
    }

    @RequestMapping(value = "{id}", method = RequestMethod.DELETE)
    @ResponseStatus(HttpStatus.NO_CONTENT)
    void deleteTask(@PathVariable Integer id) {
        // DELETE api/tasks で実行されるメソッド
        taskService.delete(id);
    }

    public static void main(String[] args) {
        SpringApplication.run(RestSampleController.class, args);
    }
}

大きな変更はなく、モックデータを削除して、サービスクラスからデータを取得するようにした位です。

@ComponentScan

必要なコンポーネントを自動で読み込んでくれるアノテーションです。
これによりサービス、リポジトリ、エンティティクラスが読み込まれます。

以上で DB 実装部分は終了です。

Java Spring Boot その6

前回のモック部分を DB 接続に変更します。
その前に、今回使う DB、O/R マッパーや概念の説明を先にします。

DB, O/Rマッパーについて

DB は H2、O/R マッパーは Spring Data JPA を使用します。

O/R マッパーというのは DB のデータをオブジェクトにマッピングするためのライブラリです。
DB のデータを Java のクラス、オブジェクトとして扱うためのライブラリだと思ってください。

JPAJava Persistence API の略で、Java の O/R マッパーの仕様です。
Spring Data JPAJPA を拡張したライブラリです。

クラス構成

また、今回は下記の通りクラスを分割し、責務を分散します。

エンティティ

DB の情報を Java オブジェクトに表したもの テーブルがクラス、カラムがフィールドになっている。

リポジトリ

エンティティを登録、検索などといった処理を担うクラス。DB のデータを取得し、エンティティへマッピングする処理等を行う。
DB からのデータ取得方法などはリポジトリクラスに隠蔽させる。

サービス

ビジネスロジックを記述する。リポジトリを使用してデータの取得等もこのクラスが行う。

コントローラ

リクエストをマッピングするクラス
サービスクラスを利用してリクエストに必要な処理を実行する。

MVC のレイヤーと、処理内容を表にすると

  リクエスト 処理 DB
アプリケーション層 コントローラ
ドメイン サービス
ドメイン リポジトリ
ドメイン エンティティ

だと私は考えています。※あくまで私の考えです。

今回はここまで。実際のソースは次の記事で。

Java Spring Boot その5

前回の続きで POST, PUT, DELETE メソッドを作成をします。

ソース

POST, PUT, DELETE を追加したソースは下記の通りです。
また、モックデータの作成方法を変更しました。

package hello;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("api/tasks")
@EnableAutoConfiguration
public class RestSampleController {

    static class Task {
        public Task() {
        }

        public Task(Integer id, String context) {
            this.id = id;
            this.context = context;
        }

        public Integer id;
        public String context;
    }

    static Map<Integer, Task> tasks = new HashMap<>();

    @RequestMapping(method = RequestMethod.GET)
    List<Task> getTasks() {
        // GET api/tasks で実行されるメソッド
        return new ArrayList<>(tasks.values());
    }

    @RequestMapping(value = "{id}", method = RequestMethod.GET)
    Task getTask(@PathVariable Integer id) {
        // GET api/tasks/{id} で実行されるメソッド
        return tasks.get(id);
    }

    @RequestMapping(method = RequestMethod.POST)
    @ResponseStatus(HttpStatus.CREATED)
    Task createTask(@RequestBody Task task) {
        // POST api/tasks で実行されるメソッド
        int nextIndex = tasks.keySet().stream().max(Comparator.naturalOrder()).get() + 1;
        task.id = nextIndex;
        tasks.put(nextIndex, task);
        return task;
    }

    @RequestMapping(value = "{id}", method = RequestMethod.PUT)
    Task updateTask(@PathVariable Integer id, @RequestBody Task task) {
        // PUT api/tasks/{id} で実行されるメソッド
        Task targetTask = tasks.get(id);
        if (targetTask == null) {
            return null;
        }
        targetTask.context = task.context;
        tasks.put(id, targetTask);
        return targetTask;
    }

    @RequestMapping(value = "{id}", method = RequestMethod.DELETE)
    @ResponseStatus(HttpStatus.NO_CONTENT)
    void deleteTask(@PathVariable Integer id) {
        // DELETE api/tasks で実行されるメソッド
        tasks.remove(id);
    }

    public static void main(String[] args) {
        // モックデータ作成
        IntStream.range(0, 3).forEach(index -> tasks.put(index, new Task(index, String.format("sample%d", index))));
        SpringApplication.run(RestSampleController.class, args);
    }
}

解説

前回解説した通り、@RequestMapping で POST, PUT, DELETE 各メソッドマッピングする Javaメソッドを作成しました。

@RequestMapping

今回 @RequestMapping(value = "{id}", method = RequestMethod.GET) のように value 属性が登場しました。
value は相対パスを指定することができます。なので getTask メソッドは GET "api/tasks/{id}" とマッピングされます。 また value に指定した {id} はメソッドの引数で取得することができます。
取得するためには @PathVariable アノテーションを付与した 変数を定義します。

@ResponseStatus

ResponseStatus アノテーションは名前の通りですが、レスポンスのステータスを指定することができます。
アノテーションに指定できる値は HttpStatus 型の値です。
HttpStatus は enum となっているので、HttpStatus.CREATED など視覚的にわかりやすい形で指定できます。

@RequestBody

メソッドの引数に指定されている RequestBody アノテーションですが、
こちらも名前の通りリクエストボディの値をマッピングするためのアノテーションです。
今回、リクエストボディに設定される JSON を Task 型の値にマッピングするために使用しています。

結果

メソッドを実行した結果をまとめました。

GET api/tasks

$ curl -v http://localhost:8080/api/tasks -X GET
*   Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> GET /api/tasks HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Sat, 10 Oct 2015 23:40:31 GMT
<
* Connection #0 to host localhost left intact
[{"id":0,"context":"sample0"},{"id":1,"context":"sample1"},{"id":2,"context":"sample2"}]

全てのタスクが返却されています。

GET api/tasks/{id}

$ curl -v http://localhost:8080/api/tasks/2 -X GET
*   Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> GET /api/tasks/2 HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Sat, 10 Oct 2015 23:41:31 GMT
<
* Connection #0 to host localhost left intact
{"id":2,"context":"sample2"}

id で指定したデータが返却されています。

POST api/tasks

$ curl -v http://localhost:8080/api/tasks -X POST -H "Content-Type: application/json" -d "{\"context\" : \"create\"}"
*   Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> POST /api/tasks HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.43.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 22
>
* upload completely sent off: 22 out of 22 bytes
< HTTP/1.1 201 Created
< Server: Apache-Coyote/1.1
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Sat, 10 Oct 2015 23:42:14 GMT
<
* Connection #0 to host localhost left intact
{"id":3,"context":"create"}

作成したデータが返却されています。
@ResponseStatus(HttpStatus.CREATED) を指定したのでレスポンスステータスが HTTP/1.1 201 Created に変化しています。

PUT api/tasks/{id}

$ curl -v http://localhost:8080/api/tasks/1 -X PUT -H "Content-Type: application/json" -d "{\"context\" : \"update\"}"
*   Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> PUT /api/tasks/1 HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.43.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 22
>
* upload completely sent off: 22 out of 22 bytes
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Sat, 10 Oct 2015 23:42:40 GMT
<
* Connection #0 to host localhost left intact
{"id":1,"context":"update"}

id で指定した要素のデータが更新されています。

DELETE api/tasks/{id}

$ curl -v http://localhost:8080/api/tasks/1 -X DELETE
*   Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> DELETE /api/tasks/1 HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 204 No Content
< Server: Apache-Coyote/1.1
< Date: Sat, 10 Oct 2015 23:42:56 GMT
<
* Connection #0 to host localhost left intact

@ResponseStatus(HttpStatus.NO_CONTENT) を指定したので HTTP/1.1 204 No Content となり レスポンスもありません。

以上で REST API の作成は完了です。
次回はモック部分のデータを DB とやりとりするよう修正したいと思います。