How to server static files with virtual hosts functioality in Go

huangapple go评论83阅读模式
英文:

How to server static files with virtual hosts functioality in Go

问题

如何在Go中为虚拟主机提供静态文件(使用FileServer)?

如果我有自己的处理函数,这个任务似乎很容易解决[1]:

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, world!")
    })
    http.HandleFunc("qa.example.com/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, improved world!")
    })
    http.ListenAndServe(":8080", nil)
}

但是,如果我需要为虚拟主机提供静态文件(使用FileServer),该怎么办?

这个代码片段:

r.PathPrefix("qa.example.com/").Handler(http.FileServer(http.Dir("/static/qa/")))

不起作用 - 它被忽略了。

我做错了什么?
这种方法一般是错误的吗?

英文:

How can I server static files (with FileServer) for a virtual host in Go?

If I have my own handler function, the task seems to be easily solvable [1]:

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, world!")
    })
    http.HandleFunc("qa.example.com/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, improved world!")
    })
    http.ListenAndServe(":8080", nil)
}

But what if I need to serve static files (with FileServer)
for a virtual host?

This

r.PathPrefix("qa.example.com/").Handler(http.FileServer(http.Dir("/static/qa/")))

does not work — it is just ignored.

What am I doing wrong?
Is this approach generally wrong?

答案1

得分: 2

package main

import (
	"embed"
	"fmt"
	"net/http"
	"strings"
	"time"
)

//go:embed *.go
var f embed.FS

func main() {
	// embed.Fs默认使用当前时间或环境变量作为默认修改时间。
	now := time.Now()
	// 使用主机名映射到http.FileSystem
	vhosts := make(map[string]http.FileSystem)
	vhosts["qa.example.com"] = http.FS(f)    // 来自embed.FS
	vhosts["my.example.com"] = http.Dir(".") // 来自http.Dir

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello, world!")
	})
	http.HandleFunc("/static/", func(w http.ResponseWriter, r *http.Request) {
		// 查找主机
		fs, ok := vhosts[r.Host]
		if !ok {
			w.WriteHeader(404)
			w.Write([]byte("404 not found vhost"))
			return
		}
		// 从http.FileSystem打开文件
		file, err := fs.Open(strings.TrimPrefix(r.URL.Path, "/static/"))
		if err != nil {
			// 参考go1.18.3/net/http/fs.go中的toHTTPError函数处理文件错误。
			w.Write([]byte("检查错误是否为403、404或500"))
			return
		}
		stat, _ := file.Stat()
		// 修复embed修改时间为零的问题。
		modtime := stat.ModTime()
		if modtime.IsZero() {
			modtime = now
		}
		// 响应
		http.ServeContent(w, r, stat.Name(), modtime, file)

	})

	http.ListenAndServe(":8080", nil)
}

运行测试执行命令 curl -H "Host: my.example.com" 127.0.0.1:8080/static/01.go,将 01.go 替换为你的静态文件名。

英文:
package main

import (
	"embed"
	"fmt"
	"net/http"
	"strings"
	"time"
)

//go:embed *.go
var f embed.FS

func main() {
	// embed.Fs defaule modtime use now or env value.
	now := time.Now()
	// use mapping host to http.FileSystem
	vhosts := make(map[string]http.FileSystem)
	vhosts["qa.example.com"] = http.FS(f)    // from embed.FS
	vhosts["my.example.com"] = http.Dir(".") // from http.Dir

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello, world!")
	})
	http.HandleFunc("/static/", func(w http.ResponseWriter, r *http.Request) {
		// find host
		fs, ok := vhosts[r.Host]
		if !ok {
			w.WriteHeader(404)
			w.Write([]byte("404 not found vhost"))
			return
		}
		// open file from http.FileSystem
		file, err := fs.Open(strings.TrimPrefix(r.URL.Path, "/static/"))
		if err != nil {
			// reference go1.18.3/net/http/fs.go toHTTPError function hander file error.
			w.Write([]byte("check err is 403 or 404 or 500"))
			return
		}
		stat, _ := file.Stat()
		// fix embed modtime is zero.
		modtime := stat.ModTime()
		if modtime.IsZero() {
			modtime = now
		}
		// response
		http.ServeContent(w, r, stat.Name(), modtime, file)

	})

	http.ListenAndServe(":8080", nil)
}

run test exec command curl -H "Host: my.example.com" 127.0.0.1:8080/static/01.go, 01.go replacte your static filename.

答案2

得分: 0

host/path注册一个处理程序。仅在调用文件处理程序时去除/path部分。

此注册将从目录./static/qa/qa.example.com/static/*提供文件服务。

http.HandleFunc("qa.example.com/static/", http.StripPrefix("/static", http.FileServer(http.Dir("./static/qa/"))))
英文:

Register a handler for host/path. Strip the /path part only when invoking the file handler.

This registration serves files for qa.example.com/static/* from the directory ./static/qa/.

http.HandleFunc("qa.example.com/static/", http.StripPrefix("/static", http.FileServer(http.Dir("./static/qa/")))

huangapple
  • 本文由 发表于 2022年6月13日 05:48:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/72596134.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定