Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to have golang redirect to frontend routing?

Currently I am serving my react app using the following setup

func main() {
    http.Handle("/", http.FileServer(http.Dir("./build/")))
    http.HandleFunc("/my_api", handler)
    http.ListenAndServe(":8090", nil)
}

and frontend

function App() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/my_frontend_path">MyPath</Link>
            </li>
          </ul>
        </nav>

        <Switch>
          <Route path="/my_frontend_path">
            <MyPath />
          </Route>
          <Route path="/">
            <Home />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

But when I directly access http://localhost:8090/my_frontend_path from the browser golang returns 404 page not found, is there a way to delegate any path not supported in main to default to frontend react router?

like image 757
user10714010 Avatar asked Nov 02 '20 17:11

user10714010


1 Answers

The issue you are experiencing is covered in some detail in the answers to this question. Its worth reading through the answers (particularly the accepted one) because there are a range of options (with varying downsides).

The basic issue is that when you make the initial request to http://localhost:8090/my_frontend_path your frontend react router is not running so the browser requests the page from the server; as my_frontend_path does not exist in the build folder http.FileServer returns a 404 page not found error.

I suspect that the simplest solution for you may be to use hash history (covered in the accepted answer).

However as you have provided some Go server side code there is a fairly simple, purely server side, option. The "Catch-all" approach works by returning index.html (and hence your app) for any path that is not handled elsewhere. This means that your application will be loaded and the router should detect the my_frontend_path and act accordingly (this is why the contents of index.html is returned rather than a redirect). One way of implementing this simply is:

const FSPATH = "./build/"

func main() {
    fs := http.FileServer(http.Dir(FSPATH))

    http.HandleFunc("/my_api", func(w http.ResponseWriter, _ *http.Request) { w.Write([]byte("API CALL")) })
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        // If the requested file exists then return if; otherwise return index.html (fileserver default page)
        if r.URL.Path != "/" {
            fullPath := FSPATH + strings.TrimPrefix(path.Clean(r.URL.Path), "/")
            _, err := os.Stat(fullPath)
            if err != nil {
                if !os.IsNotExist(err) {
                    panic(err)
                }
                // Requested file does not exist so we return the default (resolves to index.html)
                r.URL.Path = "/"
            }
        }
        fs.ServeHTTP(w, r)
    })
    http.ListenAndServe(":8090", nil)
}

As written this will return the contents of index.html if the file does not exist - this includes files like favicon.ico. If this is an issue you may want to add some limits (e.g. check the extension).

like image 129
Brits Avatar answered Nov 20 '22 03:11

Brits