用于 React 的 HTTP 服务器,Angular,等等

Go HTTP Server for React, Angular, etc

我在 GO 中为静态文件创建了这个小型 HTTP 服务器:

func wrapHandler(h http.Handler) http.HandlerFunc {
  return func(w http.ResponseWriter, r *http.Request) {
    h.ServeHTTP(srw, r)
    log.Printf("GET %s", r.RequestURI)
  }
}



func main() {
  http.HandleFunc("/", wrapHandler(
    http.FileServer(http.Dir("/static"))
  ))

  if err := http.ListenAndServe(":8080", nil); err != nil {
    panic(err)
  }
}

它与 React 和 Angular dist 文件完美配合(在转换它们之后)。但是,如果我已经选择了一条路线,例如http://example.org:8080/customers 并在浏览器中单击刷新 我收到 404 页面未找到。这是我的代码失败的唯一情况。

发生这种情况是因为在 React 上 Angular index.html 充当前端控制器并可以处理路由。但是,为了使其正常工作,我需要在内部将所有 not found 请求重定向到 index.html.

因为是 angular/react 处理路由,所以我不想为 angular/react 中创建的每个路由创建一个 http.HandleFunc。我想要类似于 express 的东西:

app.use(express.static(path.join(__dirname, 'static')));
app.use("*",function(req,res){
  res.sendFile(path.join(__dirname, 'static/index.html'));
});

或 NGINX:

try_files $uri $uri/ index.html;

关于如何在 go 中执行此操作的任何线索?

了解 Go 的 http 包如何处理路由匹配很重要,因为它与其他 languages/frameworks 有点不同。 HandleFunc 在后台使用 ServeMux 并且(来自 docs):

ServeMux is an HTTP request multiplexer. It matches the URL of each incoming request against a list of registered patterns and calls the handler for the pattern that most closely matches the URL. [emphasis mine]

鉴于此行为,我建议为每个静态文件夹创建显式处理程序(例如 css/js/),或将所有文件夹放在一个静态子文件夹中,然后用您的index.html 文件用于所有其他请求(使用根路由 (/))。这是可行的,因为路由前缀为 /css//js/ 的请求将更接近地匹配适当的静态处理程序路由,而所有其他请求不会因此仅最接近地匹配根路由。您只需要确保不要在前端创建冲突的路由。

这样,任何对 CSS/JS/image 资产的明确请求都将通过提供静态目录来处理,所有其他请求都将通过您的 React 应用程序得到响应。

这是一个示例(为简单起见,省略了您的 wrapHandler):

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        http.ServeFile(w, r, "static/index.html")
    })
    http.Handle("/js/", http.FileServer(http.Dir("static")))
    http.Handle("/css/", http.FileServer(http.Dir("static")))

    if err := http.ListenAndServe(":8080", nil); err != nil {
        panic(err)
    }
}

或者如果你想更明确一点:

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        http.ServeFile(w, r, "static/index.html")
    })
    
    jsFs := http.FileServer(http.Dir("static/js"))
    http.Handle("/js/", http.StripPrefix("/js", jsFs))
    
    cssFs := http.FileServer(http.Dir("static/css"))
    http.Handle("/css/", http.StripPrefix("/css", cssFs))

    if err := http.ListenAndServe(":8080", nil); err != nil {
        panic(err)
    }
}

我的网站的工作方式几乎相同(使用 Vue 而不是 React)。

一个改进

如上所述,您可以考虑将所有静态资源放在当前 static/ 文件夹的子文件夹中。考虑像这样构建文件:

public/
  index.html
  static/
    css/
    js/
    img/

这是构建 React 应用程序的默认文件结构(但 public 默认称为 build)。

这将使您能够以更精简的方式使用上述方法,因为您只需要一个文件服务器处理程序来处理所有静态资产。然后你可以使用下面的代码:

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        http.ServeFile(w, r, "public/index.html")
    })

    fs := http.FileServer(http.Dir("public/static/"))
    http.Handle("/static/", http.StripPrefix("/static", fs))

    if err := http.ListenAndServe(":8080", nil); err != nil {
        panic(err)
    }
}