File Server

如何用 go-zero 服务同时提供文件服务?

可以通过 go-zerorest.WithFileServer(path, dir) 来给 restful 服务增加文件服务能力。

示例代码如下:

  1. package main
  2. import (
  3. "net/http"
  4. "github.com/zeromicro/go-zero/rest"
  5. )
  6. func main() {
  7. // 在 `html` 目录下有需要对外提供的文件,比如有个文件 `index.html`,
  8. // 以 `/static/index.html` 这样的路径就可以访问该文件了。
  9. server := rest.MustNewServer(rest.RestConf{
  10. Host: "localhost",
  11. Port: 4000,
  12. }, rest.WithFileServer("/static", http.Dir("html")))
  13. defer server.Stop()
  14. server.AddRoute(rest.Route{
  15. Method: http.MethodGet,
  16. Path: "/hello",
  17. Handler: helloHandler,
  18. })
  19. server.Start()
  20. }
  21. func helloHandler(w http.ResponseWriter, r *http.Request) {
  22. w.WriteHeader(http.StatusOK)
  23. w.Write([]byte("Hello, World!"))
  24. }

这仅仅是个示例,一般不用做生产服务,或者当生产服务很简单的时候可以考虑使用,但不是最佳实践,一般会通过 nginx 或者云存储提供。

如何用 go-zero 服务部署 react 打包产物?

我们知道前端很多路由都是虚拟路由,并非像静态配置文件那样访问的每个路径都在文件中能一一匹配到,像 react-route 提供的路由,其实所有路由访问的页面都是基于 index.html 来渲染页面的,因此当我们部署服务后从 index 一层一层访问二级页面是没问题,但是每当直接从url地址打开二级页面或者刷新二级页面时,你会发现页面 404 了,这就需要我们在服务端做一些处理。在 not found handler 中将服务端不存在的路由转发到根路由 / 去。

部分关键目录信息树:

  1. .
  2. ├── main.go
  3. ├── etc
  4. ├── internal
  5. ├── config
  6. ├── handler
  7. ├── logic
  8. ├── svc
  9. └── types
  10. └── public
  11. └── static
  12. ├── css
  13. ├── js
  14. └── media
  15. 13 directories

参考代码:

  1. const basename = "/web" // 虚拟路由根路径
  2. // public 为前端打包产物相对于 main 函数的路径
  3. //go:embed public
  4. var assets embed.FS
  5. flag.Parse()
  6. var c config.Config
  7. conf.MustLoad(*configFile, &c)
  8. sub, _ := fs.Sub(assets, "public") // 读取对应目录下的内容,这里的public和上面embed目录(单目录,并非完整路径)对应
  9. //fs := http.Dir("public") // 如果不走 embed 形式,则用此方式 serve 前端目录,然后注释下一行代码即可。
  10. fs := http.FS(sub)
  11. fileServer := http.FileServer(fs)
  12. server := rest.MustNewServer(c.RestConf,
  13. rest.WithNotFoundHandler(&NotFoundHandler{// 自定义 NotFoundHandler,对虚拟路由做处理
  14. fs: fs,
  15. fileServer: fileServer,
  16. }),
  17. rest.WithFileServer(basename, fs))
  18. defer server.Stop()
  19. ctx := svc.NewServiceContext(c)
  20. handler.RegisterHandlers(server, ctx)
  21. fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
  22. server.Start()
  23. }
  24. type NotFoundHandler struct {
  25. fs http.FileSystem
  26. fileServer http.Handler
  27. }
  28. func (n NotFoundHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  29. filePath := strings.TrimPrefix(path.Clean(r.URL.Path), basename)
  30. if len(filePath) == 0 {
  31. filePath = basename
  32. }
  33. file, err := n.fs.Open(filePath)
  34. switch {
  35. case err == nil:
  36. n.fileServer.ServeHTTP(w, r)
  37. _ = file.Close()
  38. return
  39. case os.IsNotExist(err):
  40. r.URL.Path = "/" // all virtual routes in react app means visit index.html
  41. n.fileServer.ServeHTTP(w, r)
  42. return
  43. default:
  44. http.Error(w, "not found", http.StatusNotFound)
  45. return
  46. }
  47. }

go-zero 版本:>= v1.7.0