ช่วงนี้ผู้เขียนกำลังเริ่มเรียนรู้เกี่ยวกับ Golang มาสักระยะ วันนี้ก็เลยอยากจะพามาลงบทเรียนการเริ่มพัฒนา Service API ด้วยภาษา Go โดยบทความนี้จะขอ Coding โดยใช้ Echo Framework สำเร็จเพื่อรวดเร็วในการพัฒนา Web Service API มาเริ่มกันเลย
เตรียมความพร้อมก่อน
- ต้องการ Go runtime เวอร์ชั่น v1.13 หรือมากกว่า สำหรับ Echo v.4
- IDE หรือ Editor (VScode,Sublime,Atom) แล้วแต่ความถนัด (Vscode ติดตั้ง Extensions Golang)
- Postman IDE หรือ Terminal ตามความถนัด เช่นกัน
เริ่มโค๊ดกันเลย
- initial go project
go mod init poolsawat.com-echo-crud
- download echo framework libs
go get github.com/labstack/echo/v4
go get github.com/labstack/echo/v4/[email protected]
poolsawat.api@AGC-FVFCR1UWP3YV poolsawat.com-echo-crud % go get github.com/labstack/echo/v4
go: added github.com/labstack/echo/v4 v4.9.0
go: added github.com/labstack/gommon v0.3.1
go: added github.com/mattn/go-colorable v0.1.11
go: added github.com/mattn/go-isatty v0.0.14
go: added github.com/valyala/bytebufferpool v1.0.0
go: added github.com/valyala/fasttemplate v1.2.1
go: added golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
go: added golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f
go: added golang.org/x/sys v0.0.0-20211103235746-7861aae1554b
go: added golang.org/x/text v0.3.7
- สร้าง models/user.go
package models
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
- สร้าง routes/user.go
package routes
import (
"net/http"
"strconv"
"github.com/labstack/echo/v4"
"poolsawat.com-echo-crud/models"
)
var (
seq = 1
)
type RouteUserHandler struct {
Users map[int]*models.User
}
//----------
// Handlers
//----------
func (h *RouteUserHandler) CreateUser(c echo.Context) error {
u := &models.User{
ID: seq,
}
if err := c.Bind(u); err != nil {
return err
}
h.Users[u.ID] = u
seq++
return c.JSON(http.StatusCreated, u)
}
func (h *RouteUserHandler) GetUser(c echo.Context) error {
id, _ := strconv.Atoi(c.Param("id"))
return c.JSON(http.StatusOK, h.Users[id])
}
func (h *RouteUserHandler) UpdateUser(c echo.Context) error {
u := new(models.User)
if err := c.Bind(u); err != nil {
return err
}
id, _ := strconv.Atoi(c.Param("id"))
h.Users[id].Name = u.Name
return c.JSON(http.StatusOK, h.Users[id])
}
func (h *RouteUserHandler) DeleteUser(c echo.Context) error {
id, _ := strconv.Atoi(c.Param("id"))
delete(h.Users, id)
return c.NoContent(http.StatusNoContent)
}
func (h *RouteUserHandler) GetAllUsers(c echo.Context) error {
return c.JSON(http.StatusOK, h.Users)
}
- สร้าง server.go file กำหนดเป็น main package
package main
import (
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"poolsawat.com-echo-crud/models"
"poolsawat.com-echo-crud/routes"
)
func main() {
e := echo.New()
// Middleware
e.Use(middleware.Logger())
e.Use(middleware.Recover())
users := map[int]*models.User{}
route := &routes.RouteUserHandler{users}
// Routes
e.GET("/users", route.GetAllUsers)
e.POST("/users", route.CreateUser)
e.GET("/users/:id", route.GetUser)
e.PUT("/users/:id", route.UpdateUser)
e.DELETE("/users/:id", route.DeleteUser)
// Start server
e.Logger.Fatal(e.Start(":1323"))
}
- ทดสอบ Service APIs ทั้งหมด
- [POST] /users
curl -X POST \
-H 'Content-Type: application/json' \
-d '{"name":"Poolsawat.com 001"}' \
localhost:1323/users
- [GET] /users
curl localhost:1323/users
- [GET] /users/1
curl localhost:1323/users/1
- [PUT] /users/1
curl -X PUT \
-H 'Content-Type: application/json' \
-d '{"name":"Poolsawat.com 001 Updated"}' \
localhost:1323/users/1
- [DELETE] /users/1
curl -X DELETE localhost:1323/users/1
- สุดท้ายอย่าลืมเขียน Test routes/user_test.go
package routes_test
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"poolsawat.com-echo-crud/routes"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
"poolsawat.com-echo-crud/models"
)
func TestUsers(t *testing.T) {
var (
mockDB = map[int]*models.User{
1: &models.User{1, "Poolsawat.com"},
}
extectedOne = "{\"id\":1,\"name\":\"Poolsawat.com\"}"
extectedAll = "{\"1\":{\"id\":1,\"name\":\"Poolsawat.com\"}}"
)
t.Run(`should created user success`, func(t *testing.T) {
// Setup
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/users", strings.NewReader(extectedOne))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
mockRoute := &routes.RouteUserHandler{mockDB}
// Assertions
if assert.NoError(t, mockRoute.CreateUser(c)) {
assert.Equal(t, http.StatusCreated, rec.Code)
assert.Equal(t, extectedOne, strings.TrimSpace(rec.Body.String()))
}
})
t.Run(`should get all users success`, func(t *testing.T) {
// Setup
e := echo.New()
req := httptest.NewRequest(http.MethodGet, "/users", nil)
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
mockRoute := &routes.RouteUserHandler{mockDB}
// Assertions
if assert.NoError(t, mockRoute.GetAllUsers(c)) {
assert.Equal(t, http.StatusOK, rec.Code)
assert.Equal(t, extectedAll, strings.TrimSpace(rec.Body.String()))
}
})
t.Run(`should get user by id success`, func(t *testing.T) {
// Setup
e := echo.New()
req := httptest.NewRequest(http.MethodGet, "/users", nil)
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
c.SetPath("/users/:id")
c.SetParamNames("id")
c.SetParamValues("1")
mockRoute := &routes.RouteUserHandler{mockDB}
// Assertions
if assert.NoError(t, mockRoute.GetUser(c)) {
assert.Equal(t, http.StatusOK, rec.Code)
assert.Equal(t, extectedOne, strings.TrimSpace(rec.Body.String()))
}
})
t.Run(`should update user success`, func(t *testing.T) {
// Given
expected := "{\"id\":1,\"name\":\"Poolsawat.com update\"}"
// Setup
e := echo.New()
req := httptest.NewRequest(http.MethodPut, "/users", strings.NewReader(expected))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
c.SetPath("/users/:id")
c.SetParamNames("id")
c.SetParamValues("1")
mockRoute := &routes.RouteUserHandler{mockDB}
// Assertions
if assert.NoError(t, mockRoute.UpdateUser(c)) {
assert.Equal(t, http.StatusOK, rec.Code)
assert.Equal(t, expected, strings.TrimSpace(rec.Body.String()))
}
})
t.Run(`should delete user by id success`, func(t *testing.T) {
// Setup
e := echo.New()
req := httptest.NewRequest(http.MethodDelete, "/users", nil)
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
c.SetPath("/users/:id")
c.SetParamNames("id")
c.SetParamValues("1")
mockRoute := &routes.RouteUserHandler{mockDB}
// Assertions
if assert.NoError(t, mockRoute.DeleteUser(c)) {
assert.Equal(t, http.StatusNoContent, rec.Code)
assert.Equal(t, "", strings.TrimSpace(rec.Body.String()))
}
})
}
- รัน test ด้วย
go test ./routes/user.go ./routes/user_test.go -v
ขอบคุณที่ติดตามครับ