iT邦幫忙

2025 iThome 鐵人賽

DAY 11
0

理解 Gin 框架的 API 實作之後,今天就要來完成 To-do List 的「 新增 、讀取、更新、 刪除 」 (CRUD:Create、Read、Update、Delete)功能!

先來介紹為什麼要做 To-do List 好了~ 😆


是這樣的,當初在想專案要做什麼的時候,希望是不要太複雜,但是又可以實作後端很常用到的功能,所以就想到可以做一個類似備忘錄的小工具!這樣 CRUD 通通有用到,哈哈 🤣

To-do List 小工具功能與初步規劃:

  • Create:新增任務 (POST /tasks)。
  • Read:查詢所有任務 (GET /tasks)。
  • Update:更新任務狀態 (PATCH /task/:id)。
  • Delete:刪除任務 (DELETE /task/:id)。

上面這四個功能就是所有的 API 了!作法一樣是會先一個一個把各自的 function 寫好,然後透過 main fuction 呼叫。

那我們就開始吧!
/images/emoticon/emoticon33.gif

先建立 Task struct 和「 放任務的地方 」:

// 完成 Task struct 和 放 task 的地方 []

type Task struct {                   // Task 結構
	ID   int    `json:"id"`
	Item string `json:"item"`
	Status bool `json:"status"`
}

var tasks = []Task{                   // 放置所有任務的空間
	{ID: 1, Title: "範例一", Status: false},
	{ID: 2, Title: "範例二", Status: false},
}

再來,就是把新增、讀取、更新、刪除任務的 function 完成:

  1. Create:新增任務 (POST /tasks)。

    // POST /tasks
    func addTask(c *gin.Context) {
    	var newTask Task
    
    	if err := c.ShouldBindJSON(&newTask); err != nil {
    		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    		return
    	}
    
    	newTask.ID = len(tasks) + 1
    	newTask.Status = false
    	tasks = append(tasks, newTask)
    	c.JSON(http.StatusOK, newTask)
    }
    
  2. Read:查詢所有任務 (GET /tasks)。

    func getTasks(c *gin.Context) {
    	c.JSON(http.StatusOK, tasks)
    }
    
  3. Update:更新任務狀態 (PATCH /task/:id)。

    // PATCH /task/:id
    
    func updateTask(c *gin.Context) {
    	get_id := c.Param("id")
    	fmt.Println("id:", get_id)
    
    	id, err := strconv.Atoi(get_id)
    	if err != nil {
    		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
    		return
    	}
    
    	var input struct {
    		Item   *string `json:"item"`
    		Status *bool   `json:"status"`
    	}
    	fmt.Println("input:", input)
    
    	if err := c.ShouldBindJSON(&input); err != nil {
    		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    		return
    	}
    
    	for i, t := range tasks {
    		if t.ID == id {
    			if input.Item != nil {
    				tasks[i].Item = *input.Item
    			}
    			if input.Status != nil {
    				tasks[i].Status = *input.Status
    			}
    			c.JSON(http.StatusOK, tasks[i])
    			return
    		}
    	}
    	c.JSON(http.StatusNotFound, gin.H{"error": "Task not found"})
    }
    
  4. Delete:刪除任務 (DELETE /task/:id)。

    // DELETE /task/:id
    
    func deleteTask(c *gin.Context) {
    	get_id := c.Param("id")
    	fmt.Println("id:", get_id)
    
    	id, err := strconv.Atoi(get_id)
    	if err != nil {
    		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
    		return
    	}
    
    	for i, t := range tasks {
    		if t.ID == id {
    			tasks = append(tasks[:i], tasks[i+1:]...)
    			c.JSON(http.StatusOK, gin.H{"message": "Delete successfully"})
    			return
    		}
    	}
    	c.JSON(http.StatusNotFound, gin.H{"error": "Not found"})
    }
    

完整 main.go 內容:

//完整 main.go

package main
import (
	"fmt"
	"net/http"
	"strconv"
	"github.com/gin-gonic/gin"
)

