生活的秘訣,就是把自己放在適合的光線下。有些人需要百老匯的聚光燈,而有些人只需要一張被小檯燈照亮的桌子。 -- 蘇珊.坎恩 《安靜就是力量》
之前我們利用 Java thymeleaf 模版引擎完成Todo List,是屬於後端渲染(Server-Side Render)到前端 HTML CSS ,而接下來因為想與大家介紹前後端分離與RESTful API,今天要來實作Todo List的 RESTFul API 讓其他軟體(App, Web)介接。
API 端點為
採取三層式架構
介面層(Controller) 接收前端請求
業務邏輯層(Service):根據請求做資料處理或是處理從DAO回來的資料。
資料訪問層(Dao):對資料庫做增修查改等操作。
package com.caili.todolist.model.entity;
import lombok.Data;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import javax.persistence.*;
import java.util.Date;
/**
* @author cai-li
*/
@Entity
@Table
@Data
public class Todo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Integer id;
@Column
String task = "";
@Column(insertable = false, columnDefinition = "int default 1")
Integer status = 1;
@CreatedDate
@Column(updatable = false, nullable = false)
Date createTime = new Date();
@LastModifiedDate
@Column(nullable = false)
Date updateTime = new Date();
}
import com.caili.todolist.model.entity.Todo;
import org.springframework.data.repository.CrudRepository;
public interface TodoDao extends CrudRepository<Todo, Integer> {
}
分別實作getTodos()
、createTodo()
、updateTodo()
與deleteTodo()
方法。
getTodos()
純粹透過todoDao取得資料庫的所有代辦事項。
createTodo()
透過todoDao將資料儲存至資料庫,並回傳id。
updateTodo()
一開始會透過findById()
判斷id 是否存在資料庫,也會判斷傳進來的值否有無status
這個key,若沒有就會直接回傳false
,再來就會經由todoDao將資料更新至資料庫
deleteTodo()
一開始會透過findById()
判斷id 是否存在資料庫,若不存在直接回傳false,再來就會經由todoDao將資料從資料庫中刪除。
package com.caili.todolist.service;
import com.caili.todolist.model.dao.TodoDao;
import com.caili.todolist.model.entity.Todo;
import org.hibernate.service.spi.ServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class TodoService {
@Autowired
TodoDao todoDao;
public Iterable<Todo> getTodos() {
return todoDao.findAll();
}
public Integer createTodo(Todo todo) {
Todo rltTodo = todoDao.save(todo);
return rltTodo.getId();
}
public Boolean updateTodo(Integer id,Todo todo) {
Optional<Todo> isExistTodo = findById(id);
if (! isExistTodo.isPresent()) {
return false;
}
Todo newTodo = isExistTodo.get();
if (todo.getStatus() == null) {
return false;
}
newTodo.setStatus(todo.getStatus());
todoDao.save(newTodo);
return true;
}
public Optional<Todo> findById(Integer id) {
Optional<Todo> todo = todoDao.findById(id);
return todo;
}
public Boolean deleteTodo(Integer id) {
Optional<Todo> findTodo = findById(id);
if (!findTodo.isPresent()) {
return false;
}
todoDao.deleteById(id);
return true;
}
}
Controller 一開始會用@RestController
Annotation來直接回傳json,並用@RequestMapping("/api")
定義此API的父路徑,也就是說我們呼叫API要/api/xxxx
的呼叫。
Controller實作了CRUD的API呼叫,分別為GET/api/todos
、 POST/api/todos
、PUT/api/todos/{id}
、DELETE /api/todos/{id}
請求,這裡去透過service來操作資料的處理,並回傳相對應的結果。
我們可以使用ResponseEntity
這個類別操作靜態方法status(HttpStatus status)
來設定回傳的status code,與靜態方法body()
來設定要回傳的response body。可以參考Class ResponseEntity 的文件。
GET/api/todos
取得所有資料回傳status code 200 (OK)與所有資料集。
POST/api/todos
建立待辦事項資料,並回傳status code 201(Created),並回傳id。
PUT/api/todos/{id}
修改代辦事項的狀態,成功的話回傳status code 200(OK),失敗則回傳status code 400(Bad Request)
DELETE /api/todos/{id}
刪除待辦事項,成功刪除回傳status code 204(No Content),失敗則回傳status code 400(Bad Request)
package com.caili.todolist.controller;
import com.caili.todolist.model.entity.Todo;
import com.caili.todolist.service.TodoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Optional;
@RestController
@RequestMapping("/api")
public class TodoController {
@Autowired
TodoService todoService;
@GetMapping("/todos")
public ResponseEntity getTodos() {
Iterable<Todo> todoList = todoService.getTodos();
return ResponseEntity.status(HttpStatus.OK).body(todoList);
}
@GetMapping("/todos/{id}")
public Optional<Todo> getTodo(@PathVariable Integer id) {
Optional<Todo> todo = todoService.findById(id);
return todo;
}
@PostMapping("/todos")
public ResponseEntity createTodo(@RequestBody Todo todo) {
Integer rlt = todoService.createTodo(todo);
return ResponseEntity.status(HttpStatus.CREATED).body(rlt);
}
@PutMapping("/todos/{id}")
public ResponseEntity upadteTodo(@PathVariable Integer id, @RequestBody Todo todo) {
Boolean rlt = todoService.updateTodo(id ,todo);
if (!rlt) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Status 欄位不能為空");
}
return ResponseEntity.status(HttpStatus.OK).body("");
}
@DeleteMapping("/todos/{id}")
public ResponseEntity deleteTodo(@PathVariable Integer id) {
Boolean rlt = todoService.deleteTodo(id);
if (!rlt) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Id 不存在");
}
return ResponseEntity.status(HttpStatus.NO_CONTENT).body("");
}
}
最後我們使用Postman 來測試API是否成功,Postman 可以用來模擬HTTP request 的工具,快速測試你的api是否運作成功,正常的請求資料。
GET /todost 查詢代辦事項列表
POST /todos 新增代辦事項
PUT /todos/{id} 修改代辦事項狀態
DELETE /todos/{id} 刪除代辦事項