手把手和你一起实现一个Web框架实战——EzWeb框架[Go语言笔记]Go项目实战

Posted 学习先生

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手把手和你一起实现一个Web框架实战——EzWeb框架[Go语言笔记]Go项目实战相关的知识,希望对你有一定的参考价值。

手把手和你一起实现一个Web框架实战——EzWeb框架(五)[Go语言笔记]Go项目实战

代码仓库:
github
gitee
中文注释,非常详尽,可以配合食用
本篇代码,请选择demo5

中间件实现

一、Context设计

type Context struct {
   Writer http.ResponseWriter
   Req *http.Request
   //请求的信息,包括路由和方法
   Path string
   Method string
   Params map[string]string   /*用于存储外面拿到的参数 ":xxx" or "*xxx" */
   //响应的状态码
   StatusCode int
   //中间件
   handlers []HandlerFunc
   index    int   /* 用于记录当前执行到第几个中间件 */
}

我们每次请求生成的context,我们选择在其中放入和我们中间件和执行控制变量index

二、中间件对路由组的注册方法

// 将路中间件,放入路由组的中间件方法切片中
func (group *RouterGroup) Use(middlewares ...HandlerFunc)  {
	group.middlewares = append(group.middlewares, middlewares...)
}

当每个请求到来后,ServeHTTP函数执行时,它将该生成一个context并将进行传入的url和路由组前缀做前缀对比,找到满足条件的路由组,取出它的中间件,然后存入到生成的context。

// ServeHTTP 方法的实现,用于实现处理HTTP请求
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	var middlewares []HandlerFunc
	for _, group := range engine.groups {
		//比对路由组存的前缀和请求路径,把属于这个请求映射的路由组中的中间件取到
		//意思就是比对发现该请求属于哪一个路由组,需要哪些中间件,取出来执行
		if strings.HasPrefix(req.URL.Path, group.prefix) {
			middlewares = append(middlewares, group.middlewares...)
		}
	}
	//根据req和w实例一个context
	c := newContext(w, req)
	//将取道的中间件赋给这个context
	c.handlers = middlewares
	//通过封装好的context执行处理
	engine.router.handle(c)
}

三、处理函数

在处理函数handle()中,我们根据路由拿到已经注册的方法,放入到中间件后,在通过Next函数进行处理

//根据context中存储的 c.Method 和 c.Path 拿到对应的处理方法,进行执行,如果拿到的路由没有注册,则返回404
func (r *router) handle(c *Context)  {
	//获取匹配到的节点,同时也拿到两类动态路由中参数
	n, params := r.getRoute(c.Method, c.Path)
	if n != nil {
		c.Params = params
		//拿目的节点中的path做key来找handlers
		key := c.Method + "-" + n.path
		//根据路径拿到处理器
		c.handlers = append(c.handlers, r.handlers[key])
	}else {
		//不存在节点的情况下,给生成的c加入一个404方法
		c.handlers = append(c.handlers, func(c *Context) {
			c.String(http.StatusNotFound, "404 NOT FOUND: ", c.Path)
		})
	}
	c.Next()
}

四、Next方法执行处理

index进行控制,遍历完c.handlers中存储的方法执行。

// 当中间件调用了 Next 方法时,就往后执行下一个,同时index交由下一个中间件控制
func (c *Context) Next()  {
	c.index++
	//执行完之后所有的handlers
	for ;c.index < len(c.handlers); c.index++{
		c.handlers[c.index](c)
	}
}

这里的日志中间件在Next中被执行时,也调用了Next函数。这里第四行的执行巧妙的做到了,控制index++,执行下一个中间件,直到执行完了又回到Logger执行,回到原来的Next的for循环,发现不满足继续循环的条件,然后退出。

func Logger() HandlerFunc {
   return func(c *Context) {
      t := time.Now()
      c.Next()
      log.Printf("[%d] %s in %v", c.StatusCode, c.Req.RequestURI, time.Since(t))
   }
}

demo测试:

/*
@Time : 2021/8/16 下午4:01
@Author : mrxuexi
@File : main
@Software: GoLand
*/
package main

import (
	"Ez"
	"net/http"
)
func main() {
	r := Ez.New()
	//给所有的路由组都添加了中间件logger
	r.Use(Ez.Logger())
	api := r.Group("/api")

	api.POST("/hello", func(c *Ez.Context) {
		c.JSON(http.StatusOK,Ez.H{
				"message" : "hello",
		})
	})
//next的应用
	api.Use(func(c *Ez.Context) {
		c.JSON(200,Ez.H{
			"test" : "middleware2-1",
		})
		c.Next()
		c.JSON(200, Ez.H{
			"test" : "middleware2-2",
		})
	})

	r.Run(":9090")
}

成功!

参考:

[1]: https://github.com/geektutu/7days-golang/tree/master/gee-web ""gee""
[2]: https://github.com/gin-gonic/gin ""gin""

以上是关于手把手和你一起实现一个Web框架实战——EzWeb框架[Go语言笔记]Go项目实战的主要内容,如果未能解决你的问题,请参考以下文章

手把手教导实战Python Web项目

手把手教导实战Python Web项目

Spring Boot+扫码登录从0到1,手把手实战!

Spring Boot+扫码登录从0到1,手把手实战!

Android开源实战:手把手带你实现一个简单好用的搜索框(含历史搜索记录)

实战手把手 | Flink安装指南