英文:
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论