基本 ローカル(Spring Boot) 約30分

POST と PUT と DELETE の入口をつかむ

POST / PUT / DELETE の REST API を ArrayList で仮実装し、curl で動作確認するレッスン。

前回は GET で JSON データを返す API を作りました。Web アプリでは読み取りだけでなく、データの作成・更新・削除も必要です。それぞれの操作に対応する HTTP メソッドがあります。

HTTP メソッドと CRUD の対応

DB の回で SQL の CRUD を学びました。REST API でも同じ4操作を HTTP メソッドで表現します。

操作SQLHTTP メソッドSpring Boot のアノテーション
作成INSERTPOST@PostMapping
読み取りSELECTGET@GetMapping
更新UPDATEPUT@PutMapping
削除DELETEDELETE@DeleteMapping

前回は GET(読み取り)だけを扱いました。今回は残りの3つを追加します。

仮のデータストアを用意する

データベースとの接続は次回に回します。今回は ArrayList を使い、メモリ上でデータを管理します。

EmployeeController.java を次のように書き換えます。

package com.example.demo;

import java.util.ArrayList;
import java.util.List;
import org.springframework.web.bind.annotation.*;

@RestController
@CrossOrigin
public class EmployeeController {

    private List<Employee> employees = new ArrayList<>();
    private int nextId = 1;

    public EmployeeController() {
        employees.add(new Employee(nextId++, "田中太郎", "開発部"));
        employees.add(new Employee(nextId++, "鈴木花子", "営業部"));
        employees.add(new Employee(nextId++, "佐藤次郎", "開発部"));
    }

    @GetMapping("/api/employees")
    public List<Employee> getAll() {
        return employees;
    }
}

コンストラクタで初期データを3件入れています。nextId は新しい社員を追加するとき用の連番です。

import org.springframework.web.bind.annotation.*;* は、@GetMapping @PostMapping @RequestBody などをまとめてインポートする書き方です。

POST で追加する

新しい社員を追加する API を作ります。getAll メソッドの下に追加します。

@PostMapping("/api/employees")
public Employee create(@RequestBody Employee employee) {
    Employee newEmployee = new Employee(nextId++, employee.getName(), employee.getDepartment());
    employees.add(newEmployee);
    return newEmployee;
}

新しいアノテーションが2つ出てきます。

アノテーション役割
@PostMappingPOST リクエストを受け取るメソッドに付ける
@RequestBodyリクエストの JSON を Java オブジェクトに変換する

クライアントが送る JSON はこのような形です。

{"name": "山田美咲", "department": "総務部"}

@RequestBody が付いた引数に、Jackson が JSON → Employee オブジェクトの変換を行います。GET のときは Java → JSON でしたが、今度は逆方向の JSON → Java です。

id はサーバー側で nextId++ を使って採番するため、クライアントからは送りません。Employee クラスに id を受け取らないコンストラクタか setter が必要になります。

Employee クラスにデフォルトコンストラクタと setter を追加します。

package com.example.demo;

public class Employee {
    private Integer id;
    private String name;
    private String department;

    public Employee() {
    }

    public Employee(Integer id, String name, String department) {
        this.id = id;
        this.name = name;
        this.department = department;
    }

    public Integer getId() { return id; }
    public String getName() { return name; }
    public String getDepartment() { return department; }

    public void setId(Integer id) { this.id = id; }
    public void setName(String name) { this.name = name; }
    public void setDepartment(String department) { this.department = department; }
}

引数なしの public Employee() {} は Jackson がオブジェクトを作るために必要です。setter は JSON の値をフィールドに入れるために使われます。

PUT で更新する

EmployeeController.java に、create メソッドの下へ追加します。ID を指定して社員情報を更新する API です。

@PutMapping("/api/employees/{id}")
public Employee update(@PathVariable int id, @RequestBody Employee employee) {
    for (Employee e : employees) {
        if (e.getId() == id) {
            e.setName(employee.getName());
            e.setDepartment(employee.getDepartment());
            return e;
        }
    }
    return null;
}

@PathVariable は URL の {id} 部分を引数として受け取る仕組みです。/api/employees/2 にアクセスすると id2 が入ります。

