Go语言动手写Web框架 - Gee第二天 上下文Context

48 阅读5分钟

Go语言动手写Web框架 - Gee第二天 上下文Context

回顾第一天

每个web的io请求由http原生模块的req和response负责. 考虑到请求必须逻辑划分并挂载了资源,所以请求一个资源是需要这三个要素
​
这三个要素分别是
请求方法
资源路径
处理请求函数(handlerFunc)
​
GET /hello func(http.ResponseWrite, *http.Request) 
​
处理请求函数并不需要返回值,因为请求路径在request里,而处理请求函数有io的ouput对象http.ResponseWrite(简称w),可以直接返回,所以不需要返回值。而是直接通过w回应给client.
​
由于我们的web是有诸多资源的,因此对于一个请求的三要素是需要有一种管理的方法,这管理的方法涉及以下这些
​
1、如何储存请求关系
2、如何注册请求
3、如何查询和返回
4、如何启动
​
​
-- 准备工作
   请求处理对象是engine
   type Engine struct {
    router map[string]HandlerFunc
}
​
   -- 请求的方法包括以下功能
      1、各种请求方法的声明和注册
      2、添加路由的内部逻辑方法
      3、查询路由关系的逻辑方法
      
-- 如何启动
​
先从4、如何启动开始看:
调用关系 http.ListenAndServer(":9999",engine) -- > engine的类型是handle接口,而这里的eninge是自定义的,默认空是由原生的handle接管。
​
handler有一个方法是ServerHTTP,这个方法用于接管所有的请求并返回相应的请求的handlerFunc
​
http.ListenAndServe(":9999", engine)
这个函数第二个参数engine是处理器,它需要实现http.handle的接口,接口的方法又是
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}
​
​
-- 如何注册
针对engine对象声明请求方法(GET、POST)时就是进行了注册,注册的同时会调用二级方法addRoute把三要素加入enigine.router的数据表里面,key是method+pattern(urls)
​
func (engine *Engine) GET(pattern string, handler HandlerFunc) 
func (engine *Engine) POST(pattern string, handler HandlerFunc) 
func (engine *Engine) addRoute(method string, pattern string, handler HandlerFunc)
​
​
-- 如何查询和返回处理函数
查询返回其实就是在handler接口的ServerHttp方法里,这个又属于engine对象。一开始启动就以参数的形式传给http.ListenAndServe
​
​
-- 如何存储
   type Engine struct {
    router map[string]HandlerFunc
}
​

第二天

主要内容是以下几块

  • 封装了response的各种方法,为了这个封装了一个context对象
  • 把原先的engine的路由的存储和注册和处理逻辑独立抽象出一个router struct,包括一系统路由相关的方法.
  • engine struct包含 router子struct对象,并包括一系统engine相关方法

一、封装resopnse

对Web服务来说,无非是根据请求*http.Request,构造响应http.ResponseWriter。但是这两个对象提供的接口粒度太细,比如我们要构造一个完整的响应,需要考虑消息头(Header)和消息体(Body),而 Header 包含了状态码(StatusCode),消息类型(ContentType)等几乎每次请求都需要设置的信息。因此,如果不进行有效的封装,那么框架的用户将需要写大量重复,繁杂的代码,而且容易出错。针对常用场景,能够高效地构造出 HTTP 响应是一个好的框架必须考虑的点。
​
一个回应需要几个要素:status、code、header、回应内容、还有可能是一些req进来的上下文数据
​
封装后代码:
c.JSON(http.StatusOK, gee.H{
    "username": c.PostForm("username"),
    "password": c.PostForm("password"),
})
​
// 为了构建嵌套的回应对象,参照c.Json方法(封装后代码)
type H map[string]interface{}
​
//response对象因为有很多上下文需要在整个处理过程中传递,于是封装了一个context对象,包括req,writer,path(req),method(req),statusCode
type Context struct {
    // origin objects
    Writer http.ResponseWriter
    Req    *http.Request
    // request info
    Path   string
    Method string
    // response info
    StatusCode int
}
​
//创建 concontext
func newContext(w http.ResponseWriter, req *http.Request) *Context {
    return &Context{
        Writer: w,
        Req:    req,
        Path:   req.URL.Path,
        Method: req.Method,
    }
}
​
//PostForm方法
func (c *Context) PostForm(key string) string {
    return c.Req.FormValue(key)
}
​
func (c *Context) Query(key string) string {
    return c.Req.URL.Query().Get(key)
}
​
//返回stauts主要是http.code
func (c *Context) Status(code int) {
    c.StatusCode = code
    c.Writer.WriteHeader(code)
}
​
//设置header
func (c *Context) SetHeader(key string, value string) {
    c.Writer.Header().Set(key, value)
}
​
​
//响应文本类型内容
func (c *Context) String(code int, format string, values ...interface{}) {
    c.SetHeader("Content-Type", "text/plain")
    c.Status(code)
    c.Writer.Write([]byte(fmt.Sprintf(format, values...)))
}
​
​
//响应json内容,这里用到gee.H,type H map[string]interface
func (c *Context) JSON(code int, obj interface{}) {
    c.SetHeader("Content-Type", "application/json")
    c.Status(code)
    encoder := json.NewEncoder(c.Writer)
    if err := encoder.Encode(obj); err != nil {
        http.Error(c.Writer, err.Error(), 500)
    }
}
​
//字节流回应
func (c *Context) Data(code int, data []byte) {
    c.Status(code)
    c.Writer.Write(data)
}
​
//html方式回应
func (c *Context) HTML(code int, html string) {
    c.SetHeader("Content-Type", "text/html")
    c.Status(code)
    c.Writer.Write([]byte(html))
}
​

二、抽出router对象

type router struct {
    handlers map[string]HandlerFunc
}
​
func newRouter() *router {
    return &router{handlers: make(map[string]HandlerFunc)}
}
​
func (r *router) addRoute(method string, pattern string, handler HandlerFunc) {
    log.Printf("Route %4s - %s", method, pattern)
    key := method + "-" + pattern
    r.handlers[key] = handler
}
​
func (r *router) handle(c *Context) {
    key := c.Method + "-" + c.Path
    if handler, ok := r.handlers[key]; ok {
        handler(c)
    } else {
        c.String(http.StatusNotFound, "404 NOT FOUND: %s\n", c.Path)
    }
}

三、主engine 对象

主启动,在这里要创建router,创建context,在main创建engine,并调用engine.run()- http.ListenAndServe() - engine.ServerHTTP()

//原先定义HandlerFunc是如下 type HandlerFunc func(http.ResponseWriter, *http.Request) 
//因为response,req已经封装成了Context,所以改成如下//interface和type定义的函数或方法里的参数,我看到只给变量类型没给变量别名
// HandlerFunc defines the request handler used by gee
type HandlerFunc func(*Context)
​
​
//router封装成了独立的对象和方法了
// Engine implement the interface of ServeHTTP
type Engine struct {
    router *router
}
​
// New is the constructor of gee.Engine
func New() *Engine {
    return &Engine{router: newRouter()}
}
​
func (engine *Engine) addRoute(method string, pattern string, handler HandlerFunc) {
    engine.router.addRoute(method, pattern, handler)
}
​
// GET defines the method to add GET request
func (engine *Engine) GET(pattern string, handler HandlerFunc) {
    engine.addRoute("GET", pattern, handler)
}
​
// 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)
}
​
//主动启,创建context,把请求扔入engine并返回相应的handlefunc处理
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    c := newContext(w, req)
    engine.router.handle(c)
}