Go语言动手写Web框架学习笔记-第一天

93 阅读2分钟

Go语言动手写Web框架学习笔记-第一天

go中的http.ListenAndServe方法

https://www.jianshu.com/p/6b77ada03487
https://blog.csdn.net/JunChow520/article/details/122231734

一、原生http库的http请求代码

import (
    "fmt"
    "log"
    "net/http"
)
​
func main() {
    http.HandleFunc("/", indexHandler)
    http.HandleFunc("/hello", helloHandler)
    log.Fatal(http.ListenAndServe(":9999", nil))
}
​
// handler echoes r.URL.Path
func indexHandler(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(w, "URL.Path = %q\n", req.URL.Path)
}
​
// handler echoes r.URL.Header
func helloHandler(w http.ResponseWriter, req *http.Request) {
    for k, v := range req.Header {
        fmt.Fprintf(w, "Header[%q] = %q\n", k, v)
    }
}
​
​

简化实现handle接口ServerHTTP方法代码解读

https://github.com/geektutu/7days-golang/blob/master/gee-web/day1-http-base/base2/main.go
​
实现自已的处理器
​
调用关系 http.ListenAndServer(":9999",engine) -- > engine 是handle接口
​
​
http.ListenAndServe(":9999", engine)
这个函数第二个参数engine是处理器,它需要实现http.handle的接口,接口的方法又是
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}
​
所以当你要实现自已的处理器就必须实现handle接口的ServerHttp方法
​
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)
    }
}
​
这边初版的方法是比较简单的,直接写死,根据req.URL.Path接收到的条件进行做相应的case 处理
​

更灵活的处理器

首先有以下几个问题要解决

  • 可存府注册请求接口pattern和handler的关系
  • 可灵活注册请求接口和函数的关系
  • 可查找和返回接口和函数的关系

开始看源码

源码地址:https://github.com/geektutu/7days-golang/blob/master/gee-web/day1-http-base/base3/gee/gee.goimport (
    "fmt"
    "log"
    "net/http"
)
//用type 简化匿名函数
// HandlerFunc defines the request handler used by gee
type HandlerFunc func(http.ResponseWriter, *http.Request)
​
​
// engine实体数据对象,这个要用来存储pattern和handler.pattern请求接口和handle映射函数
// Engine implement the interface of ServeHTTP
type Engine struct {
    router map[string]HandlerFunc
}
​
//初始化engine,为什么要返回指针,应该是本来方便addRoute
// New is the constructor of gee.Engine
func New() *Engine {
    return &Engine{router: make(map[string]HandlerFunc)}
}
​
//存储请求pattern和handler关系
func (engine *Engine) addRoute(method string, pattern string, handler HandlerFunc) {
    key := method + "-" + pattern
    log.Printf("Route %4s - %s", method, pattern)
    engine.router[key] = handler
}
​
//这个算是注册pattern和handle最终调用addroute去存到engine.router里
// GET defines the method to add GET request
func (engine *Engine) GET(pattern string, handler HandlerFunc) {
    engine.addRoute("GET", pattern, handler)
}
​
//这个算是注册pattern和handle最终调用addroute去存到engine.router里// 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)
}
​
//实现handle接口具体的处理接口
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 NOT FOUND: %s\n", req.URL)
    }
}
​
​

总结

参考源码1,任何一个请求抽象到底层就是io,i为进,o为出,在这里是req和response
那么如果我们有一台服务器需要接到不同的请求(用/urls来区分),那就必面为每一个urls都设定一个处理io流,接收和回应
所以就有了http.HandleFunc("/", indexHandler)这样的映射参数,前者为pattern,后者为handler函数。