Validation
Setiap payload request yang masuk harus divalidasi
Kita akan menggunakan validator v.9
Setiap struct di payload request ditambahkan tag validator
package request
import (
"essentials/models"
"strconv"
)
// NewUserRequest : format json request for new user
type NewUserRequest struct {
Username string `json:"username" validate:"required"`
Email string `json:"email" validate:"required"`
Password string `json:"password" validate:"required"`
RePassword string `json:"re_password" validate:"required"`
}
// Transform NewUserRequest to User
func (u *NewUserRequest) Transform() *models.User {
var user models.User
user.Username = u.Username
user.Email = u.Email
user.Password = u.Password
return &user
}
// UserRequest : format json request for update user
type UserRequest struct {
ID uint64 `json:"id" validate:"required"`
IsActive string `json:"is_active"`
}
// Transform UserRequest to User
func (u *UserRequest) Transform(user *models.User) *models.User {
if u.ID == user.ID {
if len(u.IsActive) > 0 {
user.IsActive, _ = strconv.ParseBool(u.IsActive)
}
}
return user
}
Pengecekan bisa dilakukan secara generic di helper request libaries/api/request.go
package api
import (
"encoding/json"
"errors"
"net/http"
"gopkg.in/go-playground/validator.v9"
)
var validate *validator.Validate
// Decode reads the body of an HTTP request looking for a JSON document. The
// body is decoded into the provided value.
func Decode(r *http.Request, val interface{}) error {
if err := json.NewDecoder(r.Body).Decode(val); err != nil {
return ErrBadRequest(err, "")
}
validate = validator.New()
if err := validate.Struct(val); err != nil {
if _, ok := err.(*validator.InvalidValidationError); ok {
return err
}
for _, verr := range err.(validator.ValidationErrors) {
err = errors.New(verr.Field() + " is " + verr.Tag())
break
}
if err != nil {
return ErrBadRequest(err, err.Error())
}
}
return nil
}
Adakalanya kita ingin validasi dilakukan secara khusus di suatu payload request. Untuk itu kita akan membuat validasi di helper request adalah optional.
package api
import (
"encoding/json"
"errors"
"net/http"
"gopkg.in/go-playground/validator.v9"
)
// Decode reads the body of an HTTP request looking for a JSON document. The
// body is decoded into the provided value.
func Decode(r *http.Request, val interface{}, mustValidate bool) error {
if err := json.NewDecoder(r.Body).Decode(val); err != nil {
return ErrBadRequest(err, "")
}
if mustValidate {
validate := validator.New()
if err := validate.Struct(val); err != nil {
if _, ok := err.(*validator.InvalidValidationError); ok {
return err
}
for _, verr := range err.(validator.ValidationErrors) {
err = errors.New(verr.Field() + " is " + verr.Tag())
break
}
if err != nil {
return ErrBadRequest(err, err.Error())
}
}
}
return nil
}
Kemudian kita tambahkan validasi khusus di payload request yang membutuhkan validasi khusus.
package request
import (
"errors"
"essentials/libraries/api"
"essentials/models"
"strconv"
"gopkg.in/go-playground/validator.v9"
)
// NewUserRequest : format json request for new user
type NewUserRequest struct {
Username string `json:"username" validate:"required"`
Email string `json:"email" validate:"required"`
Password string `json:"password" validate:"required"`
RePassword string `json:"re_password" validate:"required"`
}
// Transform NewUserRequest to User
func (u *NewUserRequest) Transform() *models.User {
var user models.User
user.Username = u.Username
user.Email = u.Email
user.Password = u.Password
return &user
}
// Validate NewUserRequest
func (u *NewUserRequest) Validate() error {
validate := validator.New()
if err := validate.Struct(u); err != nil {
if _, ok := err.(*validator.InvalidValidationError); ok {
return err
}
for _, verr := range err.(validator.ValidationErrors) {
err = errors.New(verr.Field() + " is " + verr.Tag())
break
}
if err != nil {
return api.ErrBadRequest(err, err.Error())
}
}
return nil
}
// UserRequest : format json request for update user
type UserRequest struct {
ID uint64 `json:"id" validate:"required"`
IsActive string `json:"is_active"`
}
// Transform UserRequest to User
func (u *UserRequest) Transform(user *models.User) *models.User {
if u.ID == user.ID {
if len(u.IsActive) > 0 {
user.IsActive, _ = strconv.ParseBool(u.IsActive)
}
}
return user
}
Selanjutnya setiap call api.Decode() perlu memberitahu apakah akan menggunakan validasi global atau tidak dengan melempar parameter boolean musValidate. Dan mungkin perlu memanggil fungsi Validate khusus
userRequest.Validate()
Berikut contoh perubahan di file usecases/user_usecase.go
package usecases
import (
"database/sql"
"errors"
"essentials/libraries/api"
"essentials/payloads/request"
"essentials/payloads/response"
"log"
"net/http"
"golang.org/x/crypto/bcrypt"
)
// UserUsecase struct
type UserUsecase struct {
Log *log.Logger
Db *sql.DB
}
// Create new user
func (u *UserUsecase) Create(r *http.Request) (response.UserResponse, error) {
var userRequest request.NewUserRequest
var res response.UserResponse
err := api.Decode(r, &userRequest, false)
if err != nil {
u.Log.Printf("error decode user: %s", err)
return res, err
}
err = userRequest.Validate()
if err != nil {
u.Log.Printf("validate new user: %s", err)
return res, err
}
if userRequest.Password != userRequest.RePassword {
err = api.ErrBadRequest(errors.New("Password not match"), "")
u.Log.Printf("error : %s", err)
return res, err
}
pass, err := bcrypt.GenerateFromPassword([]byte(userRequest.Password), bcrypt.DefaultCost)
if err != nil {
u.Log.Printf("error generate password: %s", err)
return res, err
}
userRequest.Password = string(pass)
user := userRequest.Transform()
err = user.Create(r.Context(), u.Db)
if err != nil {
u.Log.Printf("error call create user: %s", err)
return res, err
}
res.Transform(user)
return res, nil
}
Dan ini contoh validasi yang dilakukan secara global di helper request pada method Update di file controllers/users.go
// Update user by id
func (u *Users) Update(w http.ResponseWriter, r *http.Request) {
paramID := r.Context().Value(api.Ctx("ps")).(httprouter.Params).ByName("id")
id, err := strconv.Atoi(paramID)
if err != nil {
u.Log.Println("convert param to id", err)
api.ResponseError(w, err)
return
}
user := new(models.User)
user.ID = uint64(id)
err = user.Get(r.Context(), u.Db)
if err != nil {
u.Log.Println("Get User", err)
api.ResponseError(w, err)
return
}
userRequest := new(request.UserRequest)
err = api.Decode(r, &userRequest, true)
if err != nil {
u.Log.Printf("error decode user: %s", err)
api.ResponseError(w, err)
return
}
userUpdate := userRequest.Transform(user)
err = userUpdate.Update(r.Context(), u.Db)
if err != nil {
u.Log.Printf("error update user: %s", err)
api.ResponseError(w, err)
return
}
resp := new(response.UserResponse)
resp.Transform(userUpdate)
api.ResponseOK(w, resp, http.StatusOK)
}
Last updated