#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] 自分で試してみよう