Error Handling

[!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR

Error handling is an indispensable part of service. In normal business development, we can think that the http status code is not in the 2xx series, it can be regarded as an http request error. It is accompanied by error messages in response, but these error messages are all returned in plain text. In addition, I will define some business errors in the business, and the common practice is to pass The two fields code and msg are used to describe the business processing results, and it is hoped that the response can be made with the json response body.

Business error response format

  • Business processing is normal

    1. {
    2. "code": 0,
    3. "msg": "successful",
    4. "data": {
    5. ....
    6. }
    7. }
  • Business processing exception

    1. {
    2. "code": 10001,
    3. "msg": "something wrong"
    4. }

login of user api

Previously, when we handled the login logic when the username did not exist, an error was directly returned. Let’s log in and pass a username that does not exist to see the effect.

  1. curl -X POST \
  2. http://127.0.0.1:8888/user/login \
  3. -H 'content-type: application/json' \
  4. -d '{
  5. "username":"1",
  6. "password":"123456"
  7. }'
  1. HTTP/1.1 400 Bad Request
  2. Content-Type: text/plain; charset=utf-8
  3. X-Content-Type-Options: nosniff
  4. Date: Tue, 09 Feb 2021 06:38:42 GMT
  5. Content-Length: 19
  6. Username does not exist

Next we will return it in json format

Custom error

  • First add a baseerror.go file in common and fill in the code

    1. $ cd common
    2. $ mkdir errorx&&cd errorx
    3. $ vim baseerror.go
    1. package errorx
    2. const defaultCode = 1001
    3. type CodeError struct {
    4. Code int `json:"code"`
    5. Msg string `json:"msg"`
    6. }
    7. type CodeErrorResponse struct {
    8. Code int `json:"code"`
    9. Msg string `json:"msg"`
    10. }
    11. func NewCodeError(code int, msg string) error {
    12. return &CodeError{Code: code, Msg: msg}
    13. }
    14. func NewDefaultError(msg string) error {
    15. return NewCodeError(defaultCode, msg)
    16. }
    17. func (e *CodeError) Error() string {
    18. return e.Msg
    19. }
    20. func (e *CodeError) Data() *CodeErrorResponse {
    21. return &CodeErrorResponse{
    22. Code: e.Code,
    23. Msg: e.Msg,
    24. }
    25. }
  • Replace errors in login logic with CodeError custom errors

    1. if len(strings.TrimSpace(req.Username)) == 0 || len(strings.TrimSpace(req.Password)) == 0 {
    2. return nil, errorx.NewDefaultError("Invalid parameter")
    3. }
    4. userInfo, err := l.svcCtx.UserModel.FindOneByNumber(req.Username)
    5. switch err {
    6. case nil:
    7. case model.ErrNotFound:
    8. return nil, errorx.NewDefaultError("Username does not exist")
    9. default:
    10. return nil, err
    11. }
    12. if userInfo.Password != req.Password {
    13. return nil, errorx.NewDefaultError("User password is incorrect")
    14. }
    15. now := time.Now().Unix()
    16. accessExpire := l.svcCtx.Config.Auth.AccessExpire
    17. jwtToken, err := l.getJwtToken(l.svcCtx.Config.Auth.AccessSecret, now, l.svcCtx.Config.Auth.AccessExpire, userInfo.Id)
    18. if err != nil {
    19. return nil, err
    20. }
    21. return &types.LoginReply{
    22. Id: userInfo.Id,
    23. Name: userInfo.Name,
    24. Gender: userInfo.Gender,
    25. AccessToken: jwtToken,
    26. AccessExpire: now + accessExpire,
    27. RefreshAfter: now + accessExpire/2,
    28. }, nil
  • Use custom errors

    1. $ vim service/user/cmd/api/user.go
    1. func main() {
    2. flag.Parse()
    3. var c config.Config
    4. conf.MustLoad(*configFile, &c)
    5. ctx := svc.NewServiceContext(c)
    6. server := rest.MustNewServer(c.RestConf)
    7. defer server.Stop()
    8. handler.RegisterHandlers(server, ctx)
    9. // Custom error
    10. httpx.SetErrorHandler(func(err error) (int, interface{}) {
    11. switch e := err.(type) {
    12. case *errorx.CodeError:
    13. return http.StatusOK, e.Data()
    14. default:
    15. return http.StatusInternalServerError, nil
    16. }
    17. })
    18. fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
    19. server.Start()
    20. }
  • Restart service verification

    1. $ curl -i -X POST \
    2. http://127.0.0.1:8888/user/login \
    3. -H 'content-type: application/json' \
    4. -d '{
    5. "username":"1",
    6. "password":"123456"
    7. }'
    1. HTTP/1.1 200 OK
    2. Content-Type: application/json
    3. Date: Tue, 09 Feb 2021 06:47:29 GMT
    4. Content-Length: 40
    5. {"code":1001,"msg":"Username does not exist"}