go-zero(四) 错误处理(统一响应信息)

在之前的文章中,我们尝试重复注册的时候,给我们返回来400状态码,这样不利于前端来做用户提示。


image.png

在api服务中,我们希望http接口返回的状态码code永远是200,通过业务自定义的错误码来区分业务错误或者服务内部错误。

当返回错误的时候,http接口同样也返回json格式的数据,code为业务自定义的错误码,message为自定义错误码的详细描述,客户端获取到该数据后可以判断该业务错误码,同时可以把message信息做一个错误弹窗处理。

{
    "code": 1,
    "msg": "用户已经注册"
}

一、自定义错误处理方法和自定义错误码

自定义错误处理方法,需要使用httpx.SetErrorHandler(),来捕捉错误信息。

1.自定义错误结构与格式化

我们在项目所在目录下新建biz目录,然后再这个目录下分别创建3个文件

err.go 用来定义错误结构:

package biz

type Error struct {
    Code int    `json:"code"`
    Msg  string `json:"msg"`
}

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

func NewError(code int, msg string) *Error {
    return &Error{
        Code: code,
        Msg:  msg,
    }
}

resp.go 用来处理错误响应

package biz

type Result struct {
    Code int    `json:"code"`
    Msg  string `json:"msg"`
    Data any    `json:"data"`
}

func Success(data any) *Result {
    return &Result{
        Code: Ok,
        Msg:  "success",
        Data: data,
    }
}

func Fail(err *Error) *Result {
    return &Result{
        Code: err.Code,
        Msg:  err.Msg,
    }
}

func ErrHandler(err error) (int, any) {
    switch e := err.(type) {
    case *Error:
        // 自定义一个 错误返回类型
        return http.StatusOK, Fail(e)
    default:
        return http.StatusInternalServerError, nil
    }
}

vars.go 用来定义比较通用的业务错误码

package biz

const Ok = 200

var (
    AlreadyRegister = NewError(1, "用户已注册")
    PasswordErr     = NewError(2, "密码错误")
    InsertErr       = NewError(3, "用户注册失败")
    /*
    .....
    */
)


2. 使用自定义错误

接着修改user.go 文件,在main函数中使用自定义错误处理方法:

    /*
    ....
    */
    defer server.Stop()
    
    //httpx.SetErrorHandler 函数可以帮助你定义一个全局的错误处理逻辑,
    //该逻辑会在 HTTP handler 中捕获到的所有错误中执行。
    //它将允许你统一处理各类错误,返回更加一致和用户友好的响应。
    //httpx.SetErrorHandler 仅在调用了 httpx.Error 处理响应时才有效。
    httpx.SetErrorHandler(biz.ErrHandler)

    ctx := svc.NewServiceContext(c)
        /*
    ....
    */

接着修改业务代码,我们还是以注册功能为例,把返回的错误信息修改成我们自定义错误:

func (l *RegisterLogic) Register(req *types.RegisterRequest) (resp *types.RegisterResponse, err error) {
    // todo: add your logic here and delete this line
    /*
    ...
    */
    if user != nil {
        
        //return nil, errors.New(1, "用户已注册")
        return nil, biz.AlreadyRegister
    }
    //插入新的数据
    /*
    ...
    */
    if err != nil {
        //return nil, errors.New(2, "用户注册失败")
        return nil, biz.InsertErr
    }

}

接着运行测试


image.png

二、使用第三方库

如果你不想自己编写错误处理方法,也可以使用go zero 官方提供的X仓库,可以实现统一响应格式

1.下载库

go get github.com/zeromicro/x

它会自动帮我们把响应信息改为下面这种格式:

{
  "code": 0,
  "msg": "ok",
  "data": {
    ...
  }
}

2.修改handler

使用这个库需要我们修改handler,把原来的错误处理替换掉:

//导入zeromicro库并设置别名,避免和原生的http冲突
import (
    xhttp "github.com/zeromicro/x/http"  
)

//修改RegisterHandler的返回信息
func RegisterHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        var req types.RegisterRequest
        if err := httpx.Parse(r, &req); err != nil {
            //使用xhttp.JsonBaseResponseCtx 替换掉httpx.ErrorCtx
            xhttp.JsonBaseResponseCtx(r.Context(), w, err)
            //httpx.ErrorCtx(r.Context(), w, err)
            return
        }

        l := register.NewRegisterLogic(r.Context(), svcCtx)
        resp, err := l.Register(&req)
        if err != nil {
            //使用xhttp.JsonBaseResponseCtx 替换掉httpx.ErrorCtx
            xhttp.JsonBaseResponseCtx(r.Context(), w, err)
            //httpx.ErrorCtx(r.Context(), w, err)
        } else {
            //使用xhttp.JsonBaseResponseCtx 替换掉httpx.OkJsonCtx
            xhttp.JsonBaseResponseCtx(r.Context(), w, resp)
            //httpx.OkJsonCtx(r.Context(), w, resp)
        }
    }
}

3.修改返回错误

在业务文件中,把原来的err 修改成 errors.New() ,它的参数有两个,一个是用来返回 code码 ,还有一个是message消息:

func (l *RegisterLogic) Register(req *types.RegisterRequest) (resp *types.RegisterResponse, err error) {
    // todo: add your logic here and delete this line
    /*
    .....
    */
    
    if user != nil {
        //return nil, err
        return nil, errors.New(1, "用户已注册")
    }
    //插入新的数据
    /*
    .....
    */
    if err != nil {
        //return nil, err
        return nil, errors.New(2, "用户注册失败")
    }
}

接着我们运行项目,使用Postman重新测试,结果如下:


image.png
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容