发布于 

go-Web框架实现02

go-Web框架实现 | 笔记整理!

day02 | 上下文实现

上下文 | 屏蔽内部细节

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // 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可以先补充部分信息,例如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    type Context struct {
    // 在之前的使用里面 这两个 用w 和 r代替
    // 接受到和要处理的
    Writer http.ResponseWriter
    Req *http.Request

    Path, Method string

    StatusCode int
    }
Context实现的方法
  • newContext

    1
    2
    3
    4
    5
    6
    7
    8
    func newContext(w http.ResponseWriter, r *http.Request) *Context {
    return &Context{
    Writer: w,
    Req: r,
    Path: r.URL.Path,
    Method: r.Method,
    }
    }
  • PostForm

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

    1
    2
    3
    4
    5
    6
    7
    8
    // 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

    1
    2
    3
    4
    5
    6
    7
    // 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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    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简化实现

    1
    2
    3
    4
    5
    6
    // 处理函数
    type HandleFunc func(*Context)

    type Engine struct {
    router *router
    }
Engine实现的方法
  • New

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 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中的接口方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 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先完善最重要的信息,路径实现方法

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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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