背景:
首页公司最近要启动一个项目,公司主要业务是用java开发的,但是目前这个方向的项目,公司要求部署在主机上,就是普通的一台电脑上,电脑配置不详,还有经常开关机,所以用java面临一些问题,内存占用过高,启动过慢;因此需要寻找解决方案,我这边目前根据情况,尝试用go语言,本身也想学学这块语言,花费了大概两周时间,基本上没啥大问题了,开始研究框架,也对比了很多go框架,发现国产的goframe框架各方面还不错,了解了一下,就开始搭建项目框架,遇到了一个很无语的问题,也就是goframe规范要求,controller 请求参数和返回参数必须要定义XxxReq 和XxxRes 结构体,然后才能运行通过,否则就会报:
其实对于req来说,我这边是可以接受的,因为参数用新的结构体接受是合理的,
但是返回必须要求Res 就有些强制性了,每个团队每个项目情况都不一致,强制返回会对客户端造成困扰,关键,这样的效果不好,大家看下:
对应客户端返回结果:
会发现,返回的包装体里面在包装一个字段,虽然这样也可以实现业务,但是过于麻烦,为什么不能直接用data接收呢?
{
"code": 0,
"message": "",
"data": true
}
返回这样多简单,方便。
针对上面的问题,该如何解决呢?
我这边排查了下返回包装体是,goframe有个中间件进行设置了:
ghttp.MiddlewareHandlerResponse
就是它,我们开始自定义中间件:
1、把
ghttp.MiddlewareHandlerResponse 拷贝出来自行定义一个出来,实现包装体那块稍微调整下即可:
// 返回值设置
func (s *sMiddleware) MiddlewareHandlerResponse(r *ghttp.Request) {
r.Middleware.Next()
// There's custom buffer content, it then exits current handler.
if r.Response.BufferLength() > 0 {
return
}
var (
msg string
err = r.GetError()
res = r.GetHandlerResponse()
code = gerror.Code(err)
)
if err != nil {
if code == gcode.CodeNil {
code = gcode.CodeInternalError
}
msg = err.Error()
} else {
if r.Response.Status > 0 && r.Response.Status != http.StatusOK {
msg = http.StatusText(r.Response.Status)
switch r.Response.Status {
case http.StatusNotFound:
code = gcode.CodeNotFound
case http.StatusForbidden:
code = gcode.CodeNotAuthorized
default:
code = gcode.CodeUnknown
}
// It creates error as it can be retrieved by other middlewares.
err = gerror.NewCode(code, msg)
r.SetError(err)
} else {
code = gcode.CodeOK
}
}
//删除掉或注释掉
/*r.Response.WriteJson(result.ResultRes{
Code: code.Code(),
Message: msg,
Data: res,
})*/
//code正常,直接写回结果到客户端
if code == gcode.CodeOK {
r.Response.WriteJson(res)
} else {
//非正常,重新组装结果数据,把系统相关code和错误信息一并返回客户端
r.Response.WriteJson(result.ResultRes{
Code: code.Code(),
Message: msg,
Data: nil,
})
}
}
result.ResultRes是个什么玩意呢,我这边自己写的包装体,写发出来:
type ResultRes struct {
Code int `json:"code" dc:"错误码:0成功,非0失败"`
Message string `json:"message" dc:"异常信息"`
Data interface{} `json:"data" dc:"结果信息"`
}
func (r *ResultRes) SuccessAll(data interface{}, msg string) {
if msg == "" {
msg = "操作成功"
}
r.Code = gcode.CodeOK.Code()
r.Message = msg
r.Data = data
}
func (r *ResultRes) Success(data interface{}) {
r.SuccessAll(data, "")
}
func (r *ResultRes) SuccessNo() {
r.Success(nil)
}
func (r *ResultRes) SuccessMsg(msg string) {
r.SuccessAll(nil, msg)
}
func (r *ResultRes) Fail(msg string) {
if msg == "" {
msg = "操作失败"
}
r.Code = gcode.CodeNil.Code()
r.Message = msg
r.Data = nil
}
func (r *ResultRes) FailNo() {
r.Fail("操作失败")
}
// 上面是给结构体定义的函数
// 下面是给包提定义的函数
func SuccessAll(data interface{}, msg string) *ResultRes {
if msg == "" {
msg = "操作成功"
}
return &ResultRes{gcode.CodeOK.Code(), msg, data}
}
func Success(data interface{}) *ResultRes {
return SuccessAll(data, "")
}
func SuccessNo() *ResultRes {
return Success(nil)
}
func SuccessMsg(msg string) *ResultRes {
return SuccessAll(nil, msg)
}
func Fail(msg string) *ResultRes {
if msg == "" {
msg = "操作失败"
}
return &ResultRes{gcode.CodeNil.Code(), msg, nil}
}
func FailNo() *ResultRes {
return Fail("操作失败")
}
大家注意了我这个结构体的名称了吧,叫ResultRes ,是因为要符合goframe规范要求,否则无法运行的,就是上面报错信息,ok。
这块定义后就好了,我就拿登陆来给大家演示下:
req封装:
接口:接口返回值注意,要给我们刚才定义的结果集
controller实现:
结果:
这样就完成了。
总结:
1、重写框架自带的包装体中间件,建议重写,后期可能要进行扩展(重写了返回包装体那块,大家可以看看就能明白)
2、创建自定义包装体,注意包装体的名称必须以Res结尾,否则goframe无法运行会报错
3、所有接口都可以用自定义包装体进行返回就可以了