day02 | 上下文实现 | web框架 | 笔记心得

160 阅读1分钟

day02 | 上下文实现

上下文 | 屏蔽内部细节

简化处理细节 | 提高使用的便捷

  • 每一次对于Web服务请求,主要是访问响应,体现在程序上就是ResponseWriter *Request.在这个互联请求过程还有很多其余信息,例如Request信息

    // go官方 Request结构体
    type Request struct {
    	Method string
    	URL *url.URL
    	Proto      string // "HTTP/1.0"
    	ProtoMajor int    // 1
    	ProtoMinor int    // 0
    	Header Header
    	Body io.ReadCloser
    	GetBody func() (io.ReadCloser, error)
    	ContentLength int64
    	TransferEncoding []string
    	Close bool
    	Host string
    	Form url.Values
    	PostForm url.Values
    	MultipartForm *multipart.Form
    	Trailer Header
    	RemoteAddr string
    	RequestURI string
    	TLS *tls.ConnectionState
    	Cancel <-chan struct{}
    	Response *Response
    	ctx context.Context
    }
    
  • 我们没必要每一次传递输入和输出信息,只需要设置一个结构体和定义一组方法,来实现彼此间的交互,具体传入的参数可以是我们自己指定的.习惯上,把这样的结构体成为Context

  • 这样一来,对外只需要暴露接口,简化了使用的复杂度.要求我们在内部实现即可

Context内部实现

ConText结构体
  • 我们对于Context可以先补充部分信息,例如

    type Context struct {
    	// 在之前的使用里面 这两个 用w 和 r代替
    	// 接受到和要处理的
    	Writer http.ResponseWriter
    	Req    *http.Request
    
    	Path, Method string
    
    	StatusCode int
    }
    
Context实现的方法
  • newContext

    func newContext(w http.ResponseWriter, r *http.Request) *Context {
    	return &Context{
    		Writer: w,
    		Req:    r,
    		Path:   r.URL.Path,
    		Method: r.Method,
    	}
    }
    
  • PostForm

    // PostForm 直接调用FormValue 以下类似
    func (c *Context) PostForm(key string) string {
    	return c.Req.FormValue(key)
    }
    
  • Query

    // Query 中调用GET方法
    // Get gets the first value associated with the given key.
    // If there are no values associated with the key, Get returns
    // the empty string. To access multiple values, use the map
    // directly.
    func (c *Context) Query(key string) string {
    	return c.Req.URL.Query().Get(key)
    }
    
  • Status

    // Status 调用WriteHeader 方法
    // WriteHeader sends an HTTP response header with the provided
    // status code.
    func (c *Context) Status(code int) {
    	c.StatusCode = code
    	c.Writer.WriteHeader(code)
    }
    
  • SetHeader

    // SetHeader 一下方法实现 同上思路
    func (c *Context) SetHeader(key, value string) {
    	c.Writer.Header().Set(key, value)
    }
    
  • String | JSON |Data | HTML

    func (c *Context) String(code int, format string, values ...interface{}) {
    	// 调用刚写好的方法
    	c.SetHeader("Content-Type", "text/plain")
    	c.Status(code)
    	// 格式化 fmt.Sprintf的两个参数 format string, a ...any
    	c.Writer.Write([]byte(fmt.Sprintf(format, values)))
    }
    
    func (c *Context) JSON(code int, obj interface{}) {
    	c.SetHeader("Content-Type", "application/json")
    	c.Status(code)
    	val := json.NewEncoder(c.Writer)
    	if err := val.Encode(obj); err != nil {
    		// http.Error所需要的参数 w ResponseWriter, error string, code int
    		http.Error(c.Writer, err.Error(), 500)
    	}
    }
    
    func (c *Context) Data(code int, data []byte) {
    	c.Status(code)
    	// http.ResponseWriter 需要给回复的信息
    	c.Writer.Write(data)
    }
    
    func (c *Context) HTML(code int, html string) {
    	c.SetHeader("Content-Type", "text/html")
    	c.Status(code)
    	c.Writer.Write([]byte(html))
    }
    
    

优化Engine | 在实现上下文的基础上

  • 只是在部分函数上进行优化处理即可
Engine结构体
  • 我们对于Engine可以直接存储router指针

  • HandleFunc可以直接由*Context代替http.ResponseWriter,和 *http.Request简化实现

    // 处理函数
    type HandleFunc func(*Context)
    
    type Engine struct {
    	router *router
    }
    
Engine实现的方法
  • New

    func New() *Engine {
    	return &Engine{router: newRounter()}
    }
    
  • GET和POST方法不需要改变

    // get方法实现
    func (engine *Engine) GET(pattern string, handler HandleFunc) {
    	engine.addRoute("GET", pattern, handler)
    }
    
    // post方法实现
    func (engine *Engine) POST(pattern string, handler HandleFunc) {
    	engine.addRoute("POST", pattern, handler)
    }
    
  • ServeHTTP | handler中的接口方法

    // run 开始运行
    func (engine *Engine) Run(addr string) (err error) {
    	return http.ListenAndServe(addr, engine)
    }
    
    // 直接给出上下文环境 让路由自己实现的 handle 方法 处理上下文 即可
    func (engine *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    	c := newContext(w, r)
    	engine.router.handle(c)
    }
    

Router| 路由设置

  • 优化处理了一下 上下文 ,自然在 建立路由过程之中,首要的实现绑定路由 其次是处理方法HandleFunc
Router 结构体
  • 我们对于router先完善最重要的信息,路径实现方法

    type router struct {
    	handlers map[string]HandleFunc
    }
    
Router 实现的方法
  • newRounter | 建立新的路由

    func newRounter() *router {
    	return &router{handlers: make(map[string]HandleFunc)}
    }
    
  • addRouter | 增加路由

    func (r *router) addRouter(method, pattern string, handler HandleFunc) {
    	key := method + "-" + pattern
    	r.handlers[key] = handler
    }
    
  • handle | 进行路由匹配,如果不存在则表示404

    func (r *router) handle(c *Context) {
    	// 对应上文的
    	key := c.Method + "-" + c.Path
    	if handler, err := r.handlers[key]; err {
    		handler(c)
    	} else {
    		c.String(http.StatusNotFound, "404 NOT FOUND:%s \n", c.Path)
    	}
    }
    

完整代码比较长 | 存储于Github仓库

附录 | 参考 | 好文

  1. 七天动手实现go-web框架
  2. 官方文档
  3. 微信读书中go开发实战
  4. go框架-Ez