#author("2021-07-01T08:25:07+00:00","","")
#author("2021-07-03T05:44:29+00:00","","")
[[第3回]]

* REST API [#k72e7252]

- テキストはパワーポイントを参照のこと

** ToDo管理アプリにWeb-APIを実装する [#vc9829bb]

*** やりたいこと [#de855ec6]
- ToDo管理のためのRESTfulなAPIを作成する
- POST/GET/PUT/DELTE をそれぞれ CRUDに対応させる
- フォームはJSONで送る
-- @ModelAttributeではなく,@RequestBody で受けること

*** API仕様 [#y966b67b]
|操作|API呼び出し|戻り値|h
|ToDo作成|POST /api/{uid}/todos {"title": "タイトル"}|作成されたToDoオブジェクト|
|ToDo取得|GET /api/{uid}/todos/{seq}|指定したToDoオブジェクト|
|ToDo取得|GET /api/{uid}/todos |そのユーザのToDoリスト|
|ToDo取得|GET /api/{uid}/dones |そのユーザのDoneリスト|
|ToDo更新|PUT /api/{uid}/todos/{seq}/done|完了したToDoオブジェクト|
|ToDo更新|PUT /api/{uid}/todos/{seq} {"title":"変更するタイトル"}|更新されたToDoオブジェクト|
|ToDo削除|DELETE /api/{uid}/todos/{seq}|true|

*** RESTController - RESTコントローラ [#x3611e1a]
- controller/ToDoRestController

 package jp.ac.kobe_u.cs.itspecialist.todoapp.controller;
 
 import java.util.List;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.MethodArgumentNotValidException;
 import org.springframework.web.bind.annotation.DeleteMapping;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.PutMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 import jp.ac.kobe_u.cs.itspecialist.todoapp.dto.ToDoForm;
 import jp.ac.kobe_u.cs.itspecialist.todoapp.entity.ToDo;
 import jp.ac.kobe_u.cs.itspecialist.todoapp.exception.ToDoAppException;
 import jp.ac.kobe_u.cs.itspecialist.todoapp.service.MemberService;
 import jp.ac.kobe_u.cs.itspecialist.todoapp.service.ToDoService;
 
 /**
  * ToDoの操作,CRUDを行うAPI
  */
 @RestController
 @RequestMapping("/api")
 public class ToDoRestController {
     @Autowired
     ToDoService todoService;
     @Autowired
     MemberService memberService;
 
     /* --- C: ToDoを作成する --- */
     @PostMapping("/{mid}/todos")
     ToDo createToDo(@PathVariable String mid, @Validated @RequestBody ToDoForm form) {
         return todoService.createToDo(mid, form);
     }
 
     /* --- R: ToDoを取得する (1件) --- */
     @GetMapping("/{mid}/todos/{seq}")
     ToDo getToDoList(@PathVariable String mid, @PathVariable Long seq) {
         return todoService.getToDo(seq);
     }
 
     /* --- R: ToDoを取得する (リスト) --- */
     @GetMapping("/{mid}/todos")
     List<ToDo> getToDoList(@PathVariable String mid) {
         return todoService.getToDoList(mid);
     }
 
     /* --- R: Doneを取得する (リスト) --- */
     @GetMapping("/{mid}/dones")
     List<ToDo> getDoneList(@PathVariable String mid) {
         return todoService.getDoneList(mid);
     }
 
     /* --- U: ToDoを完了する --- */
     @PutMapping("/{mid}/todos/{seq}/done")
     ToDo done(@PathVariable String mid, @PathVariable Long seq) {
         return todoService.done(mid, seq);
     }
 
     /* --- U: ToDoを更新する --- */
     @PutMapping("/{mid}/todos/{seq}")
     ToDo updateToDo(@PathVariable String mid, @PathVariable Long seq, @Validated @RequestBody ToDoForm form) {
         return todoService.updateToDo(mid, seq, form);
     }
 
     /* --- D: ToDoを削除する --- */
     @DeleteMapping("/{mid}/todos/{seq}")
     boolean deleteToDo(@PathVariable String mid, @PathVariable Long seq) {
         todoService.deleteToDo(mid, seq);
         return true;
     }
 
     /*--------------------- エラーハンドラー -----------------------------*/
     /*
      * 本当は@RestControllerAdviceにまとめて書きたいが,@ControllerAdviceと競合するので,
      * Controllerに書いている.
      */
     @ExceptionHandler(ToDoAppException.class)
     public ResponseEntity<Object> handleToDoException(ToDoAppException ex) {
         HttpStatus status;
         switch (ex.getCode()) {
             // 存在しない系例外
             case ToDoAppException.NO_SUCH_MEMBER_EXISTS:
             case ToDoAppException.NO_SUCH_TODO_EXISTS:
                 status = HttpStatus.NOT_FOUND;
                 break;
             // パラメタ異常系例外
             case ToDoAppException.MEMBER_ALREADY_EXISTS:
             case ToDoAppException.INVALID_MEMBER_INFO:
             case ToDoAppException.INVALID_TODO_INFO:
                 status = HttpStatus.BAD_REQUEST;
                 break;
             // 認可失敗系例外
             case ToDoAppException.INVALID_MEMBER_OPERATION:
             case ToDoAppException.INVALID_TODO_OPERATION:
                 status = HttpStatus.FORBIDDEN;
                 break;
             default:
                 status = HttpStatus.INTERNAL_SERVER_ERROR;
         }
         return new ResponseEntity<>(ex, status);
     }
 
     /* -- バリデーション失敗 -- */
     @ExceptionHandler(MethodArgumentNotValidException.class)
     public ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex) {
         return new ResponseEntity<>(ex, HttpStatus.BAD_REQUEST);
     }
 
     /* -- その他の例外 -- */
     @ExceptionHandler(Exception.class)
     public ResponseEntity<Object> handleException(Exception ex) {
         return new ResponseEntity<>(ex, HttpStatus.BAD_REQUEST);
         return new ResponseEntity<>(ex, HttpStatus.INTERNAL_SERVER_ERROR);
     }
 
 }

解説
- RESTコントローラを定義するために,@RestControllerを付与する
- 各メソッドの中身は,サービスをそのままラップしているだけ
- フォームは@ModelAttributeではなく,@RequestBody で受ける
-- 
- バリデーションは同様に@Validated をつけるだけ
- メソッドの戻り値は,自動的にJSONオブジェクトに変換されて,HTTPレスポンスに返却される
- 例外処理を同じクラス内に書いている
-- 本来は,別クラスの@RestControllerAdviceを作りたいが,@ControllerAdviceと競合するため,こちらに書いている


** POSTMANで試す [#z665cb3b]
*** ToDoの取得 [#xb96c261]
GETでエンドポイントにリクエストする
- &attachref(api_get_todos.png);

*** ToDoの作成 [#t4bf8014]
POSTでエンドポイントにリクエストする
- フォームは,BODY->raw->JSONでJSON形式で渡す

- &attachref(api_post_todos.png);

*** ToDoの更新 [#ted2ec89]
自分で試してみよう

*** ToDoの削除 [#c4f31a13]
自分で試してみよう

トップ   編集 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS