ข้ามไปยังเนื้อหา

Binding และ Validation

ใน Express คุณเข้าถึง req.body (หลังจาก express.json()) ส่วนใน Echo c.Bind(&target) ทำงานแบบเดียวกัน: อ่าน Content-Type header แล้ว decode body เข้า struct ที่ส่งไป และยัง bind path param กับ query param ได้ด้วยถ้า field ใน struct มี tag ที่ถูกต้อง

TypeScript
// Express + TypeScript
interface CreateBookDTO {
title: string;
author: string;
year: number;
}
app.post('/books', (req, res) => {
const body = req.body as CreateBookDTO;
// body.title, body.author, body.year พร้อมใช้
res.status(201).json(body);
});
Go
// Echo
type CreateBookRequest struct {
Title string `json:"title"`
Author string `json:"author"`
Year int `json:"year"`
}
func createBook(c echo.Context) error {
var req CreateBookRequest
if err := c.Bind(&req); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
// req.Title, req.Author, req.Year พร้อมใช้
return c.JSON(http.StatusCreated, req)
}

Struct tag คือ string literal ที่แนบกับ field ทำงานคล้าย TypeScript decorator แต่ไม่ได้รันโค้ด — โค้ดจะอ่าน tag ตอน runtime ผ่าน reflection ตัว json:"name" คุมเรื่อง serialization ส่วน validate:"rule" ให้ validator library อ่านไปใช้

TypeScript
// TypeScript class-validator
import { IsString, IsInt, Min, IsNotEmpty } from 'class-validator';
class CreateBookDTO {
@IsString()
@IsNotEmpty()
title: string;
@IsString()
author: string;
@IsInt()
@Min(1000)
year: number;
}
Go
// Go struct tag
type CreateBookRequest struct {
Title string `json:"title" validate:"required"`
Author string `json:"author" validate:"required"`
Year int `json:"year" validate:"required,min=1000"`
}

Package encoding/json ของ stdlib อ่าน json:"..." tag ระหว่าง Marshal/Unmarshal รันได้ใน playground:

package main
import (
"encoding/json"
"fmt"
)
type Book struct {
Title string `json:"title"`
Author string `json:"author"`
Year int `json:"year"`
// omitempty: field ถูกละเว้นจาก JSON เมื่อเป็น zero/empty
ISBN string `json:"isbn,omitempty"`
}
func main() {
// Unmarshal (JSON -> struct)
raw := `{"title":"The Go Programming Language","author":"Donovan & Kernighan","year":2015}`
var b Book
if err := json.Unmarshal([]byte(raw), &b); err != nil {
fmt.Println("error:", err)
return
}
fmt.Printf("Title: %s\n", b.Title)
fmt.Printf("Author: %s\n", b.Author)
fmt.Printf("Year: %d\n", b.Year)
fmt.Printf("ISBN: %q (empty — ละเว้นใน output)\n", b.ISBN)
// Marshal (struct -> JSON)
out, _ := json.MarshalIndent(b, "", " ")
fmt.Println(string(out))
}

Echo ไม่มี validation มาให้ในตัว แต่เปิด e.Validator ไว้ให้เสียบ validator เอง วิธีมาตรฐานคือเสียบ go-playground/validator เข้าไป

TypeScript
// NestJS global validation pipe
import { ValidationPipe } from '@nestjs/common';
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
// DTO
class CreateBookDTO {
@IsString() @IsNotEmpty() title: string;
@IsInt() @Min(1000) year: number;
}
Go
// เชื่อม validator ครั้งเดียวใน main.go
import "github.com/go-playground/validator/v10"
type CustomValidator struct {
v *validator.Validate
}
func (cv *CustomValidator) Validate(i any) error {
return cv.v.Struct(i)
}
// ใน main():
e.Validator = &CustomValidator{v: validator.New()}
// Handler — bind แล้ว validate
func createBook(c echo.Context) error {
var req CreateBookRequest
if err := c.Bind(&req); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
if err := c.Validate(req); err != nil {
return echo.NewHTTPError(http.StatusUnprocessableEntity, err.Error())
}
// req พร้อมใช้งานอย่างปลอดภัย
return c.JSON(http.StatusCreated, req)
}
method ไหนของ Echo ที่ decode JSON request body เข้า struct?
กลไกใดของ Go ที่ library เช่น encoding/json และ go-playground/validator ใช้อ่าน metadata ของ field?
json:"isbn,omitempty" tag ทำหน้าที่อะไร?