param, ok := req.Query("param") / param, ok := req.Header.Get("param") / err := xxx.Bind(req, ¶m)
之类的代码,这和业务毫无关系- 没有请求/响应的结构化实体: 如果有开发过这些框架中间件的同学一定知道,大部分框架中间件的协议定义都是以
http.Request/httpResponse
为主体的,这意味着如果不做任何前置处理,你只能通过字节数组来感知请求与响应
这在部分场景都不太方便,比如:根据请求、响应的结构体是否具备某些特征(比如接口)来执行某些特定的业务通用逻辑;又或者想在中间件中融入一些自动化的参数校验逻辑,因为你没有一个具体的结构化对象;再或者你不想要在每一个 API handler 中去设置一个响应的 Wrapper(通常它类似于{code: 0, msg: "", data:{}}
),想要在中间件去自动包装上它,也很难执行;最后就是——如果只依靠http.Request/httpResponse
,你也难在中间件感知到其他参与者的处理状态, 。
工作原理带着上面提到的这些问题,我们来看看 Droplet 的工作原理是怎样的,如下图所示:

文章插图
如我所说的那样,Droplet 的核心在于
提供基于pipeline的请求/响应处理能力
,因此我们可以看见这个图中涉及的所有模块都是基于 pipleline,可以说 Droplet 的所有能力都是由其扩展而来 。这里我们先介绍下图中出现的几个中间件(Middleware,这是组成 pipepine 关键元素):- HttpInfoInjector: 注入 http 相关的一些信息,如 requestid, http.Request 等
- RespReshape: 根据 handler 的响应结果来进行一些调整,包括:发生错误时设置上默认的错误码、错误信息;如果缺少响应 wrapper 时包装上配置好的 wrapper
- HttpInput: 如果你设置了 API 的输入参数类型,那么该中间件会自动根据 Content-Type、 struct tag 来读取对应的参数值,同时自动使用 validator 来检测参数错误
- TrafficLog: 如名字所示,如果你配置了响应的 logger,那么该中间件会执行日志记录 。请注意该中间件工作在其他默认中间件的后面、你的handler之前,因此它统计的耗时是你业务函数的真正耗时,而不包含其他中间件的耗时时间,你可以考虑通过 网关 或 Mesh 来记录完整的接口耗时 。
TipsGetStart
- Middleware 处理请求和响应顺序是相反的——即第一个处理请求的中间件它会是最后一个处理响应的 。
- 框架工作在应用层的优势有两点:
- 与接入层框架解耦,保证绝项目代码可平滑 扩展/切换 其他接入层框架
- 能够获取到结构化的接口 输入参数 与 输出参数 你可以对其进行更具精细的切面操作
这里以首先获取对应 wrapper 的 submodule:Gin
为例,其他框架类似 。
go get github.com/shiningrush/droplet/wrapper/gin// if you want to ensure the droplet is latest, you can get dropletgo get github.com/shiningrush/droplet
然后程序代码如下:package mainimport ( "reflect" "github.com/gin-gonic/gin" "github.com/shiningrush/droplet/core" "github.com/shiningrush/droplet/wrapper" ginwrap "github.com/shiningrush/droplet/wrapper/gin")func main() { r := gin.Default()// 使用 wrapper 包装原始的 API r.POST("/json_input/:id", ginwrap.Wraps(JsonInputDo, wrapper.InputType(reflect.TypeOf(&JsonInput{})))) r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")}type JsonInput struct {// 从 path 读取, 并且为必须参数 IDstring`auto_read:"id,path" json:"id" validate:"required"`// 从 header 读取, 并且为必须参数 Userstring`auto_read:"user,header" json:"user" validate:"required"`// 从 json unmarshal 后的ips字段读取 IPs[]string `json:"ips"`// 从 json unmarshal 后的 count 字段读取 Count int`json:"count"`// 读取原始的 http body,接收参数类型必须为 []byte or io.ReadCloser Body[]byte`auto_read:"@body"`}func JsonInputDo(ctx core.Context) (interface{}, error) { input := ctx.Input().(*JsonInput) return input, nil}
经验总结扩展阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 《上传那些事儿之Nest与Koa》——文件格式怎么了!
- 四十七 SpringCloud微服务实战——搭建企业级开发框架:【移动开发】整合uni-app搭建移动端快速开发框架-添加Axios并实现登录功能
- 一篇文章带你了解NoSql数据库——Redis简单入门
- 微信小程序仿手机相册组件——简单版
- 个人如何开发手机游戏(如何自己开发一款手机游戏)
- 一篇文章带你了解服务器操作系统——Linux简单入门
- 一 意大利留学—米兰新美术学院 米兰新美术学院
- 二 【SSM】学习笔记——SpringMVC入门
- three.js 如何用webgl搭建一个3D库房,3D仓库,3D码头,3D集装箱可视化孪生系统——第十五课
- 四十六 SpringCloud微服务实战——搭建企业级开发框架:【移动开发】整合uni-app搭建移动端快速开发框架-环境搭建