In order to facilitate the front-end page of the blog to deal with the request error of the back-end interface, we need to deal with the error result of the server. If the unified interface error normalization process is not carried out, there are many different interface error results, which will lead to the front-end error result processing becomes extremely tedious.

Interface response error handling

First, standardize the errcode error code

0," request successful "20001," server error" 20002," parameter error "20003," unable to find" 20004," Request too frequent "20005," Authentication failed, unable to find corresponding permission" 20006," authentication failed, Token error "20007," authentication failed, Token timeout "20008," Authentication failed, Token generation failed"Copy the code

Create base_code.go under PKG /errcode

package errcode

var (
  Success = NewError(0."Request successful")
  ServerError = NewError(20001."Server error")
  InvalidParams = NewError(20002."Parameter error")
  NotFound = NewError(20003."Can't find it.")
  TooManyRequests = NewError(20004."Too many requests")
  UnauthorizedAuthNotExist = NewError(20005."Authentication failed, cannot find corresponding permission")
  UnauthorizedTokenError = NewError(20006 ,"Authentication failed, Token error")
  UnauthorizedTokenTimeout = NewError(20007."Authentication failed, Token timed out")
  UnauthorizedTokenGeneration = NewError(20008 ,"Authentication failed, Token generation failed"))Copy the code

Then we create our Error structure and method

Create errcode.go under PKG/errCode

package errcode

import (
  "fmt"
  "net/http"
)

type Error struct {
  code    int      `json:"code"`
  msg     string   `json:"msg"`
  details []string `json:"details"`
}

var codes = map[int]string{}

func NewError(code int, msg string) *Error {
  if _, ok := codes[code]; ok {
    panic(fmt.Sprintf("Error code %d already exists, please change it.", code))
  }
  codes[code] = msg
  return &Error{code:code, msg:msg}
}

func (e *Error) Error(a) string {
  return fmt.Sprintf("Error code: %d, error message: %s", e.code, e.msg)
}

func (e *Error) Code(a) int {
  return e.code
}

func (e *Error) Msg(a) string {
  return e.msg
}

func (e *Error) Msgf(args []interface{}) string {
  return fmt.Sprintf(e.msg, args...)
}

func (e *Error) Details(a) []string {
  return e.details
}

func (e *Error) WithDetails(details ...string) *Error {
  newError := *e
  newError.details = []string{}
  for _, d := range details {
    newError.details = append(newError.details, d)
  }
  return &newError
}

func (e *Error) StatusCode(a) int {
  switch e.Code() {
  case Success.Code():
    return http.StatusOK
  case ServerError.Code():
    return http.StatusInternalServerError
  case InvalidParams.Code():
    return http.StatusBadRequest
  case NotFound.Code():
    return http.StatusNotFound
  case TooManyRequests.Code():
    return http.StatusTooManyRequests
  case UnauthorizedAuthNotExist.Code():
    fallthrough
  case UnauthorizedTokenError.Code():
    fallthrough
  case UnauthorizedTokenTimeout.Code():
    fallthrough
  case UnauthorizedTokenGeneration.Code():
    return http.StatusUnauthorized
  }
  return http.StatusInternalServerError
}

Copy the code

Then we’ll use our Error structure for interface processing

Use Viper to manage our configuration

Viper is a complete configuration solution for Go applications that is designed to work within applications and handle all types of configuration requirements and formats. It supports.

  • Setting defaults

  • Read configuration files for JSON, TOML, YAML, HCL, envFile, and Java properties

  • Viewing and rereading configuration files in real time (optional)

  • Read from an environment variable

  • Read from a remote configuration system (ETCD or Consul) and watch for changes

  • Read from the command line flag

  • Read from buffer

  • Set explicit values

Viper can be thought of as a registry for all your application configuration needs.

Github github.com/spf13/viper

Viper installation

go get github.com/spf13/viper
Copy the code

Create config.yaml under /configs

Server:
  RunMode: debug
  HttpPort: 8080
  ReadTimeout: 60
  WriteTimeout: 60
App:
  DefaultPageSize: 10 The default page display number
  MaxPageSize: 100 # Maximum number of pages to display
  LogSavePath: storage/logs
  LogFileName: app
  LogFileExt: .log # Extension of log output file
