使用Golang和Chi路由器提供SPA进入循环。

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

Serving SPA with Golang and Chi router enters a loop

问题

我正在尝试使用Golang提供一个SPA,并且解决404错误成为了一个挑战。

  • 在init()函数中,我将index.html文件缓存到内存中。
  • 我创建了一个indexHandler函数,用于返回缓存的index文件。
  • 我在router.NotFound()函数中调用indexHandler函数,以便将路由返回给SPA。
  • 我使用FileServer来提供其他静态文件。

问题是,当我尝试在浏览器中访问应用程序时,它会无限重新加载。

我期望index.html由indexHandler函数提供,其他文件由FileServer提供,并且处理找不到页面的错误由SPA处理。

英文:

I´m trying to serve a SPA with Golang and solving the 404 error became a challenge.

  • In the init() func I buffered into memory the index.html file.
  • I created a indexHandler that returns the index file buffered.
  • I call the indexHandler in the router.NotFound() func so the route is returned to the SPA.
  • I serve the rest of the static files with FileServer.

The problem is that it seems to be in a loop, when I try to access the app in the browser, it reloads itself indefinitely.

package main

import (
	"log"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"

	"github.com/joho/godotenv"
)

var indexBuffer []byte

func main() {	
	r := chi.NewRouter()
	
	r.Use(middleware.RequestID)
	r.Use(middleware.RealIP)
	r.Use(middleware.Logger)
	r.Use(middleware.Recoverer)

	fs := http.FileServer(http.Dir(os.Getenv("FRONTEND_PATH")))
	r.Handle("/app/static/*", http.StripPrefix("/app/static/", fs))

	apiRouter := chi.NewRouter()
	apiRouter.Get("/cargas", handlerCargas)

	r.Mount("/app/api", apiRouter)

	r.NotFound(indexHandler)

	log.Fatal(http.ListenAndServe(":8000", r))
}

func init() {
	err := godotenv.Load()
	if err != nil {
		log.Fatal("Erro ao ler .env verifique!")
	}

	indexBuffer, err = os.ReadFile(filepath.Join(os.Getenv("FRONTEND_PATH"), "index.html"))
	if err != nil {
		log.Fatal("Erro ao tentar bufferizar a index na init().")
	}
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	w.Write(indexBuffer)
}

I´m expecting index.html to be served by the indexHandler and the rest of the files be served by the FileServer and errors of page not found be handled by the SPA.

答案1

得分: 1

我在这里找到了一个解决方案:

我修改了我的代码,并使用以下方式使用FileServer:

// FileServer para SPA
func FileServerSPA(r chi.Router, public string, static string) {

	if strings.ContainsAny(public, "{}*") {
		panic("FileServer does not permit URL parameters.")
	}

	root, _ := filepath.Abs(static)
	if _, err := os.Stat(root); os.IsNotExist(err) {
		panic("Static Documents Directory Not Found")
	}

	fs := http.StripPrefix(public, http.FileServer(http.Dir(root)))

	if public != "/" && public[len(public)-1] != '/' {
		r.Get(public, http.RedirectHandler(public+"/", 301).ServeHTTP)
		public += "/"
	}

	r.Get(public+"*", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		file := strings.Replace(r.RequestURI, public, "/", 1)
		if _, err := os.Stat(root + file); os.IsNotExist(err) {
			http.ServeFile(w, r, path.Join(root, "index.html"))
			return
		}
		fs.ServeHTTP(w, r)
	}))
}

在main()函数中:

// ...
// 在所有api的端点之后

frontendPath := os.Getenv("FRONTEND_PATH")
FileServerSPA(r, "/app", frontendPath)

log.Fatal(http.ListenAndServe(":8000", r))

希望对你有帮助。

英文:

I found a solution here

I adapted my code and used the FileServer as below:

// FileServer para SPA
func FileServerSPA(r chi.Router, public string, static string) {

	if strings.ContainsAny(public, "{}*") {
		panic("FileServer does not permit URL parameters.")
	}

	root, _ := filepath.Abs(static)
	if _, err := os.Stat(root); os.IsNotExist(err) {
		panic("Static Documents Directory Not Found")
	}

	fs := http.StripPrefix(public, http.FileServer(http.Dir(root)))

	if public != "/" && public[len(public)-1] != '/' {
		r.Get(public, http.RedirectHandler(public+"/", 301).ServeHTTP)
		public += "/"
	}

	r.Get(public+"*", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		file := strings.Replace(r.RequestURI, public, "/", 1)
		if _, err := os.Stat(root + file); os.IsNotExist(err) {
			http.ServeFile(w, r, path.Join(root, "index.html"))
			return
		}
		fs.ServeHTTP(w, r)
	}))
}

and, in main():

// ...
// after all the endpoints of the api

frontendPath := os.Getenv("FRONTEND_PATH")
FileServerSPA(r, "/app", frontendPath)

log.Fatal(http.ListenAndServe(":8000", r))

Hope it helps

huangapple
  • 本文由 发表于 2023年6月20日 21:30:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/76515271.html
匿名

发表评论

匿名网友

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

确定