错误处理
错误的处理是一个服务必不可缺的环节。在平时的业务开发中,我们可以认为http状态码不为2xx系列的,都可以认为是http请求错误, 并伴随响应的错误信息,但这些错误信息都是以plain text形式返回的。除此之外,我在业务中还会定义一些业务性错误,常用做法都是通过 code、msg 两个字段来进行业务处理结果描述,并且希望能够以json响应体来进行响应。
业务错误响应格式
{
"code": 0,
"msg": "successful",
"data": {
....
}
}
业务处理异常
{
"code": 10001,
"msg": "参数错误"
}
在之前,我们在登录逻辑中处理用户名不存在时,直接返回来一个error。我们来登录并传递一个不存在的用户名看看效果。
用户名不存在
自定义错误
接下来我们将其以json格式进行返回
首先在ommon/errorx中添加一个baseerror.go文件,并填入代码
$ common/errorx
$ mkdir -p common/errorx && cd common/errorx
$ vim baseerror.go
package errorx
const defaultCode = 1001
type CodeError struct {
Code int `json:"code"`
Msg string `json:"msg"`
}
type CodeErrorResponse struct {
Code int `json:"code"`
Msg string `json:"msg"`
}
func NewCodeError(code int, msg string) error {
return &CodeError{Code: code, Msg: msg}
}
func NewDefaultError(msg string) error {
return NewCodeError(defaultCode, msg)
}
func (e *CodeError) Error() string {
return e.Msg
}
func (e *CodeError) Data() *CodeErrorResponse {
return &CodeErrorResponse{
Code: e.Code,
Msg: e.Msg,
}
}
自定义成功返参
package response
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
)
type Body struct {
Status bool `json:"status"`
Code int `json:"code"`
Msg string `json:"msg"`
Data interface{} `json:"data,omitempty"`
}
func Response(w http.ResponseWriter, resp interface{}, err error) {
var body Body
if err != nil {
body.Code = -1
body.Msg = err.Error()
} else {
body.Code = 200
body.Msg = "success"
body.Data = resp
}
httpx.OkJson(w, body)
}
func Ok(w http.ResponseWriter, resp interface{}, msg string) {
var body Body
body.Code = 200
body.Msg = "OK"
if len(msg) > 0 {
body.Msg = msg
}
body.Data = resp
httpx.OkJson(w, body)
}
开启自定义错误
开启自定义错误的代码建议写在模板里goctlTpl/1.4.2/api/main.tpl
,就可以自动生成了
如果本地没有~/.goctl/${goctl版本号}/api/handler.tpl文件,可以通过模板初始化命令
goctl template init
进行初始化
$ vim service/user/api/user.go
代码
package main
import (
"flag"
"fmt"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/rest"
"github.com/zeromicro/go-zero/rest/httpx"
"go-zero-demo/common/errorx"
"go-zero-demo/user-api/internal/config"
"go-zero-demo/user-api/internal/handler"
"go-zero-demo/user-api/internal/svc"
"net/http"
)
var configFile = flag.String("f", "etc/user-api.yaml", "the config file")
func main() {
//读取控制台启动命令时带的参数 例 下面命令里-f 带的参数
//go run user.go -f etc/greet-api.yaml
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c) //yaml配置初使化到config里
ctx := svc.NewServiceContext(c) //初使化ServiceContext配置文件
server := rest.MustNewServer(c.RestConf) //启一个server
defer server.Stop() //退出时,服务停时停止服务
//注册路由
handler.RegisterHandlers(server, ctx)
// 自定义错误 start
httpx.SetErrorHandlerCtx(func(ctx context.Context, err error) (int, interface{}) {
//处理errors.New生成的错误,支持返回json格式, 兼容go-zero入参校验不过的情况
if fmt.Sprintf("%T", err) == "*errors.errorString" {
err = errorx.NewDefaultError(err.Error())
}
switch e := err.(type) {
case *errorx.CodeError:
return http.StatusOK, e.Data()
default:
return http.StatusInternalServerError, nil
}
})
// 自定义错误 end
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
//启动服务
server.Start()
}
Handhle
func BlackIpsListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.BlackIpsListReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := black_ips.NewBlackIpsListLogic(r.Context(), svcCtx)
resp, err := l.BlackIpsList(&req)
//response.Response(w, resp, err) //②
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
response.Ok(w, resp, "")
//httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}
重启服务验证
$ curl -i -X POST \
http://127.0.0.1:8888/user/login \
-H 'content-type: application/json' \
-d '{
"username":"1",
"password":"123456"
}'
HTTP/1.1 200 OK
Content-Type: application/json
Date: Tue, 09 Feb 2021 06:47:29 GMT
Content-Length: 40
{"code":1001,"msg":"用户名不存在"}
参考:
https://go-zero.dev/cn/docs/advance/error-handle
作者:海马 创建时间:2022-10-30 08:25
最后编辑:海马 更新时间:2024-12-22 19:32
最后编辑:海马 更新时间:2024-12-22 19:32