• Go语言Web框架gwk介绍 3


    Go语言Web框架gwk介绍 (三)

     

    上一篇忘了ChanResult

    ChanResult


    可以用来模拟BigPipe,定义如下

    type ChanResult struct {
        Wait        sync.WaitGroup
        Chan        chan string
        ContentType string
        Start       []byte
        End         []byte
        Timeout     time.Duration
    }
    

    ChanResult会先输出Start,然后读取Chan中的字符串输出到客户端,最后输出End。

    还忘了Controller的事件。

    如果Controller的每个Action执行前后需要执行一些相同的代码怎么办?这就需要用到ActionSubscriber接口:

    type ActionSubscriber interface {
        OnActionExecuting(action *ActionContext)
        OnActionExecuted(action *ActionContext)
        OnException(action *ActionContext)
    }
    

    OnActionExecuting在具体的Action执行之前执行,OnActionExecuted在具体的Action执行之后执行,OnException在具体的Action执行出错后执行。

    通过ActionSubscriber可以做权限验证,数据验证,记录日志,同一错误处理等等。

    ------

    GWK内部机制


    gwk的内部逻辑十分简单,它的核心对象是HttpServer,HttpContent, HttpProcessor。下面分别介绍。

    HttpServer


    前面的例子里已经演示了怎么用函数NewDefaultServer创建一个缺省配置的HttpServer实例,NewDefaultServer会调用函数ReadDefaultConfigFile来尝试读取默认的配置文件,如果./conf/web.conf存在,则解析这个配置文件,如果解析出错或者文件不存在则调用函数NewDefaultConfig获得缺省配置。

    你也可以用NewHttpServer传入WebConfig参数创建HttpServer实例。

    func NewHttpServer(config *WebConfig) (srv *HttpServer, err error) 
    

    WebConfig的定义在后面介绍。

    创建好HttpServer后,调用它的Start方法来监听http请求,启动Web服务。如果你的代码运行在Google App Engine这样不需要监听端口的平台,可以调用Setup方法来初始化HttpServer。Start方法内部实际上是先调用Setup方法,再调用http.Server的ListenAndServe方法。

    HttpServer内部会创建一个http.Server实例,可以通过InnerServer方法来获得这个http.Server。

    HttpServer有一个Variables字段,如果你有什么整个HttpServer共享的全局变量,可以放在Variables中。

    //server variables
    Variables map[string]interface{}
    

    HttpServer实现了http.Handler接口,ServeHTTP函数的内部流程是:

    1. 创建HttpContext
    2. 循环执行每一个HttpProcessor的Execute方法

    我们先介绍HttpContext。

    HttpContext


    HttpContext是对当前http请求的封装,定义如下:

    type HttpContext struct {
        Server *HttpServer
        Request *http.Request
        Resonse http.ResponseWriter
        Method string
        RequestPath string
        PhysicalPath string
        RouteData RouteData
        ViewData map[string]interface{}
        Result HttpResult
        Error error
        Flash map[string]interface{}
        Session Session
        SessionIsNew bool
    }
    
    • Server: 当前的HttpServer
    • Request: http.Request
    • Resonse: http.ResponseWriter
    • Method: http请求的method,比如GET,PUT,DELETE,POST...
    • RequestPath: http请求url的path部分
    • PhysicalPath: 请求对应的物理文件,只有请求的是静态文件时,该字段才有值
    • RouteData: RequestPath解析后的路由参数
    • ViewData: 存放传递给View模板的数据
    • Result: 此次请求的HttpResult
    • Error: 此次请求中的Error
    • Flash: 可以存放临时变量,生命周期为此次请求
    • Session: Session
    • SessionIsNew: Session是否此次在请求创建

    HttpContext还定义了若干方法简化一些常见的操作:

    RouteValue,读取RouteData里的数据

    func (ctx *HttpContext) RouteValue(name string) (string, bool) 
    

    FormValue,调用http.Request的FormValue,FV也是相同的逻辑

    func (ctx *HttpContext) FV(name string) string 
    func (ctx *HttpContext) FormValue(name string) string
    

    另外还有FormInt,FormIntOr,FormBool,FormBoolOr,FormFloat,FormFloatOr,前面已经做过介绍。

    ReqHeader,读取Http request header的数据

    func (ctx *HttpContext) ReqHeader(name string) string 
    

    SetHeader,设置Http resonse header的数据

    func (ctx *HttpContext) SetHeader(key string, value string) 
    

    AddHeader,向http response header添加数据

    func (ctx *HttpContext) AddHeader(key string, value string) 
    

    ContentType,设置http response header的"Content-Type"

    func (ctx *HttpContext) ContentType(ctype string) 
    

    Status,设置返回http status code

    func (ctx *HttpContext) Status(code int) 
    

    Accept,读取http request header的"Accept"

    func (ctx *HttpContext) Accept() string 
    

    Write,调用http response的Write方法

    func (ctx *HttpContext) Write(b []byte) (int, error) 
    

    Expires,设置http response header的"Expires"

    func (ctx *HttpContext) Expires(t string) 
    

    SetCookie,设置cookie

    func (ctx *HttpContext) SetCookie(cookie *http.Cookie) 
    

    Cookie,读取Cookie

    func (ctx *HttpContext) Cookie(name string) (*http.Cookie, error) 
    

    SessionId,返回SessionId,只有启用了Session才有效

    func (ctx *HttpContext) SessionId() string 
    

    GetFlash,读取Flash中的变量

    func (ctx *HttpContext) GetFlash(key string) (v interface{}, ok bool)
    

    SetFlash,设置Flash中的变量

    func (ctx *HttpContext) SetFlash(key string, v interface{})
    

    ReadBody,读取整个http request的内容

    func (ctx *HttpContext) ReadBody() ([]byte, error)
    

    Flush,Flush当前Response中的数据到客户端

    func (ctx *HttpContext) Flush() {
    

    前面介绍的ChanResult就是调用Flush把内容输出到客户端,代码基本逻辑如下:

    ctx.Write(c.Start)
    ctx.Flush()
    
    if c.Timeout < time.Millisecond {
        c.Timeout = defaultChanResultTimeout
    }
    
    waitchan := make(chan bool)
    donechan := make(chan bool)
    
    go func() {
        for s := range c.Chan {
            ctx.Write([]byte(s))
            ctx.Flush()
        }
        donechan <- true
    }()
    
    go func() {
        c.Wait.Wait()
        close(c.Chan)
        waitchan <- true
    }()
    
    select {
    case <-waitchan:
    case <-time.After(c.Timeout):
    }
    
    <-donechan
    ctx.Write(c.End)
    


    HttpProcessor


    HttpProcessor的定义如下

    type HttpProcessor interface {
        Execute(ctx *HttpContext)
        Register(server *HttpServer)
    }
    

    Execute负责处理http请求,Register会在HttpServer初始化时调用一次,如果你的HttpProcessor需要执行一些初始化代码,可以放在Register方法中。

    调用RegisterProcessor可以注册一个HttpProcessor

    func RegisterProcessor(name string, p HttpProcessor)
    

    注册的HttpProcessor存在ProcessTable类型的全局变量中

    type ProcessTable []*Process
    
    type Process struct {
        Name string
        Path string
        Method string 
        Handler HttpProcessor
    }
    

    如果一个Processor需要特定的条件才执行,可以设置它的Path和Method字段,Method是要匹配的http method,既GET、PUT、POST、DELETE...,"*"或者""匹配所有的http method,Path是要匹配的Request Path,目前版本是前缀匹配,以后可能改成支持通配符。

    HttpServer启动时,默认注册三个HttpProcessor:StaticProcessor、RouteProcessor、RenderProcessor。


    StaticProcessor


    StaticProcessor负责处理静态文件,如果请求的路径能匹配到物理文件,则将HttpContext的的Result设置为FileResult。

    StaticProcessor支持缓存静态文件以及自定义http response header。缓存静态文件在缓存一节详细介绍,自定义输出的http header是指为每个静态文件的Response设置你定义的http header,比如统一为静态文件设置Cache-Control。下面是配置的例子:

    #static processor config
    static_processor: {
    
        cache_enable:   true
        cache_expire:   3600
    
        header: {
            Cache-Control:  max-age=43200
            X-Title:        gwk-demo
        }
    }
    # -->end static processor
    


    RouteProcessor


    RouteProcessor负责按照你定义的路由规则调用具体的处理代码,逻辑很简单,只有几十行代码。


    RenderProcessor


    RenderProcessor负责执行HttpResult的Execute,也只有几十行代码。HttpResult没有赋值的话则返回404错误。


    自定义HttpProcessor


    你可以增删HttpProcessor或者调整顺序来改变默认的处理逻辑,比如你的程序是一个web api服务,不需要处理静态文件,则可以去掉RouteProcessor。ProcessTable定义了Append、InsertBefore、InsertAfter、Remove方法来简化对HttpProcessor的调整。


    http gzip压缩


    CompressProcessor可以对http输出做gzip压缩,需要注册到RenderProcessor之前才有效,其本质是用compressResponseWriter来代替默认的ResponseWriter。

    type CompressProcessor struct {
        Enable   bool
        Level    int
        MimeType string
    }
    
    type compressResponseWriter struct {
        rw            http.ResponseWriter
        writer        compresser
        contentType   string
        format        string
        headerWritten bool
    }
    

    CompressProcessor设计时考虑能够按照MimeType或者RequestPath来过滤需要压缩的内容,但一直没实现,因为访问量小流量小的网站开不开启gzip压缩意义不大,访问量大的网站一般会用一些http反向代理或者http缓存的服务,自己没必要处理gzip压缩。

    通过自定义HttpProcessor,你可以为全网站做统一的权限验证,访问限制,日志处理,错误处理等等。

    除了HttpProcessor,你还可以通过gwk的事件机制来实现这些逻辑。

     
     
    分类: Go
    标签: golangWeb FrameworkGWKMVC
  • 相关阅读:
    偶感
    数据库脚本开发中应注意的事项
    事务与隔离级别笔记
    Spring MVC学习笔记
    Eclipse环境配置与快捷命令
    告别2016,展望2017
    待总结项
    常见问题汇总
    我的工具箱
    精品文章收藏
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3247126.html
Copyright © 2020-2023  润新知