Skip to content
This repository has been archived by the owner on Jul 2, 2024. It is now read-only.

Latest commit

 

History

History
128 lines (97 loc) · 3.72 KB

README.md

File metadata and controls

128 lines (97 loc) · 3.72 KB

Error Packer

Go struct packer, specifically for handling errors

Why

The way Go handles errors will always end up with a bunch of if err!=nil, it's ok itself but when we have to return a struct every time an error occurs, it can take up a lot of lines which significantly decreases the readability and make typing work more tedious.

Take Gin framework as an example, the usual way to handle errors is something like this

func handler(c *gin.Context) {
    if err != nil {
       c.JSON(http.StatusOK, SomeStruct{
            Resp: Resp{
                Code: -1,
                Msg:  err.Error(),
            },
            Data: someData,
        })
        return
    }
}

After introducing the Error Packer (ep), it will become something like this

func handler(c *gin.Context) {
    packer := ep.Packer{V: SomeStruct{}}
    if err != nil {
        c.JSON(http.StatusOK, packer.Pack(err))
        return
    }
}

Much cleaner right? Let's get into it.

What does it do?

Packer

Packer is for "packing a struct". It will pack an ErrPack struct into any struct and fill the fields with tagged values.

ErrPack struct

ErrPack struct is an error information struct which implements the error type, it contains a code field and a message field. You can customize these fields as you want.

If you don't need a code field, you can also just pass the err (type error) into Packer, it accepts both types.

Usage

Copy the packer.go and err_pack.go file to wherever you want.

Define your struct with "ep" tags.

There are 4 meaningful tags, others stand for the default values of the fields:

  • ep:"err.code" - the field with this tag will be filled with ErrPack.Code, it must be int type
  • ep:"err.msg" - the field with this tag will be filled with ErrPack.Msg, it must be string type
  • ep:"" - the field with an empty tag will remain the default value of the type, you can also omit the tag
  • ep:"-" - the field with this tag will be forcefully ignored, this is useful when it comes to embedded struct (see explanation below)

Example

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    ep "github.com/zenpk/error-packer"
    "net/http"
    "net/http/httptest"
)

type User struct {
    Name string `json:"name,omitempty"`
}

type UserLoginResp struct {
    Seq  int64  `json:"seq" ep:"-1"`          // will be -1
    Code int64  `json:"code" ep:"err.code"`   // will eventually be ErrPack.Code
    Msg  string `json:"msg" ep:"err.msg"`     // will eventually be ErrPack.Msg
    User *User  `json:"user,omitempty" ep:""` // will omit this field
}

func handler(c *gin.Context) {
    // create a new packer your JSON interface
    packer := ep.Packer{V: UserLoginResp{}}
    err := ep.ErrInputBody      // assume that an input body error happened
    resp := packer.Pack(err)    // pack the response struct with the error
    c.JSON(http.StatusOK, resp) // return with resp
}

func main() {
    req, _ := http.NewRequest(http.MethodGet, "/err", nil) // make a mock request
    rec := httptest.NewRecorder()                          // record the mock request
    // use Gin to handle the request
    r := gin.Default()
    r.GET("/err", handler)
    r.ServeHTTP(rec, req)
    fmt.Println(rec.Body.String())
}

Output

{"seq":-1,"code":4003,"msg":"input body error"}

Embedded Struct

Error Packer supports embedded struct. However, every field must be exported, otherwise errors will occur. If you don't want Packer to traverse the whole embedded struct, you can add anep:"-"tag to a struct to ignore the field.

type Parent struct{
    children `ep:"-"` // if not adding the tag, Packer will go into the children struct which is not exported, and causing an error
    Others string
}