Day4构建gee

55 阅读3分钟

参考教程geektutu.com/post/gee.ht… 今天首先是写了base2的代码,其实和base1(Day3的代码)差不多,作者认为base2可以根据路径switch方法好一点,但是我认为base1的解耦性和可读性都更好,因为对于我这种小白如果不是先看过base1,直接看base2会有点一头雾水,又或许是注释没有写好。
我把base2的代码和注释粘贴下来,大家可以自行比对:

package main

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

// Engine is the uni handler for all requests
type Engine struct{}

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)
    log.Fatal(http.ListenAndServe(":1103", engine))
}

// test command
// $ curl "http://localhost:1103/"
// URL.Path = "/"
// $ curl "http://localhost:1103/hello"
//Header["User-Agent"] = ["curl/8.9.1"]
//Header["Accept"] = ["*/*"]

今天我认为比较重要的是base3的gee内容,这比较接近项目核心了(小白的一家之言,因为我感觉我是在模仿gin实现GET、POST、RUN等HTTP方法了),因为不是很熟悉go,所以我做了详细的注释,方便大家观看和我以后回顾。

首先是base3/main.go这里的GET、POST(怎么没有)、RUN,感觉和gin的效果好像一样捏嘻嘻,除了这个gee.New(),我记得gin是Gin.Default()还会自动生成Logger和Recovery中间件。代码如下:

package main

import (
    "fmt"
    "net/http"

    "github.com/EGG-Gerome/gee-web/day1-http-base/base3/gee"
)

func indexHandler(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(w, "URL.Path = %q\n", req.URL.Path)
}
func helloHandler(w http.ResponseWriter, req *http.Request) {
    for k, v := range req.Header {
       fmt.Fprintf(w, "Header[%q] = %q\n", k, v)
    }
}
func main() {
    r := gee.New()
    r.GET("/", indexHandler)
    r.GET("/hello", helloHandler)
    r.RUN(":1103")
}

接下来是重要的gee,感觉这个就是gin封装的一些HTTP方法(GET、POST、RUN)。

package gee

import (
    "fmt"
    "net/http"
)

/*
定义请求处理函数类型:
作用:定义一个函数类型 HandlerFunc,他接受两个参数

    http.RespondWriter:用于向客户端发送请求
    *http.Request: 客户端的HTTP 请求(指针类型)

意义:所有的路由处理函数都必须符合这个签名,确保接口统一
*/
// 用来规定传入的参数(这里的参数是函数)是这个模式的(函数会有这两个传入)
type HandlerFunc func(http.ResponseWriter, *http.Request)

/*
定义路由器核心结构 Engine
router:一个映射表(map),键为 URL 路径(如"/hello"),值为对应的处理函数(HandlerFunc 类型)
作用:Engine 是路由器的核心,负责存储路径与处理函数的映射关系,并在接收到请求时根据路径找到相应的处理函数
*/
type Engine struct {
    router map[string]HandlerFunc
}

/*
构造函数 New()
作用:创建并初始化一个 Engine 实例,返回其指针
关键点:

    make(map[string]HandlerFunc):初始化 map,避免空指针异常
    返回指针 *Engine 确保所有调用者共享同一个路由器实例(单例模式)
*/
func New() *Engine {
    return &Engine{
       router: make(map[string]HandlerFunc),
    }
}

/*
method(HTTP 请求方法)

    类型:string (eg "GET","POST","PUT")
    作用:指定请求的 HTTP 方法,区分对同一 URL 的不同操作

pattern(URL路径模式)

    类型:string(eg "/","/hello","/user:id")
    作用:指定请求的 URL 路径或路径模板,支持静态路径和动态参数
*/
func (engine *Engine) addRoute(method string, pattern string, handler HandlerFunc) {
    key := method + "-" + pattern
    engine.router[key] = handler
}

// GET defines the method to add GET request
func (engine *Engine) GET(pattern string, handler HandlerFunc) {
    engine.addRoute("GET", pattern, handler)
}

// POST defines the method to add POST request
func (engine *Engine) POST(pattern string, handler HandlerFunc) {
    engine.addRoute("POST", pattern, handler)
}

// RUN defines the method to start a http server
func (engine *Engine) RUN(addr string) (err error) {
    return http.ListenAndServe(addr, engine) // 第二个参数必须是一个实现了 http.Handler 接口的对象。
}

// 第二个参数必须是一个实现了 http.Handler 接口的对象。
// http.Handler 接口定义:
//
//  type Handler interface {
//     ServeHTTP(w ResponseWriter, r *Request)
//  }
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    key := req.Method + "-" + req.URL.Path
    if handler, ok := engine.router[key]; ok {
       handler(w, req)
    } else {
       fmt.Fprintf(w, "404 NOTFOUND: %s\n", req.URL)
    }
}

这个平台应该是昨天上传的,但是昨天晚上回去,忘了🥹