I am writing a test web application in Golang.
Create post handler:
type NewPostReq struct {
Title string `validate:"required"`
Description string `validate:"required"`
Body string `validate:"required"`
Tags []struct {
Tag string `validate:"required"`
} `validate:"min=2,dive"`
}
// PostResource the Post's json response
type PostResource struct {
ID uint `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Body string `json:"body"`
Tags []struct {
ID uint `json:"id"`
Tag string `json:"tag"`
} `json:"tags"`
}
func CreatePost(c *Context) (err error) {
req := new(NewPostReq)
err = BindAndValidate(c.Request, req)
if err != nil {
return
}
post := new(Post)
err = CopyTo(req, post)
if err != nil {
return
}
DB.Create(post)
pr := new(PostResource)
err = CopyTo(post, pr)
if err != nil {
return
}
err = c.JSON(201, pr)
return
}
I am not sure if this is a good approach. I think I am repeating myself during error handling.
There is dependencies code:
type Handler func(*Context) error
func (mh Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
c := &Context{Response: w, Request: r}
err := mh(c)
if err != nil {
switch e := err.(type) {
case *HTTPError:
c.JSON(e.HTTPStatusCode, e)
default:
http.Error(c.Response, e.Error(), http.StatusInternalServerError)
}
}
}
func Bind(r *http.Request, v interface{}) (err error) {
err = json.NewDecoder(r.Body).Decode(v)
return
}
func ValidateStruct(v interface{}) (err error) {
errs := validate.Struct(v)
if errs != nil {
err = errs
return
}
return nil
}
func BindAndValidate(r *http.Request, v interface{}) (err error) {
err = Bind(r, v)
if err != nil {
return
}
err = ValidateStruct(v)
if err != nil {
err = NewFormValidationError(err.(validator.ValidationErrors))
}
return
}
// CopyTo copy data from struct to another struct
func CopyTo(from interface{}, to interface{}) (err error) {
b, err := json.Marshal(from)
if err != nil {
return
}
err = json.Unmarshal(b, to)
return
}