เนื่องจาก Go 1.18 เริ่มใช้ generic แล้ว ซึ่งใน block ก่อนหน้านี้ ผมก็นำเอา blog ต้นฉบับของ contributors มาแปลคร่าวๆให้ได้อ่านกัน ทำให้ได้รู้ usecases ของการใช้ generic ว่าปกติจะใช้กับ ฟังก์ชั่นที่ ทำงานกับ map, slice หรือ channel เป็นหลัก และเอาจริงๆ หลายๆ usecases ที่คิดกันออกมา มันอาจจะทำงานกับ interface ได้ดีกว่า ที่จะพยายามยัดเยียดเอา generic ใส่เข้าไปเพื่อความเท่
วันนี้ก็เลยลองใช้ generic มาช่วยในการสร้าง gin HandlerFunc เพื่อให้เราสามารถเขียน handler ได้ง่ายขึ้นด้วยหน้าตาประมาณนี้
func Handler(request) (response,error)
ประโยชน์ก็คือ มันจะช่วยให้เราเขียนเทสให้ handler ง่ายขึ้น โดยไม่ต้อง mock gin.Context
โดยเราจะวางโครงสร้างโปรเจ็คเราแบบนี้
- domain ภายในนี้จะเป็นที่รวม business logic ทั้งหมด
- ginhandlerfunc เป็น converter ที่จะช่วยแปลง handler แบบที่เราอยากได้ ไปเป็น gin.HandlerFunc
เรามาดูหน้าตาของ handler ที่ทำเสร็จแล้วกันก่อนว่าจะมีหน้าตาแบบไหน
type CustomerRequest struct {
Name string `json:"name"`
}
type CustomerResponse struct {
Name string `json:"name"`
}
func CustomerHandler(request CustomerRequest) (CustomerResponse, error) {
fmt.Println("CustomerHandler", request)
return CustomerResponse{Name: request.Name}, nil
}
func New() gin.HandlerFunc {
return ginhandlerfunc.NewBindRequestOKResponse(CustomerHandler)
}
ซึ่งเมื่อได้ code หน้าตาแบบนี้แล้ว เวลาเราจะเขียน business logic ก็เขียนแค่ใน CustomerHandler ได้เลย
ทีนี้เราลองมาดูใน ginhandlerfunc.NewBindRequestOKResponse กันว่ามีวิธีเขียนอย่างไร
func NewBindRequestOKResponse[Request, Response any](handler func(Request) (Response, error)) gin.HandlerFunc {
return func(c *gin.Context) {
var request Request
if err := c.ShouldBindJSON(&request); err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
response, err := handler(request)
if err != nil {
c.AbortWithError(http.StatusInternalServerError, err)
return
}
c.JSON(http.StatusOK, response)
}
}
นี่ก็คือหน้าตาของมัน โดยเราใช้ generic เข้ามาช่วยในส่วนการประกาศ Request และ Response ให้ ซึ่งวิธีนี้จะทำไม่ได้หากไม่มีการใช้ generic เข้ามาช่วย
และสามารถไปดู ตัวอย่างที่มากกว่านี้ได้ที่ https://github.com/pallat/generic_gin_handler
ซึ่งจะมีตัวอย่างของ handler ที่รับ parameter ผ่าน URI Param และการสร้าง handler struct มาเป็น handler ของเราในกรณีที่ต้องมี dependencies อื่นๆ
นี่เป็นเพียงแค่โค้ดตัวอย่างเบื้องต้น หวังว่าจะเป็นประโยชน์ ไม่ว่าจะนำไปใช้ หรือสร้างแรงบันดาลใจให้เขียนท่าอื่นๆที่เหมาะสมกับสไตล์ของตัวเองกันนะครับ
Top comments (0)