処理の流れをたどります。

  1. URL の {id} で対象を特定する
  2. リクエストの JSON からname と department を取得する
  3. リスト内で一致する Employee を探して値を更新する
  4. 更新後の Employee を返す

DELETE で削除する

同じく EmployeeController.javaupdate メソッドの下へ追加します。

@DeleteMapping("/api/employees/{id}")
public String delete(@PathVariable int id) {
    employees.removeIf(e -> e.getId() == id);
    return "deleted";
}

removeIf は、条件に一致する要素をリストから取り除くメソッドです。e -> e.getId() == id はラムダ式と呼ばれる書き方で、「各要素 e について e.getId() == id が true なら削除する」という意味です。

ラムダ式は Java 8 で追加された機能で、「処理を短く書く方法」として覚えておいてください。

EmployeeController.java の完成形

ここまでの4つのメソッドをすべて追加した EmployeeController.java の全体です。

package com.example.demo;

import java.util.ArrayList;
import java.util.List;
import org.springframework.web.bind.annotation.*;

@RestController
@CrossOrigin
public class EmployeeController {

    private List<Employee> employees = new ArrayList<>();
    private int nextId = 1;

    public EmployeeController() {
        employees.add(new Employee(nextId++, "田中太郎", "開発部"));
        employees.add(new Employee(nextId++, "鈴木花子", "営業部"));
        employees.add(new Employee(nextId++, "佐藤次郎", "開発部"));
    }

    @GetMapping("/api/employees")
    public List<Employee> getAll() {
        return employees;
    }

    @PostMapping("/api/employees")
    public Employee create(@RequestBody Employee employee) {
        Employee newEmployee = new Employee(nextId++, employee.getName(), employee.getDepartment());
        employees.add(newEmployee);
        return newEmployee;
    }

    @PutMapping("/api/employees/{id}")
    public Employee update(@PathVariable int id, @RequestBody Employee employee) {
        for (Employee e : employees) {
            if (e.getId() == id) {
                e.setName(employee.getName());
                e.setDepartment(employee.getDepartment());
                return e;
            }
        }
        return null;
    }

    @DeleteMapping("/api/employees/{id}")
    public String delete(@PathVariable int id) {
        employees.removeIf(e -> e.getId() == id);
        return "deleted";
    }
}

curl で動作確認する

POST / PUT / DELETE はブラウザの URL バーでは試せません。ターミナルからコマンドを使って確認します。

Spring Boot を起動した状態で、別のターミナルを開いて実行します。

一覧を確認(どちらの環境でも同じ):

curl http://localhost:8080/api/employees

新規追加(POST):

Windows(PowerShell)の場合:

Invoke-RestMethod -Uri http://localhost:8080/api/employees -Method Post -ContentType "application/json" -Body '{"name":"山田美咲","department":"総務部"}'

macOS / Linux の場合:

curl -X POST http://localhost:8080/api/employees -H "Content-Type: application/json" -d '{"name":"山田美咲","department":"総務部"}'

更新(PUT):

Windows(PowerShell)の場合:

Invoke-RestMethod -Uri http://localhost:8080/api/employees/1 -Method Put -ContentType "application/json" -Body '{"name":"田中太郎","department":"総務部"}'

macOS / Linux の場合:

curl -X PUT http://localhost:8080/api/employees/1 -H "Content-Type: application/json" -d '{"name":"田中太郎","department":"総務部"}'

削除(DELETE):

Windows(PowerShell)の場合:

Invoke-RestMethod -Uri http://localhost:8080/api/employees/1 -Method Delete

macOS / Linux の場合:

curl -X DELETE http://localhost:8080/api/employees/1

各コマンドを実行したあとに一覧取得を打って、データが変わっていることを確認します。

アプリを再起動すると、ArrayList の中身はリセットされます。データの永続化は次回、データベースを接続して実現します。

ここまでの整理

メソッドアノテーション役割
GET@GetMappingデータの取得
POST@PostMapping + @RequestBodyデータの作成
PUT@PutMapping + @PathVariable + @RequestBodyデータの更新
DELETE@DeleteMapping + @PathVariableデータの削除

次の回へ

POST で社員を追加し、PUT で更新し、DELETE で削除できることを curl で確認できたら、この回は完了です。

今のデータは再起動で消えます。次の回ではデータベースとつないで、データを永続的に保存できるようにします。