问题现象
浏览器出现如下报错信息
检查发现, js 脚本的返回类型是 text/plain, 正常的值应该是 application/javascript
环境
- go 1.17.6
-
解决方式
调试
查 浏览器开发者工具-> 网络 每个 js 文件返回的类型是
text/plain在程序里将返回 Header 的
Content-Type打印出来, 失败在静态代理时好像不过中间件server.BindMiddlewareDefault(func(r *ghttp.Request) {fmt.Println(r.Response.Header().Get("Content-Type"))})
查 goframe 静态代理的代码实现方式, 找到
serveFile和serveFileSearch, 然后自己去实现.server.BindMiddlewareDefault(func(r *ghttp.Request) {r.Response.ServeFile(gfile.Join("./public", r.URL.Path))})
跟踪代码, 发现 goframe 在做文件代理时使用了
http.ServeContent, 这个库里实现 Content-Type又是如下的代码(具体位置 SDK中的net/http/fs.go中,这个应该是官方库的问题.)ctypes, haveType := w.Header()["Content-Type"]var ctype stringif !haveType {ctype = mime.TypeByExtension(filepath.Ext(name))if ctype == "" {// read a chunk to decide between utf-8 text and binaryvar buf [sniffLen]byten, _ := io.ReadFull(content, buf[:])ctype = DetectContentType(buf[:n])_, err := content.Seek(0, io.SeekStart) // rewind to output whole fileif err != nil {Error(w, "seeker can't seek", StatusInternalServerError)return}}w.Header().Set("Content-Type", ctype)} else if len(ctypes) > 0 {ctype = ctypes[0]}
发现没有进入下一个中间件
server.BindMiddlewareDefault(func(r *ghttp.Request) {r.Middleware.Next()fmt.Println(r.Response.Header().Get("Content-Type"))})
输出的类型都是
text/plain与我们查到的mime.TypeByExtension返回的情况不一致 应该是test/javascript,断点到函数内去看对应的变量. 输出确实是text/plainctypes生成代码可以看出,在本身有值时就不会去用mime库生成类型值.我们利用中间件为文件直接加上head
server.BindMiddlewareDefault(func(r *ghttp.Request) {builtinTypesLower := map[string]string{".avif": "image/avif",".css": "text/css; charset=utf-8",".gif": "image/gif",".htm": "text/html; charset=utf-8",".html": "text/html; charset=utf-8",".jpeg": "image/jpeg",".jpg": "image/jpeg",".js": "text/javascript; charset=utf-8",".json": "application/json",".mjs": "text/javascript; charset=utf-8",".pdf": "application/pdf",".png": "image/png",".svg": "image/svg+xml",".wasm": "application/wasm",".webp": "image/webp",".xml": "text/xml; charset=utf-8",}typ, ok := builtinTypesLower[gfile.Ext(r.URL.Path)]if ok {r.Response.Header().Set("Content-Type", typ)}r.Middleware.Next()fmt.Println(r.URL.Path, ":", r.Response.Header().Get("Content-Type"))})
这个实现不够完美, 没有利用goframe已实现的静态文件代理功能, 再看 Mine 库 找到
AddExtensionType.我们把 js 的对应类型改了即可.err := mime.AddExtensionType(".js", "application/javascript; charset=utf-8")if err != nil {fmt.Println(err)return}
重新调试发现又不是下面的问题
mine 库返回的类型是 ~~`text/javascript`和要求的`application/javascript`不一致.~~下面说我给出的解决方案增加中间件, 并自己代理静态文件
这是我想到的第一步解决, 看到那个函数实现就是判断是否已经设置过类型, 没有设置就会去查库, 那我们就在开始把 header 加上 让它不去查库. 这里做一个前置中间件根据文件类型把参数设置上即可, 而由于自带的静态文件代理不能通过中间件截取, 这里我们将自己代理静态文件 ```go // 下面是全局注册 server.BindMiddlewareDefault(ChangeType) server.BindMiddlewareDefault(func(req *ghttp.Request) { req.Response.ServeFile(gfile.Join(“./public”, req.URL.Path)) })
func ChangeType(r *ghttp.Request) {
builtinTypesLower := map[string]string{".avif": "image/avif",".css": "text/css; charset=utf-8",".gif": "image/gif",".htm": "text/html; charset=utf-8",".html": "text/html; charset=utf-8",".jpeg": "image/jpeg",".jpg": "image/jpeg",".js": "application/javascript; charset=utf-8",".json": "application/json",".mjs": "text/javascript; charset=utf-8",".pdf": "application/pdf",".png": "image/png",".svg": "image/svg+xml",".wasm": "application/wasm",".webp": "image/webp",".xml": "text/xml; charset=utf-8",}typ, ok := builtinTypesLower[gfile.Ext(r.URL.Path)]if ok {r.Response.Header().Set("Content-Type", typ)fmt.Println(r.URL.Path, typ)}r.Middleware.Next()
}
<a name="b1xbI"></a>## 修改 Mine 库的默认值上一个方案的写法, 会让我们自己去对静态代理做处理, 要做好就需要自己去实现 gf 框架实现的静态代理功能, 那能否再找到一个优雅的处理方式?<br />继续研究 Mine 库发现 `AddExtensionType`函数, 可以直接修改默认值. 将下述代码放在 main 函数开头即可解决此问题, 同时可以正常使用 gf 的静态文件代理功能.```goerr := mime.AddExtensionType(".js", "application/javascript; charset=utf-8")if err != nil {fmt.Println(err)return}
