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

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

代码仓库:
github
gitee
本篇代码请选择分支demo1

中文注释,非常详尽可以配合食用
本篇设计说明草图:

一、Golang中的net/http标准库如何处理一个请求

func main() {
    http.HandleFunc("/", handler)
    http.HandleFunc("/count", counter)
    log.Fatal(http.ListenAndServe("localhost:9090", nil))
}

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path)
}

标准库中实现了监听,映射路由,和解析http报文。

浏览器的请求,http.ListenAndServe方法中,第一个参数是监听地址响,第二个参数代表处理所有的HTTP请求的实例,nil代表使用标准库中的实例处理。

我们通过第二个参数,转入到我们的实例,这样所有的HTTP请求就都交给了我们的实例来处理。

二、实现http.Handler接口

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

import (
   "fmt"
   "log"
   "net/http"
)

type Engine struct {}
/**
 * @Description:
   对Engine结构体的ServeHttp方法实现。ServeHTTP是Handler接口中的一个方法,在这里我们对这个方法进行实现,放入到我们的Engine中。这里就用上了我们的自定义方法来处理请求。
 * @receiver engine
 * @param w http.ResponseWriter(通过该ResponseWriter构造针对该请求的响应)
 * @param req *http.Request(Request中包括了发送来的HTTP请求的全部信息)
 */
func (engine *Engine)ServeHTTP(w http.ResponseWriter, req *http.Request) {
   switch req.URL.Path {
   case "/":
      fmt.Fprintf(w,"URL.Path = %q\n", req.URL.Path)
   case "/hello":
      for k, v := range req.Header {
         fmt.Fprintf(w,"Header[%q] = %q \n", k, v)
      }
   default:
      fmt.Fprintf(w,"404 NOT FOUND: %s\n", req.URL)
   }
}

func main() {
   engine := new(Engine)
    //此处后者的参数填入engine,让请求交由我们实现的engine实例来处理
   log.Fatal(http.ListenAndServe(":9090",engine))
}

三、实现简单的GET,POST方法,实现框架简单原型

设计自己的Engine结构体,然后将原来启动web项目的

   log.Fatal(http.ListenAndServe(":9090",engine))

封装成Run(addr),并将第二个参数的默认实例,直接写为我们的engine实例。

func (engine *Engine) Run(addr string) (err error) {
   return http.ListenAndServe(addr, engine)
}

这里engine涉及到一个转换,实现了接口方法的struct转换为接口类型

我们要实现的GET,POST方法。作用是,将路由和方法,注册进来,然后在监听的时候进行查找和处理。

这里设计一个router放入到我们的Engine结构体中:

// Engine 实现了"net/http"标准库中的 Handler 接口中的ServeHTTP方法
type Engine struct {
   //用于存储路由处理方法
   //key是方法类型加路径,value是用户的处理方法
   router map[string]HandlerFunc
}

我们的GET和POST方法就是这样实现的,调用addRoute方法,将路由和响应方法存入到router中:

// GET 实现的是Engine的处理GET请求的方法,注册到router中
func (engine *Engine) GET(path string, handler HandlerFunc) {
   engine.addRoute("GET", path, handler)
}

// POST 同上
func (engine *Engine) POST(path string, handler HandlerFunc) {
   engine.addRoute("POST", path, handler)
}

这里就是addRoute方法,method-path作为key,handler作为value

// Engine 中 addRoute 方法,在 router map[string]HandlerFunc 中存入对应处理方法
//存入形式为例如:{ "GET-/index" : 定义的处理方法 }engine
func (engine *Engine) addRoute(method string, path string, handler HandlerFunc) {
   key := method + "-" + path
   engine.router[key] = handler
}

我们再重写Handler接口中的ServeHTTP方法,通过req请求的路由查找router中注册的方法执行。

// ServeHTTP 方法的实现,用于实现处理HTTP请求
// 先解析req对应传入的路径,查找router中,如果有相应的处理方法,则执行处理方法,如果没有则返回找不到的提示
// 来自Handler接口
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
   key := req.Method + "-" + req.URL.Path
   //根据请求req中的数据,从router中取出对应的方法
   if handler, ok := engine.router[key]; ok {
      handler(w, req)
   } else {
      fmt.Fprintf(w, "could not find the route: %s\n", req.URL)
   }
}

雏形的全部源码:

/*
@Time : 2021/8/16 下午4:03
@Author : Mrxuexi
@File : Ez
@Software: GoLand
*/

package Ez

import (
   "fmt"
   "net/http"
)

// HandlerFunc 是Ez框架中定义的对请求的响应处理方法,默认传入这两个参数,针对http请求处理
type HandlerFunc func(http.ResponseWriter, *http.Request)

// Engine 实现了"net/http"标准库中的 Handler 接口中的ServeHTTP方法
type Engine struct {
   //用于存储路由处理方法
   //key是方法类型加路径,value是用户的处理方法
   router map[string]HandlerFunc
}

// ServeHTTP 方法的实现,用于实现处理HTTP请求
// 先解析req对应传入的路径,查找router中,如果有相应的处理方法,则执行处理方法,如果没有则返回找不到的提示
// 来自Handler接口
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
   key := req.Method + "-" + req.URL.Path
   //根据请求req中的数据,从router中取出对应的方法
   if handler, ok := engine.router[key]; ok {
      handler(w, req)
   } else {
      fmt.Fprintf(w, "could not find the route: %s\n", req.URL)
   }
}

// New 是Ez.Engine的构造函数
func New() *Engine {
   return &Engine{router: make(map[string]HandlerFunc)}
}

// Engine 中 addRoute 方法,在 router map[string]HandlerFunc 中存入对应处理方法
//存入形式为例如:{ "GET-/index" : 定义的处理方法 }engine
func (engine *Engine) addRoute(method string, path string, handler HandlerFunc) {
   key := method + "-" + path
   engine.router[key] = handler
}

// GET 实现的是Engine的处理GET请求的方法,注册到router中
func (engine *Engine) GET(path string, handler HandlerFunc) {
   engine.addRoute("GET", path, handler)
}

// POST 同上
func (engine *Engine) POST(path string, handler HandlerFunc) {
   engine.addRoute("POST", path, handler)
}

// Run 用于启动服务,直接制定用该路由的engine
func (engine *Engine) Run(addr string) (err error) {
   return http.ListenAndServe(addr, engine)
}

尝试使用:

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

import (
   "Ez"
   "fmt"
   "net/http"
)
func main() {
   r := Ez.New()
   r.GET("/hello", func(w http.ResponseWriter, req *http.Request) {
      fmt.Fprintf(w,"Hello")
   })
   r.Run(":9090")
}

成功!
后续我们继续完善内容

参考:

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

posted @ 2021-08-16 19:10  Mrxuexi  阅读(1397)  评论(0编辑  收藏  举报