revel扩展mime类型

 

      最近在学习ffos的应用开发,需要为每一个应用写一个manifest.webapp文件。这个文件是json格式的,但是不能以application/json类型返回,返回的Content-Type必须是application/x-web-app-manifest+json。

 

      刚开始用了一个笨方法来实现的。通过查看Static模块的代码,看到public下的文件,是通过RenderFile来实现的。就增加了一个filter,如果请求的文件是以.webapp为后缀名的,就通过c.Response.WriteHeader()来设置返回的Content-Type。这样,ffos的模拟器就可以通过这个manifest.webapp来安装这个web应用了。但是这样做有个小问题,当WriteHeader的时候,会出现一个error log:server.go:597: http: multiple response.WriteHeader calls。具体原因待查明。

 

      后来仔细看ffos的文档(链接在此:https://developer.mozilla.org/en-US/docs/Web/Apps/Manifest),发现里面写明了在apache和nginx下的Content-Type配置,在配置文件里增加了对应的MIME类型,web server就可以自动返回对应的Content-Type。以前在读revel源码的时候,好像见到了关于mime的东西。于是一顿“find xxx | grep xxx”。终于找到了revel中mime的配置,代码在github.com/robfig/revel/conf/mime-types.conf。只要增加一行:webapp=application/x-web-app-manifest+json,就可以了。迅速删掉之前非常2b的实现。

 

      后续:

      mime配置的地方找到了,那么它是在哪里读取并返回的呢?

      1.加载:github.com/robfig/revel/revel/run.go中runApp函数里,调用revel.LoadMimeConfig()。也就是说程序一跑起来,就加载一次。

      2.LoadMimeConfig的实现是在github.com/robfig/revel/util.go中,关键一句:

mimeConfig, err = LoadConfig("mime-types.conf")

         看到就是我前面修改的那个文件,mime-types.conf。

      3.当请求一个static文件时,通过返回一个BinaryResult,来进行response的数据填写。看文件results.go中的一段代码:

func (r *BinaryResult) Apply(req *Request, resp *Response) {
    disposition := string(r.Delivery)
    if r.Name != "" {
        disposition += fmt.Sprintf("; filename=%s", r.Name)
    }
    resp.Out.Header().Set("Content-Disposition", disposition)

    // If we have a ReadSeeker, delegate to http.ServeContent
    if rs, ok := r.Reader.(io.ReadSeeker); ok {
        http.ServeContent(resp.Out, req.Request, r.Name, r.ModTime, rs)
    } else {
        // Else, do a simple io.Copy.
        if r.Length != -1 {
            resp.Out.Header().Set("Content-Length", strconv.FormatInt(r.Length, 10))
        }
        resp.WriteHeader(http.StatusOK, ContentTypeByFilename(r.Name))
        io.Copy(resp.Out, r.Reader)
    }

    // Close the Reader if we can
    if v, ok := r.Reader.(io.Closer); ok {
        v.Close()
    }
}

        可以看加黑的一句,通过ContentTypeByFilename函数,从文件名称的名称找到对应的content type。

      4.最后看一下ContentTypeByFilename的实现,还是在util.go中:

func ContentTypeByFilename(filename string) string {
    dot := strings.LastIndex(filename, ".")
    if dot == -1 || dot+1 >= len(filename) {
        return DefaultFileContentType
    }

    extension := filename[dot+1:]
    contentType := mimeConfig.StringDefault(extension, "")
    if contentType == "" {
        return DefaultFileContentType
    }

    if strings.HasPrefix(contentType, "text/") {
        return contentType + "; charset=utf-8"
    }

    return contentType
}

        很简单,就是截取后缀名,然后通过之前加载的mimeConfig,找到对应的Content-Type。

 

 

posted @ 2013-12-31 17:14  coding4范儿  阅读(490)  评论(0编辑  收藏  举报