Database:
  DBType: mysql # database type
  Username: root  Mysql user name
  Password: root  # mysql password
  Host: 127.0. 01.: 3306 Mysql > connect to mysql
  DBName: go_server_mysqldb Mysql database name
  TablePrefix: blog_ # table prefix
  Charset: utf8 The # character set
  ParseTime: True  The query result is automatically parsed to time
  MaxIdleConns: 10 The maximum number of free connections in Mysql is 2
  MaxOpenConns: 30 Mysql > set maximum number of connections

Copy the code

Setting. Go initializes our viper under/PKG /setting

package setting

import "github.com/spf13/viper"

type Setting struct {
    vp *viper.Viper
}

func NewSetting(a) (*Setting, error) {
  // Create a viper instance
  vp := viper.New()
  // Set the configuration name
  vp.SetConfigName("config")
  // Add the configuration file directory
  vp.AddConfigPath("configs/")
  // Specify the configuration file format
  vp.SetConfigType("yaml")
  // Read the configuration file
  err := vp.ReadInConfig()
  iferr ! =nil {
      return nil, err
  }
  // The output of the read configuration file can be omitted
  err = vp.SafeWriteConfigAs("configs/output.yaml")
  iferr ! =nil {
    return nil, err
  }
  / / returns the setting
  return &Setting{vp}, nil
}
Copy the code

Create section. Go under/PKG /setting to create our file configuration block field structure and method

package setting

import "time"

type ServerSettings struct {
  RunMode string
  HttpPort string
  ReadTimeout time.Duration
  WriteTimeout time.Duration
}

type AppSettings struct {
    DefaultPageSize int
    MaxPageSize int
    LogSavePath string
    LogFileName string
    LogFileExt string
}

type DatabaseSettings struct{
  DBType string
  UserName string
  Password string
  Host string
  DBName string
  TablePrefix string
  Charset string
  ParseTime bool
  MaxIdleConns int
  MaxOpenConns int
}

// ReadSection Reads configuration section information
func (s *Setting) ReadSection(k string, v interface{}) error{

  /* You also have the option of Unmarshalling all or a specific value to a struct, map, etc. There are two methods to do this: Unmarshal(rawVal interface{}) : error UnmarshalKey(key string, rawVal interface{}) : error */

  // Deconstruct the configuration file information group
  err := s.vp.UnmarshalKey(k,v)
  iferr ! =nil {
      return err
  }
  return nil
}

Copy the code

Create our global configuration manager under /golabl and create setting.go

package global

import "go-blog-server/pkg/setting"

var (
   ServerSetting *setting.ServerSettings
   AppSetting *setting.AppSettings
   DatabaseSetting *setting.DatabaseSettings
)
Copy the code

Finally, change our main.go

package main

import (
  "fmt"
  "go-blog-server/global"
  "go-blog-server/internal/routers"
  "go-blog-server/pkg/setting"
  "log"
  "net/http"
  "time"
)

// Call the go initializer
func init(a) {
  // Install configuration
  err := setupSetting()
  iferr ! =nil {
      log.Fatalf("init setupSetting Error : %v",err)
  }
}

func main(a) {
  // Instantiate the route handler
  router := routers.NewRouter()
  // Customize the HTTP Server
  s := &http.Server {
    // Set the listening port
      Addr : ":" + global.ServerSetting.HttpPort,
      // Add the handler we wrote
      Handler: router,
      // Sets the maximum time allowed to read and write
      ReadTimeout : global.ServerSetting.ReadTimeout,
      WriteTimeout : global.ServerSetting.WriteTimeout,
      // set the maximum number of bytes in the request header to 2^20bytes 1Mb
      MaxHeaderBytes: 1<<20,
  }
  err := s.ListenAndServe()
  iferr ! =nil {
    fmt.Println(err)
  }
}


//setupSetting
func setupSetting(a) error{
  mySetting,err := setting.NewSetting()
  iferr ! =nil {
      return err
  }
  err = mySetting.ReadSection("Server",&global.ServerSetting)
  iferr ! =nil {
    return err
  }
  err = mySetting.ReadSection("App", &global.AppSetting)
  iferr ! =nil {
    return err
  }
  err = mySetting.ReadSection("Database",&global.DatabaseSetting)
  iferr ! =nil {
    return err
  }

  // Set the data to seconds
  global.ServerSetting.ReadTimeout *= time.Second
  global.ServerSetting.WriteTimeout *= time.Second

  return nil
}
Copy the code