แนะนำ CRUD / Request
เริ่มต้น CRUD
Ref: https://docs.gofiber.io/guide/routing
มาเริ่มต้นด้วยการเพิ่ม HTTP Method ของ API กัน โดยปกติ Fiber ได้เตรียม method ไว้ให้ใช้ตามนี้
// HTTP methods
func (app *App) Get(path string, handlers ...Handler) Router
func (app *App) Head(path string, handlers ...Handler) Router
func (app *App) Post(path string, handlers ...Handler) Router
func (app *App) Put(path string, handlers ...Handler) Router
func (app *App) Delete(path string, handlers ...Handler) Router
func (app *App) Connect(path string, handlers ...Handler) Router
func (app *App) Options(path string, handlers ...Handler) Router
func (app *App) Trace(path string, handlers ...Handler) Router
func (app *App) Patch(path string, handlers ...Handler) Router
// Add allows you to specifiy a method as value
func (app *App) Add(method, path string, handlers ...Handler) Router
// All will register the route on all HTTP methods
// Almost the same as app.Use but not bound to prefixes
func (app *App) All(path string, handlers ...Handler) Router
ในเริ่มต้นนี้เราจะ
- ทำ CRUD ซึ่งประกอบด้วย method ทั้งหมด 4 อันคือ GET, POST, PUT, DELETE
- ทำการเพิ่มข้อมูลหนังสือเข้า struct เก็บไว้ก่อน
- และทำการเรียกใช้ผ่าน struct ออกมาได้
** เนื่องจากเรายังไม่มี database = ตอนเราปิด process ไป ข้อมูลทั้งหมดจะหายไป
ซึ่ง function จะมีด้วยกันคือ
- getBooks = ดึงหนังสือทั้งหมด
- getBook = ดึงหนังสือตาม id
- createBook = สร้างหนังสือใหม่
- updateBook = แก้ไขข้อมูลหนังสือเดิมผ่าน id
- deleteBook = ลบหนังสือ
ซึ่งเราจะนำทั้งหมดไปประกอบกับ REST API ออกมา
package main
import (
"github.com/gofiber/fiber/v2"
"strconv"
)
// Book struct to hold book data
type Book struct {
ID int `json:"id"`
Title string `json:"title"`
Author string `json:"author"`
}
var books []Book // Slice to store books
func main() {
app := fiber.New()
// Initialize in-memory data
books = append(books, Book{ID: 1, Title: "1984", Author: "George Orwell"})
books = append(books, Book{ID: 2, Title: "The Great Gatsby", Author: "F. Scott Fitzgerald"})
// CRUD routes
app.Get("/book", getBooks)
app.Get("/book/:id", getBook)
app.Post("/book", createBook)
app.Put("/book/:id", updateBook)
app.Delete("/book/:id", deleteBook)
app.Listen(":8080")
}
// Handlers
func getBooks(c *fiber.Ctx) error {
return c.JSON(books)
}
func getBook(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
for _, book := range books {
if book.ID == id {
return c.JSON(book)
}
}
return c.SendStatus(fiber.StatusNotFound)
}
func createBook(c *fiber.Ctx) error {
book := new(Book)
if err := c.BodyParser(book); err != nil {
return c.Status(fiber.StatusBadRequest).SendString(err.Error())
}
book.ID = len(books) + 1
books = append(books, *book)
return c.JSON(book)
}
func updateBook(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
bookUpdate := new(Book)
if err := c.BodyParser(bookUpdate); err != nil {
return c.Status(fiber.StatusBadRequest).SendString(err.Error())
}
for i, book := range books {
if book.ID == id {
book.Title = bookUpdate.Title
book.Author = bookUpdate.Author
books[i] = book
return c.JSON(book)
}
}
return c.SendStatus(fiber.StatusNotFound)
}
func deleteBook(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
for i, book := range books {
if book.ID == id {
books = append(books[:i], books[i+1:]...)
return c.SendStatus(fiber.StatusNoContent)
}
}
return c.SendStatus(fiber.StatusNotFound)
}
ให้ข้อสังเกต 1 อย่าง
- ในการ return ของ function fiber Handler จะทำการ return เป็น error ออก ไป
- Fiber expected ผ่านการ return ว่า ค่า
error
ที่ออกมาต้องออกมาเป็น nil โดย- ถ้าเป็น success case = return nil ออกมา และ Fiber ก็จะทำการนำค่าที่ return ไปใช้ตามปกติ
- ถ้าเป็น fail = return error ออกไป และ Fiber จะสามารถตรวจจับ error และ handle error ออกไปเป็นแบบเคสเดียวกับ go ได้
แยก file Router
Note
- เราสามารถแยกไฟล์ออกจาก main.go ได้โดยสามารถแยกตาม package
- หรือจะแยกให้อยู่ใน main เหมือนกัน แต่คนละ file ก็ได้ (อยู่ directory เดียวกัน แต่ประกาศเป็น package main เหมือนกัน ก็จะสามารถเรียกหากันได้)
แยกเป็น
- main.go
package main
import (
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
// Setup routes
app.Get("/book", getBooks)
app.Get("/book/:id", getBook)
app.Post("/book", createBook)
app.Put("/book/:id", updateBook)
app.Delete("/book/:id", deleteBook)
app.Listen(":8080")
}
- book.go
package main
import (
"github.com/gofiber/fiber/v2"
"strconv"
)
// Book struct to hold book data
type Book struct {
ID int `json:"id"`
Title string `json:"title"`
Author string `json:"author"`
}
// Initialize in-memory data
var books []Book = []Book{
{ID: 1, Title: "1984", Author: "George Orwell"},
{ID: 2, Title: "The Great Gatsby", Author: "F. Scott Fitzgerald"},
}
// Handler functions
func getBooks(c *fiber.Ctx) error {
return c.JSON(books)
}
func getBook(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
for _, book := range books {
if book.ID == id {
return c.JSON(book)
}
}
return c.SendStatus(fiber.StatusNotFound)
}
func createBook(c *fiber.Ctx) error {
book := new(Book)
if err := c.BodyParser(book); err != nil {
return c.Status(fiber.StatusBadRequest).SendString(err.Error())
}
book.ID = len(books) + 1
books = append(books, *book)
return c.JSON(book)
}
func updateBook(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
updatedBook := new(Book)
if err := c.BodyParser(updatedBook); err != nil {
return c.Status(fiber.StatusBadRequest).SendString(err.Error())
}
for i, book := range books {
if book.ID == id {
book.Title = updatedBook.Title
book.Author = updatedBook.Author
books[i] = book
return c.JSON(book)
}
}
return c.SendStatus(fiber.StatusNotFound)
}
func deleteBook(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
for i, book := range books {
if book.ID == id {
books = append(books[:i], books[i+1:]...)
return c.SendStatus(fiber.StatusNoContent)
}
}
return c.SendStatus(fiber.StatusNotFound)
}
เวลา run command ให้เปลี่ยนจากการ run ราย file เป็น run ทุกไฟล์แทน
# run แบบปกติ
go run *.go
go run .
# run แบบ build
go build
เปิด CORS
- กรณีอยากทดสอบต่อกับ Frontend = สามารถเปิด CORS (เพิ่ม config เข้าไปได้)
- library fiber มี support เรื่อง CORS อยู ่แล้ว
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
)
func main() {
app := fiber.New()
// Apply CORS middleware
app.Use(cors.New(cors.Config{
AllowOrigins: "*", // Adjust this to be more restrictive if needed
AllowMethods: "GET,POST,HEAD,PUT,DELETE,PATCH",
AllowHeaders: "Origin, Content-Type, Accept",
}))
// Setup routes
app.Get("/book", getBooks)
app.Get("/book/:id", getBook)
app.Post("/book", createBook)
app.Put("/book/:id", updateBook)
app.Delete("/book/:id", deleteBook)
app.Listen(":8080")
}
upload file
Note
- ลองมารับ file ด้วยรูปแบบอื่นนอกจาก JSON กันบ้าง เปลี่ยนมารับไฟล์จาก FormData แทน
- เช่นเคสนี้ เราจะลอง upload ภาพเข้ามา > แล้วนำภาพมา upload ลง directory เอาไว้
package main
import (
"github.com/gofiber/fiber/v2"
)
func uploadImage(c *fiber.Ctx) error {
// Read file from request
file, err := c.FormFile("image")
if err != nil {
return c.Status(fiber.StatusBadRequest).SendString(err.Error())
}
// Save the file to the server
err = c.SaveFile(file, "./uploads/" + file.Filename)
if err != nil {
return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
}
return c.SendString("File uploaded successfully: " + file.Filename)
}
func main() {
app := fiber.New()
// Setup route
app.Post("/upload", uploadImage)
app.Listen(":8080")
}