GO自研微服务框架-中间件

一.中间件

中间件的作用是给应用添加一些额外的功能,但是并不会影响原有应用的编码方式,想用的时候直接添加,不想用可以很轻松的去除,做到所谓的可插拔。

中间件的实现位置在哪里?

  1. 不能耦合在用户的代码中
  2. 需要独立存在,但又能拿到上下文,并能做出影响

位置:在处理器的前后

注意:中间件是一个调用链条,所以在处理真正的业务之前,可能会经过多个中间件

1. 定义中间件

type MiddlewareFunc func(handleFunc HandleFunc)HandleFunc

中间件的执行,定义为组级别。

type routerGroup struct {name             stringhandleFuncMap    map[string]map[string]HandleFunchandlerMethodMap map[string][]stringtreeNode         *treeNodepreMiddlewares   []MiddlewareFunc //前置中间件postMiddlewares  []MiddlewareFunc //后置中间件
}

2. 通用前置中间件

func (r *routerGroup) PreHandle(middlewareFunc ...MiddlewareFunc) {r.preMiddlewares = append(r.preMiddlewares, middlewareFunc...)
}
func (r *routerGroup) methodHandle(h HandleFunc, ctx *Context) {//前置中间件if r.preMiddlewares != nil {for _, middlewareFunc := range r.preMiddlewares {h = middlewareFunc(h)}}h(ctx)//后置中间件for _, middlewareFunc := range r.preMiddlewares {h = middlewareFunc(h)}
}
func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {e.httpRquestHandler(w, r)
}
func (e *Engine) httpRquestHandler(w http.ResponseWriter, r *http.Request) {method := r.Methodfor _, group := range e.routerGroups {routerName := SubStringLast(r.RequestURI, "/"+group.name)// get/1node := group.treeNode.Get(routerName)if node != nil && node.isEnd {//路由匹配上了ctx := &Context{W: w,R: r,}handle, ok := group.handleFuncMap[node.routerName][ANY]if ok {group.methodHandle(handle, ctx)handle(ctx)return}handle, ok = group.handleFuncMap[node.routerName][method]if ok {group.methodHandle(handle, ctx)return}//method 进行匹配w.WriteHeader(http.StatusMethodNotAllowed)fmt.Fprintf(w, "%s %s not allow\n", r.RequestURI, method)return}}w.WriteHeader(http.StatusNotFound)fmt.Fprintf(w, "%s not found\n", r.RequestURI)
}

3. 通用后置中间件

func (r *routerGroup) PostHandle(middlewareFunc ...MiddlewareFunc) {r.postMiddlewares = append(r.postMiddlewares, middlewareFunc...)
}
func (r *routerGroup) methodHandle(h HandleFunc, ctx *Context) {//前置中间件if r.preMiddlewares != nil {for _, middlewareFunc := range r.preMiddlewares {h = middlewareFunc(h)}}h(ctx)//后置中间件if r.postMiddlewares != nil {for _, middlewareFunc := range r.postMiddlewares {h = middlewareFunc(h)}}h(ctx)
}

4. 测试

func main() {engine := msgo.New()g := engine.Group("user")//g.Get("/hello", func(ctx *msgo.Context) {//	fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")//})g.PreHandle(func(next msgo.HandleFunc) msgo.HandleFunc {return func(ctx *msgo.Context) {fmt.Println("pre Handle")next(ctx)}})g.PostHandle(func(handleFunc msgo.HandleFunc) msgo.HandleFunc {return func(ctx *msgo.Context) {fmt.Println("post handle")}})g.Get("/hello/get", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")})g.Post("/hello", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s Post欢迎学习GO自研框架", "lisus2000")})g.Post("/info", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s info功能", "lisus2000")})g.Get("/get/:id", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s get user 路径变量的值", "lisus2000")})engine.Run()
}

5. 改造后置中间件

中间件的触发是等待用户处理函数的执行,也就是说前置中间件就可以完成后置中间件的功能,所以这里我们去除后置中间件

// 定义路由分组结构
type routerGroup struct {name             stringhandleFuncMap    map[string]map[string]HandleFunchandlerMethodMap map[string][]stringtreeNode         *treeNodemiddlewares      []MiddlewareFunc
}
func (r *routerGroup) Use(middlewareFunc ...MiddlewareFunc) {r.middlewares = append(r.middlewares, middlewareFunc...)
}
func (r *routerGroup) methodHandle(h HandleFunc, ctx *Context) {//前置中间件if r.middlewares != nil {for _, middlewareFunc := range r.middlewares {h = middlewareFunc(h)}}h(ctx)
}

测试代码

func main() {engine := msgo.New()g := engine.Group("user")//g.Get("/hello", func(ctx *msgo.Context) {// fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")//})g.Use(func(next msgo.HandleFunc) msgo.HandleFunc {return func(ctx *msgo.Context) {fmt.Println("pre Handle")next(ctx)fmt.Println("post Handler")}})g.Get("/hello/get", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")})g.Post("/hello", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s Post欢迎学习GO自研框架", "lisus2000")})g.Post("/info", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s info功能", "lisus2000")})g.Get("/get/:id", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s get user 路径变量的值", "lisus2000")})engine.Run()
}

6. 路由级别中间件

func (r *routerGroup) methodHandle(name string, mehtod string, h HandleFunc, ctx *Context) {//组通用中间件if r.middlewares != nil {for _, middlewareFunc := range r.middlewares {h = middlewareFunc(h)}}//路由级别中间件middlewareFuncs := r.middlewaresFuncMap[name][mehtod]if middlewareFuncs != nil {for _, middlewareFunc := range middlewareFuncs {h = middlewareFunc(h)}}h(ctx)
}
func (r *routerGroup) handle(name string, method string, handleFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {_, ok := r.handleFuncMap[name]if !ok {r.handleFuncMap[name] = make(map[string]HandleFunc)r.middlewaresFuncMap[name] = make(map[string][]MiddlewareFunc)}_, ok = r.handleFuncMap[name][method]if ok {panic("有重复的路由")}r.handleFuncMap[name][method] = handleFuncr.middlewaresFuncMap[name][method] = append(r.middlewaresFuncMap[name][method], middlewareFunc...)r.treeNode.Put(name)
}
func (r *routerGroup) Any(name string, handleFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {r.handle(name, ANY, handleFunc, middlewareFunc...)
}
func (r *routerGroup) Get(name string, handleFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {r.handle(name, http.MethodGet, handleFunc, middlewareFunc...)
}
func (r *routerGroup) Post(name string, handleFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {r.handle(name, http.MethodPost, handleFunc, middlewareFunc...)
}
func (r *routerGroup) Delete(name string, handlerFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {r.handle(name, http.MethodDelete, handlerFunc, middlewareFunc...)
}
func (r *routerGroup) Put(name string, handlerFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {r.handle(name, http.MethodPut, handlerFunc, middlewareFunc...)
}
func (r *routerGroup) Patch(name string, handlerFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {r.handle(name, http.MethodPatch, handlerFunc, middlewareFunc...)
}
func (r *routerGroup) Options(name string, handlerFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {r.handle(name, http.MethodOptions, handlerFunc, middlewareFunc...)
}
func (r *routerGroup) Head(name string, handlerFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {r.handle(name, http.MethodHead, handlerFunc, middlewareFunc...)
}

完整代码如下:

package msgoimport ("fmt""log""net/http"
)const ANY = "ANY"type HandleFunc func(ctx *Context)type MiddlewareFunc func(handleFunc HandleFunc) HandleFunc// 定义路由分组结构
type routerGroup struct {name               stringhandleFuncMap      map[string]map[string]HandleFuncmiddlewaresFuncMap map[string]map[string][]MiddlewareFunchandlerMethodMap   map[string][]stringtreeNode           *treeNodemiddlewares        []MiddlewareFunc
}type router struct {routerGroups []*routerGroup
}// Group 分组方法
func (r *router) Group(name string) *routerGroup {routerGroup := &routerGroup{name:               name,handleFuncMap:      make(map[string]map[string]HandleFunc),middlewaresFuncMap: make(map[string]map[string][]MiddlewareFunc),handlerMethodMap:   make(map[string][]string),treeNode:           &treeNode{name: "/", children: make([]*treeNode, 0)},}r.routerGroups = append(r.routerGroups, routerGroup)return routerGroup
}func (r *routerGroup) Use(middlewareFunc ...MiddlewareFunc) {r.middlewares = append(r.middlewares, middlewareFunc...)
}func (r *routerGroup) methodHandle(name string, mehtod string, h HandleFunc, ctx *Context) {//组通用中间件if r.middlewares != nil {for _, middlewareFunc := range r.middlewares {h = middlewareFunc(h)}}//路由级别中间件middlewareFuncs := r.middlewaresFuncMap[name][mehtod]if middlewareFuncs != nil {for _, middlewareFunc := range middlewareFuncs {h = middlewareFunc(h)}}h(ctx)
}func (r *routerGroup) handle(name string, method string, handleFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {_, ok := r.handleFuncMap[name]if !ok {r.handleFuncMap[name] = make(map[string]HandleFunc)r.middlewaresFuncMap[name] = make(map[string][]MiddlewareFunc)}_, ok = r.handleFuncMap[name][method]if ok {panic("有重复的路由")}r.handleFuncMap[name][method] = handleFuncr.middlewaresFuncMap[name][method] = append(r.middlewaresFuncMap[name][method], middlewareFunc...)r.treeNode.Put(name)
}func (r *routerGroup) Any(name string, handleFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {r.handle(name, ANY, handleFunc, middlewareFunc...)
}
func (r *routerGroup) Get(name string, handleFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {r.handle(name, http.MethodGet, handleFunc, middlewareFunc...)
}
func (r *routerGroup) Post(name string, handleFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {r.handle(name, http.MethodPost, handleFunc, middlewareFunc...)
}
func (r *routerGroup) Delete(name string, handlerFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {r.handle(name, http.MethodDelete, handlerFunc, middlewareFunc...)
}
func (r *routerGroup) Put(name string, handlerFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {r.handle(name, http.MethodPut, handlerFunc, middlewareFunc...)
}
func (r *routerGroup) Patch(name string, handlerFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {r.handle(name, http.MethodPatch, handlerFunc, middlewareFunc...)
}
func (r *routerGroup) Options(name string, handlerFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {r.handle(name, http.MethodOptions, handlerFunc, middlewareFunc...)
}
func (r *routerGroup) Head(name string, handlerFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {r.handle(name, http.MethodHead, handlerFunc, middlewareFunc...)
}type Engine struct {router
}func New() *Engine {return &Engine{router: router{},}
}
func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {e.httpRquestHandler(w, r)
}
func (e *Engine) Run() {http.Handle("/", e)err := http.ListenAndServe(":8111", nil)if err != nil {log.Fatal(err)}
}func (e *Engine) httpRquestHandler(w http.ResponseWriter, r *http.Request) {method := r.Methodfor _, group := range e.routerGroups {routerName := SubStringLast(r.RequestURI, "/"+group.name)// get/1node := group.treeNode.Get(routerName)if node != nil && node.isEnd {//路由匹配上了ctx := &Context{W: w,R: r,}handle, ok := group.handleFuncMap[node.routerName][ANY]if ok {group.methodHandle(node.routerName, ANY, handle, ctx)handle(ctx)return}handle, ok = group.handleFuncMap[node.routerName][method]if ok {group.methodHandle(node.routerName, method, handle, ctx)return}//method 进行匹配w.WriteHeader(http.StatusMethodNotAllowed)fmt.Fprintf(w, "%s %s not allow\n", r.RequestURI, method)return}}w.WriteHeader(http.StatusNotFound)fmt.Fprintf(w, "%s not found\n", r.RequestURI)
}

路由级别测试代码

package mainimport ("fmt""msgo"
)func Log(next msgo.HandleFunc) msgo.HandleFunc {return func(ctx *msgo.Context) {fmt.Println("打印请求参数")next(ctx)fmt.Println("返回执行")}
}func main() {engine := msgo.New()g := engine.Group("user")//g.Get("/hello", func(ctx *msgo.Context) {// fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")//})g.Use(func(next msgo.HandleFunc) msgo.HandleFunc {return func(ctx *msgo.Context) {fmt.Println("pre Handle")next(ctx)fmt.Println("post Handler")}})g.Get("/hello/get", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")}, Log)g.Post("/hello", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s Post欢迎学习GO自研框架", "lisus2000")})g.Post("/info", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s info功能", "lisus2000")})g.Get("/get/:id", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s get user 路径变量的值", "lisus2000")})engine.Run()
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/626474.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

减肥或者运动真的能改变面相吗?

减肥或运动真的能改变面相吗? 在我们日常生活中,经常可以听到一些关于面相的说法,比如“胖的人更有福气”、“瘦的人更有灵气”等等。那么,减肥或运动是否真的能够改变一个人的面相呢?下面我们就来探讨一下这个问题。…

高精度磁导航传感器MGS系列RS232|RS485|CANBUS通讯连线方法

高精度磁导航传感器MGS系列,包含:CNS-MGS-080N、CNS-MGS-160N等,具有1mm的检测精度,特别适应于⾼精度磁条导航。利⽤检测磁场相对位置来进⾏AGV的辅助定位对接,获得更⾼的导航、定位、驻⻋精度。 MGS系列磁导航传感器⽀…

多臂老虎机 “Multi-armed Bandits”

将强化学习与机器学习、深度学习区分开的最重要的特征为:它通过训练中信息来评估所采取的动作,而不是给出正确的动作进行指导,这极大地促进了寻找更优动作的需求。 1、多臂老虎机(Multi-armed Bandits)问题 赌场的老虎…

阿里云服务器选哪个好(各自的优势区别)

阿里云服务器该怎么选? 一:99元一年,每天花费0.27元 点击 实惠好用,祝从此链接购买的用户天天好事不断! 1.想省点钱的个人开发者建议选经济型e实例(2核2G,3M网络带宽,40G云盘) 2.可以用来干什么? 搭建中小…

系统语言德语时浮点数转化问题

原因 游戏上线后,玩家反馈系统语言为德语时会有问题,经排查测试,发现是德语的浮点数转化,也就是float.parser对于德语来说会有问题。 在中国和英国,表示小数是用 小数点,即:0.123。但是在德国…

2024 极术通讯-“周易”NPU与飞桨完成II 级兼容性测试,助力实现多样化AI部署

导读:极术社区推出极术通讯,引入行业媒体和技术社区、咨询机构优质内容,定期分享产业技术趋势与市场应用热点。 芯方向 Helium 技术讲堂 | 循环缓冲区的使用 当人工智能 (AI) 下沉到各式各样的应用当中,作为市场上最大量的物联网…

聚酰亚胺PI的特性有哪些?UV胶水能够粘接聚酰亚胺PI吗?又有哪些优势呢?

聚酰亚胺(Polyimide,PI)是一种高性能的聚合物材料,具有许多优异的特性。以下是聚酰亚胺的主要特性: 1. 高耐热性:聚酰亚胺的玻璃化转变温度和熔点都很高,可在非常高的温度下工作,其长…

Python 获取ResponseContent的文件名

通过网络请求下载的文件,有些时候不想重命名可以直接使用网页所返回的文件名 需要用到两个库 uuid 负责生成随机的数 unquote 负责解析编码后的字符 分了两种情况进行处理 1.找得到header 里面文件名 直接使用文件所返回的文件名 2.找不到header里面的文件…

leecode2719 | 统计整数数目

给你两个数字字符串 num1 和 num2 &#xff0c;以及两个整数 max_sum 和 min_sum 。如果一个整数 x 满足以下条件&#xff0c;我们称它是一个好整数&#xff1a; num1 < x < num2 min_sum < digit_sum(x) < max_sum. 请你返回好整数的数目。答案可能很大&#xff0…

极智芯 | 解读自动驾驶芯片之高通骁龙Ride系列

欢迎关注我的公众号「极智视界」,获取我的更多技术分享 大家好,我是极智视界,本文分享一下 解读自动驾驶芯片之高通骁龙Ride系列。 邀您加入我的知识星球「极智视界」,星球内有超多好玩的项目实战源码和资源下载,链接:https://t.zsxq.com/0aiNxERDq 对于自动驾驶芯片,之…

抖音弹幕玩法汉字找不同让鼠标指针自动漂浮的实现原理及代码

如下图&#xff0c;抖音直播间弹幕互动玩法&#xff0c;为了增强用户的视觉感知体验&#xff0c;在里面加了一个鼠标&#xff0c;来让用户感知到自己在操作。下一节我们将背景音乐也给加上去。 我们实现的方案是用anime.js动画&#xff0c;来让一个图片在指定区域范围内随机漂浮…

PDF文件中字体乱码的一种简单的处理方法

要解决问题先得碰到问题&#xff0c;碰到问题就迈出了解决问题的关键一步。 问题PDF文件的下载链接 这文件用Acrobat打开&#xff0c;无法搜索文本&#xff0c;复制文本出来也都是乱码。但用sumatra PDF打开就不存在这个问题&#xff01; 用Acrobat的印前检查解决。prefligh…

解决kubelet报failed to get imageFs info: non-existent label \“docker-images\“

问题&#xff1a; 一环境主机重启后&#xff0c;查看kubelet日志经常有大量无法回收镜像文件报错&#xff0c;会导致kubelet的pleg不健康&#xff0c;从而导致kubelet发生重启。报错如下&#xff1a; 解决办法 解决方法一&#xff1a; systemctl stop docker systemctl stop …

解锁Python神器vars:让你的代码瞬间脱颖而出!

vars()函数是一个内置函数&#xff0c;用于返回对象的__字典__&#xff0c;其中包含对象的__属性__。它适用于模块、类和实例对象&#xff0c;为你提供了访问对象属性的便捷方式。 1. vars() 函数概述 vars()函数有两种使用方式&#xff1a; 不带参数&#xff1a;返回当前作…

<软考高项备考>《论文专题 - 69 风险管理(1) 》

1 论文基础 1.1 写作要点 过程定义、作用写作要点、思路规划风险管理是定义如何实施项目风险管理活动的过程。作用:确保风险管理的水平、方法和可见度与项目风险程度相匹配&#xff0c;与对组织和其他干系人的重要程度相匹配。风险管理计划的内容、编写原则。结合风险管理计划…

20240104金融读报1分钟小得

数字技术引领将是补齐金融机构在普惠金融服务质量、效率等方面短板的有效方式 从省农担的名单制管理-》整村担保&#xff08;整个村子都担保&#xff0c;具体售给谁由银行决定&#xff09;

SAP不同语言开发

文章目录 1 Please write English Nmae2 go to goto menu and translation3 Write your target language .4 Please input Chinese5 Summary 1 Please write English Nmae 2 go to goto menu and translation 3 Write your target language . 4 Please input Chinese 5 Summary…

LeetCode——2085. 统计出现过一次的公共字符串

通过万岁&#xff01;&#xff01;&#xff01; 题目&#xff1a;给你两个字符串数组&#xff0c;然后让你统计字符串数组中&#xff0c;出现过分别只出现过一次的字符串的个数。思路&#xff1a;直接两个map&#xff0c;用于分别统计每个字符串数组中字符出现的次数。然后遍历…

从理论到实践:数字孪生技术的全面应用探讨

数字孪生是一种将实际物体或系统的数字模型与其实时运行状态相结合的概念。这一概念的核心在于创建一个虚拟的、与真实世界相对应的数字副本&#xff0c;以便监测、分析和优化实体系统的性能。 简单理解&#xff0c;数字孪生就是在一个设备或系统的基础上&#xff0c;创造一个…

pycharm 非局域网下 远程连接服务器docker中的conda环境

前提条件&#xff1a;有外网ip。 可以买个阿里云域名&#xff08;一般实验室配置&#xff09; 用蒲公英&#xff08;参考我的这篇文章&#xff0c;一般是个人使用&#xff0c;免费三个用户&#xff09; https://blog.csdn.net/weixin_75205835/article/details/135159099?spm1…