{"code": 0,// API 错误码"message": "",// API 消息"data": {},// 响应数据"request_id": "" // 请求ID}
当然你可以完全去掉这个默认 Wrapper 或者 使用满足你们团队规范的 Wrapper(需要实现 data.HttpResponse
接口) 来替换它:
type NativeJsonResp struct { data interface{}}func (n *NativeJsonResp) Set(code int, msg string, data interface{}) { n.data = https://www.huyubaike.com/biancheng/data}func (n *NativeJsonResp) SetReqID(reqId string) {}func (n *NativeJsonResp) MarshalJSON() ([]byte, error) { return json.Marshal(n.data)}func main() {... droplet.Option.ResponseNewFunc = func() data.HttpResponse {return &NativeJsonResp{} }...}
对于另外一些并不需要 Wrapper 或者 你想要自行控制返回的内容时可以在 Handler 中使用一些实现了特定接口的返回值,如下所示:
func GetLoginQRCode(ctx droplet.Context) (interface{}, error) { type makeQRCodeResp struct {SceneID string `json:"scene_id"`Stateint`json:"state"`Urlstring `json:"url"` } var resp makeQRCodeResp if err := goreq.Get(UrlMakeQRCode, goreq.SetHeader(fakeClientHeader()), goreq.JsonResp(&resp)).Do(); err != nil {return nil, fmt.Errorf("get qrcode failed: %w", err) } return &data.RawResponse{StatusCode: http.StatusOK,Body:[]byte(fmt.Sprintf(QRCodeBase, resp.SceneID, resp.SceneID, resp.Url)), }, nil}
类似的还有 data.FileResponse
、data.SpecCodeResponse
, 根据其名字你可以在需要的场景选择它们 。
同时在整形过程中,为了业务研发不再需要关心错误处理 , Droplet 会自动将 err != nil
的响应转化到 code 与 message 字段上 。如下图所示:
func ErrorAPI(ctx droplet.Context) (interface{}, error) {return nil, errors.New("failed")}
那么你将得到如下的响应:
{"code": 10000,"message": "failed"}
当然 , 你可以使用 data.BaseError
来指定你想返回的错误码:
func ErrorAPI(ctx droplet.Context) (interface{}, error) {return nil, data.BaseError{Code: 100, Message: "custom message"}}
Tips流量记录Droplet 自带了记录 API 出参与入参的能力,但是默认所有记录信息都会被抛弃,如果想要启用它,你需要实现 Droplet 的全局 Logger,如下所示:
- 这些特定的响应其背后都是实现了某一类接口,如果有需要你也完全可以自行实现 。
import ("github.com/shiningrush/droplet/log")func main() {...// CustomLogger 需要实现 log.Interface log.DefLogger = &CustomLogger{}// droplet 默认只会记录 Path , Method,耗时等信息,如果你需要打印 API 的输入与输出,可以在全局选项中开启(在Wraps函数中也可指定)droplet.Option.TrafficLogOpt = &middleware.TrafficLogOpt{LogReq:true,LogResp: true, }...}
自定义中间件实现一个自定义中间件很简单 , 你只需要实现与 Hanler 类似的接口即可,下图是一个简单的中间件,它会用于检测输入参数是否需要 Quota 并执行相关逻辑:type DemoMiddleware struct {// 继承基本的middleware,里面有用于实现处理链路的公共逻辑middleware.BaseMiddleware}func (mw *HttpInputMiddleware) Handle(ctx core.Context) error {if ck, ok := ctx.Input().(QuotaChecker); !ok {if err := ck.IsQuotaEnough(); err != nil {return err}}// 调用下一个中间件 , 有需要的话你也可以在响应返回后执行部分逻辑return mw.Handle(ctx)}func main() {// 如果你需要所有API都添加该中间件,可以在全局选项中将你的中间件编排 droplet.Option.Orchestrator = func(mws []core.Middleware) []core.Middleware {return append(mws, &DemoMiddleware{}) }...// 在单个API上启用 r.POST("/json_input/:id", ginwrap.Wraps(APIHandler,wrapper.Orchestrator(func(mws []core.Middleware) []core.Middleware {return append(mws, &DemoMiddleware{}) })))...}
Tips小结正如文中所说,Droplet 的核心目标是 提供位于应用层的、pipeline 形式的请求处理能力,并以此为基础提供了一些开箱即用的中间件 。它对项目带来的收益总结为几点:
Q: 为什么使用 Orchestrator 这样的形式来配置中间件,而非通过 Priorty 之类的权重来实现中间件的编排,这样在未来可以做到通过配置文件来调整中间件
A: 主要出于几个考虑
- 考虑现代微服务的架构下,多数业务无关的通用能力都会下沉到网关以及Mesh,因此一个服务的切面不会太多 , 在通过这样的方式来配置,成本是可以接受的 。
- 通过 Orchestrator 方式,用户还可以任意操作已添加的中间件,比如移除一些不必要的中间件,这是权重的方式无法做到的 。
- 当然如果以后有需要,现在的设计并不妨碍我们支持基于权重的方式
推荐阅读
- 《上传那些事儿之Nest与Koa》——文件格式怎么了!
- 四十七 SpringCloud微服务实战——搭建企业级开发框架:【移动开发】整合uni-app搭建移动端快速开发框架-添加Axios并实现登录功能
- 一篇文章带你了解NoSql数据库——Redis简单入门
- 微信小程序仿手机相册组件——简单版
- 个人如何开发手机游戏(如何自己开发一款手机游戏)
- 一篇文章带你了解服务器操作系统——Linux简单入门
- 一 意大利留学—米兰新美术学院 米兰新美术学院
- 二 【SSM】学习笔记——SpringMVC入门
- three.js 如何用webgl搭建一个3D库房,3D仓库,3D码头,3D集装箱可视化孪生系统——第十五课
- 四十六 SpringCloud微服务实战——搭建企业级开发框架:【移动开发】整合uni-app搭建移动端快速开发框架-环境搭建