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

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

在实现注册逻辑时,尝试重复注册可能会返回 400 状态码,显然不符合正常设计思维。我们希望状态码为 200,并在响应中返回错误信息。

一、使用第三方库

1.下载库

目前 go-zero官方的 zeromicro 下有一个 x 仓库,可以实现统一响应格式,我们先安装下:

bash 复制代码
go get github.com/zeromicro/x

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

json 复制代码
{
  "code": 0,
  "msg": "ok",
  "data": {
    ...
  }
}

2.修改handler

接着我们修改internal/handler/register/registerhandler.go文件,把原来的响应处理,替换成这个库的:

go 复制代码
//导入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.修改返回错误

internal/logic/user/registerlogic.go 文件中,把原来的err 修改成 errors.New() ,它的参数有两个,一个是用来返回 code码 ,还有一个是message消息:

go 复制代码
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重新测试,结果如下:

二、自定义错误管理

如果你不想使用这个库,或者想自己实现一些自定义错误,那么你也可以自己设置错误管理

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

我们在internal目录下新建biz目录,用于业务处理,然后再这个目录下分别创建3个文件

创建biz.go 文件

go 复制代码
package biz

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

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

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

创建resp.go 文件

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,
	}
}

创建vars.go 文件

go 复制代码
package biz

const Ok = 200

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

2. 使用 httpx.Error 和 httpx.SetErrorHandler

接着修改user.go 文件:

go 复制代码
	/*
	....
	*/
	defer server.Stop()
	
	//httpx.SetErrorHandler 函数可以帮助你定义一个全局的错误处理逻辑,
	//该逻辑会在 HTTP handler 中捕获到的所有错误中执行。
	//它将允许你统一处理各类错误,返回更加一致和用户友好的响应。
	//httpx.SetErrorHandler 仅在调用了 httpx.Error 处理响应时才有效。
	httpx.SetErrorHandler(func(err error) (int, any) {
		switch e := err.(type) {
		case *biz.Error:
			// 自定义一个 错误返回类型
			return http.StatusOK, biz.Fail(e)
		default:
			return http.StatusInternalServerError, nil
		}
	})

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

修改internal/handler/register/registerhandler.go文件:

go 复制代码
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 {
			//httpx.SetErrorHandler 仅在调用了 httpx.Error 处理响应时才有效。
			//所以我们现在还是使用原来的httpx.ErrorCtx
			httpx.ErrorCtx(r.Context(), w, err)
			return
		}

		l := register.NewRegisterLogic(r.Context(), svcCtx)
		resp, err := l.Register(&req)
		if err != nil {
			//httpx.SetErrorHandler 仅在调用了 httpx.Error 处理响应时才有效。
			//所以我们现在还是使用原来的httpx.ErrorCtx
			httpx.ErrorCtx(r.Context(), w, err)
		} else {
			//成功的请求,httpx.SetErrorHandler 是捕获不到的
			// 所以需要我们自定义返回信息
			httpx.OkJsonCtx(r.Context(), w, biz.Success(resp))
		}
	}
}

3.实现统一的错误响应机制

接下来,修改 internal/user/register/registerlogic.go文件:

go 复制代码
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
	}

}

接着运行测试

相关推荐
geovindu2 小时前
go: Lock/Mutex Pattern
开发语言·后端·设计模式·golang·互斥锁模式
知识分享小能手2 小时前
R语言入门学习教程,从入门到精通,R语言日期和时间序列(6)
开发语言·学习·r语言
叼烟扛炮2 小时前
C++ 知识点18 内部类
开发语言·c++·算法·内部类
TAN-90°-3 小时前
Java 3——getter和setter super()关键字
java·开发语言
wand codemonkey3 小时前
(二十七)Maven(依赖)【安装】+【项目结构】
java·开发语言·maven
linda公馆3 小时前
Maven项目报错:java:错误:不支持发行版本 5
java·开发语言·maven
Ulyanov3 小时前
《从质点到位姿:基于Python与PyVista的导弹制导控制全栈仿真》: 可视化革命——基于 PyVista 的 3D 战场构建与实时渲染
开发语言·python·算法·3d·系统仿真
Heaphaestus,RC3 小时前
Slate到UMG的封装原理揭秘
开发语言·ue5
爱喝热水的呀哈喽3 小时前
一段即插即用的hypermesh命令行
开发语言·python
Ulyanov3 小时前
《从质点到位姿:基于Python与PyVista的导弹制导控制全栈仿真》: 终极试炼——全链路综合仿真与蒙特卡洛打靶
开发语言·python·系统仿真·雷达电子对抗