type Task struct {
	ID     int    `json:"id"`
	Item   string `json:"item"`
	Status bool   `json:"status"`
}

var tasks = []Task{
	{ID: 1, Item: "鐵人賽文章", Status: false},
}

func main() {
	r := gin.Default()
	
	r.GET("/tasks", getTasks)
	r.POST("/tasks", addTask)
	r.PATCH("/task/:id", updateTask)
	r.DELETE("/task/:id", deleteTask)
	r.Run(":8080")
}

// GET /tasks
func getTasks(c *gin.Context) {
	c.JSON(http.StatusOK, tasks)
}

// POST /tasks
func addTask(c *gin.Context) {
	var newTask Task

	if err := c.ShouldBindJSON(&newTask); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	newTask.ID = len(tasks) + 1
	newTask.Status = false
	tasks = append(tasks, newTask)
	c.JSON(http.StatusOK, newTask)
}

// PATCH /task/:id
func updateTask(c *gin.Context) {
	get_id := c.Param("id")
	fmt.Println("id:", get_id)

	id, err := strconv.Atoi(get_id)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
		return
	}

	var input struct {
		Item   *string `json:"item"`
		Status *bool   `json:"status"`
	}
	fmt.Println("input:", input)

	if err := c.ShouldBindJSON(&input); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	for i, t := range tasks {
		if t.ID == id {
			if input.Item != nil {
				tasks[i].Item = *input.Item
			}
			if input.Status != nil {
				tasks[i].Status = *input.Status
			}
			c.JSON(http.StatusOK, tasks[i])
			return
		}
	}
	c.JSON(http.StatusNotFound, gin.H{"error": "Task not found"})
}

// DELETE /task/:id
func deleteTask(c *gin.Context) {
	get_id := c.Param("id")
	fmt.Println("id:", get_id)

	id, err := strconv.Atoi(get_id)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
		return
	}

	for i, t := range tasks {
		if t.ID == id {
			tasks = append(tasks[:i], tasks[i+1:]...)
			c.JSON(http.StatusOK, gin.H{"message": "Delete successfully"})
			return
		}
	}
	c.JSON(http.StatusNotFound, gin.H{"error": "Task not found"})
}

測試 API

完成之後,就來測試 API 吧!
我們一樣使用 終端機(cmd)來試打 API,看看它的回傳結果以及功能是不是沒有問題的。
要記得先開啟 server 才有辦法測試喔~

  1. 查詢任務(GET /tasks

    curl http://localhost:8080/tasks
    

    回傳:
    https://ithelp.ithome.com.tw/upload/images/20250925/201782237kXXxLXcys.png

  2. 新增任務(POST /tasks

    curl -X POST http://localhost:8080/tasks \
      -H "Content-Type: application/json" \
      -d '{"item":"參加 IT 鐵人賽"}'
    

    回傳:
    https://ithelp.ithome.com.tw/upload/images/20250925/20178223jANOAGkZCA.png

  3. 修改任務(PATCH /task/:id

    curl -X PATCH http://localhost:8080/task/1 \
      -H "Content-Type: application/json" \
      -d '{"status": true}'
    

    回傳:
    https://ithelp.ithome.com.tw/upload/images/20250925/20178223gsVcwkUOcM.png

    這時候會發現 id:1 的 status 已經被我們改成 true 了!

  4. 刪除任務(DELETE /task/:id

    curl -X DELETE http://localhost:8080/tasks/2
    

    回傳:
    https://ithelp.ithome.com.tw/upload/images/20250925/20178223I6bryH3YWj.png

這樣就全部的功能都有測試過一次了,算是完成基本 To-do List 初版了!!!🎉🎉🎉
是不是沒有想像中的困難呢~
明天會介紹 RESTful API 的設計原則 ✨ 再來看看我們今天設計的 API 有沒有符合設計規範~


上一篇
Day10 - 用 Gin 框架建立 API
下一篇
Day12 - RESTful 設計:檢視 To-do List API 規劃
系列文
Go,一起成為全端吧!—— 給前端工程師的 Golang 後端學習筆記12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言