在之前的文章中,我们尝试重复注册的时候,给我们返回来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