第三篇的模仿还是从这段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不传指针的话,会有异常。
终于顺利得到想要的结果
