如何用Go部署Polymer单页应用?页面空白问题排查
Great question! Based on your setup description, this is almost certainly a Go server configuration issue rather than a Polymer routing problem. Let's break down why, how to diagnose it, and fix it:
Why This Happens
Your server is correctly serving the root index.html (since you see the title and favicon), but it's failing to serve the ES module files like src/my-app.js. This usually boils down to one of two issues with your spaHandler implementation:
- The handler is incorrectly routing static asset requests to
index.htmlinstead of the actual file. - The path resolution logic in the handler isn't correctly mapping requests to files in your
staticdirectory.
Step 1: Diagnose with Browser DevTools
First, confirm what's happening with the failed request:
- Open your browser's DevTools (F12) and go to the Network tab.
- Refresh the page and look for the
src/my-app.jsrequest. Check:- Status Code: Is it 404 (file not found)? Or 200 but returning
index.htmlinstead of the JS file? - Response Content: If it's 200, click into the request to see if the response is your JS code or the full
index.html(this is a common red flag). - MIME Type: If you see an error like "Expected a JavaScript module script but got text/html", that confirms the server is sending
index.htmlinstead of the JS file.
- Status Code: Is it 404 (file not found)? Or 200 but returning
Step 2: Fix the Server Configuration
Assuming you're using the Gorilla Mux recommended spaHandler, let's verify and adjust its logic. Here's the standard implementation with key fixes highlighted:
package main import ( "encoding/json" "log" "net/http" "os" "path/filepath" "time" "github.com/gorilla/mux" ) type spaHandler struct { staticPath string indexPath string } func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Get absolute path to prevent directory traversal path, err := filepath.Abs(r.URL.Path) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } // Map request path to static directory path = filepath.Join(h.staticPath, path) // Check if the file exists info, err := os.Stat(path) if err != nil { if os.IsNotExist(err) { // File doesn't exist: serve SPA index http.ServeFile(w, r, filepath.Join(h.staticPath, h.indexPath)) return } // Other error: return 500 http.Error(w, err.Error(), http.StatusInternalServerError) return } // If it's a directory, serve the index.html inside it (if exists) if info.IsDir() { path = filepath.Join(path, h.indexPath) _, err := os.Stat(path) if err != nil { http.Error(w, "Directory index not found", http.StatusNotFound) return } } // Serve the static file with correct MIME types http.ServeFile(w, r, path) } func main() { router := mux.NewRouter() router.HandleFunc("/api/health", func(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(map[string]bool{"ok": true}) }) spa := spaHandler{staticPath: "static", indexPath: "index.html"} router.PathPrefix("/").Handler(spa) srv := &http.Server{ Handler: router, Addr: "127.0.0.1:8000", WriteTimeout: 15 * time.Second, ReadTimeout: 15 * time.Second, } log.Fatal(srv.ListenAndServe()) }
Key Fixes in This Implementation:
- Explicitly checks if the requested path is a directory and handles it properly (avoids serving
index.htmlfor valid directory paths without an index file). - Uses
http.ServeFiledirectly for existing files instead of delegating tohttp.FileServer, which ensures correct path resolution. - Adds better error handling for directory traversal and missing files.
Alternative Approach: Separate Static Asset Routing
If you want to avoid potential path resolution issues, split your routing into two parts: one for static assets, another for SPA routes:
func main() { router := mux.NewRouter() // Serve static assets directly from the /static/ path router.PathPrefix("/static/").Handler( http.StripPrefix("/static/", http.FileServer(http.Dir("static"))), ) // Handle API routes router.HandleFunc("/api/health", func(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(map[string]bool{"ok": true}) }) // Serve SPA index for all other routes spa := spaHandler{staticPath: "static", indexPath: "index.html"} router.PathPrefix("/").Handler(spa) srv := &http.Server{ Handler: router, Addr: "127.0.0.1:8000", WriteTimeout: 15 * time.Second, ReadTimeout: 15 * time.Second, } log.Fatal(srv.ListenAndServe()) }
For this to work, update your index.html script tag to use the /static/ prefix:
<script type="module" src="/static/src/my-app.js"></script>
Final Checks
- Verify that
static/src/my-app.jsactually exists (runls static/srcto confirm). - Clear your browser cache or do a hard refresh (Ctrl+Shift+R) to avoid cached responses.
内容的提问来源于stack exchange,提问作者amongil

