第三篇的模仿还是从这段base on Gin的项目代码开始

模仿的程度当然是从简。前面的文章描述了 Server 接口

type Server interface {
	http.Handler
	addRoute(method string, path string, handler HandleFunc)
	Start(addr string) error
}

其中的http.Handler长这样子

回顾前面的文章可以看到HttpServer 就是继承了这个接口

而新增的addRoute方法是复杂度最高的地方之一。文章的目的是为了描述服务器收到http请求后的基础流程,所有的设计会从简。

在写的过程中,发现addRoute方法应该是放到Route里,然后再httpserver里面的ServeHTTP方法里面match到路由,再从match到的route里面获取handler。

日志方面暂时从简,使用fmt直接打印到console

还是用前文出现过的Person struct

type Person struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

So:这是HttpServer

package geetime_yk

import (
	"net"
	"net/http"
)

// 这个就是添加路由时候的参数,相当于MVC模型的controller,当然Gin不是MVC额
type HandleFunc func(ctx Context)

// 这里引用Router
type HttpServer struct {
	router *Router
}

type Server interface {
	http.Handler

	Start(addr string) error
}

func (h *HttpServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
	ctx := Context{
		Req:  req,
		Resp: resp,
	}

	key := req.Method + "_" + req.URL.Path
	handle := h.router.get(key)
	handle.handler(ctx)

}

func (h *HttpServer) Start(addr string) error {
	l, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	return http.Serve(l, h)
}


router,本来这个是比较复杂的,有资料里面使用了树形结构,我就简单的method + path 了事

package geetime_yk

import "fmt"

type Router struct {
	nodes map[string]*Node
}

type Node struct {
	path    string
	handler HandleFunc
	route   string
	method  string
}

func newRouter() Router {
	return Router{
		nodes: map[string]*Node{},
	}
}

func (r *Router) addRoute(method string, path string, handler HandleFunc) {

	key := method + "_" + path
	_, ok := r.nodes[key]
	if !ok {
		nodeTemp := &Node{
			path:    path,
			method:  method,
			handler: handler,
		}
		r.nodes[key] = nodeTemp
	} else {
		panic("路由重复")
	}

}

func (r *Router) get(key string) *Node {
	n, ok := r.nodes[key]
	if ok {
		return n
	} else {
		fmt.Println("路由不存在")
		return nil
	}
}

Context文件

package geetime_yk

import (
	"encoding/json"
	"errors"
	"net/http"
)

type Context struct {
	Req  *http.Request
	Resp http.ResponseWriter

	RespStatusCode int
	RespData       []byte
}

func (c *Context) BindJSON(val any) error {
	if c.Req.Body == nil {
		return errors.New("body is nil")
	}

	decoder := json.NewDecoder(c.Req.Body)
	decoder.DisallowUnknownFields()
	return decoder.Decode(val)

}

func (c *Context) RespJSONOK(val any) error {
	return c.RespJSON(http.StatusOK, val)
}

func (c *Context) RespJSON(code int, val any) error {
	bs, err := json.Marshal(val)
	if err != nil {
		return err
	}
	c.RespStatusCode = code
	c.RespData = bs

	//json.Marshal(c)
	//c.Resp.Write([]byte(c))
	c.Resp.WriteHeader(code)
	c.Resp.Write(c.RespData)
	return nil
}

文件里面的2行代码,我是比较意外的

	c.Resp.WriteHeader(code)
	c.Resp.Write(c.RespData)

writeheader()方法居然传入的是code,就是http的code,例如200。


test文件,为了纪念毕业年代很火的4399,那个找PHP还需要会C++的年代,port用了4399

package geetime_yk

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

func TestHttpServer_ServeHTTP(t *testing.T) {

	r := newRouter()
	
	r.addRoute(http.MethodPost, "/people/get", func(ctx Context) {

		var people Person
		if err := ctx.BindJSON(&people); err != nil {
			fmt.Println("bindjsonErr:", err.Error())
			ctx.RespJSON(300, "bindJsonError")
			return
		}

		fmt.Println(people.Name)

		ctx.RespJSONOK(people)

	})

	serv := HttpServer{
		router: &r,
	}

	serv.Start("0.0.0.0:4399")
}

如果这里BindJSON不传指针的话,会有异常。

终于顺利得到想要的结果