7

I'm setting up a web server with Go (using Echo) as the backend and Angular 6 as the frontend. What I do is make a simple app using Angular-cli 'ng new my-app', add a helloworld component and a '/helloworld' route and then build it into production with 'ng build --prod' which output as 'dist' folder. Folder structure:

dist
├── assets
│   ├── icons
│   └── logo.png
├── favicon.ico
├── index.html
├── main.js
├── polyfills.js
├── runtime.js
└── styles.css

I then have Go serve the static files in that 'dist' folder with following code main.go

func main() {
    e := echo.New()
    e.Static("/", "dist")
    e.File("/", "dist/index.html")
    e.Start(":3000")
}

Now when I use the browser and go to 'localhost:3000/' then the page will be serve correctly, I can navigate around using href thanks to Angular routing ,for example to: 'localhost:3000/home' the page will show correctly but if I try refreshing it then Echo will return a page content that shows:

{"message":"Not Found"}

I know I can setup the route manually like this:

e.File("/home","dist/index.html")

However if I have a lot more routes then its quite a hassle to do all that.

What I need is that any route that's not defined for Echo will be map to 'index.html'. I did try with:

e.File("/*", "dist/index.html")

and

e.GET("/*", func(c echo.Context) 
    return c.File("dist/index.html")
})

but then I get a blank page with error

"Uncaught SyntaxError: Unexpected token <  " 

with all 3 files main.js, polyfill.js and runtime.js

I'm new to Echo so I don't know how to do this.

Increasingly Idiotic
  • 5,700
  • 5
  • 35
  • 73

4 Answers4

3

The problem isn't strictly related to Echo. The way Angular does routing is that it does NOT request the page from the server. It changes the URL without actually requesting another page from the server.

Thus when you go to "/home", then refresh, your browser will try to reach the server and ask it for "/home" (in contrast to the first time, the browser requests "/" which is mapped to "dist/index.html"). "/home" is not found or defined in Echo routing. Hence you get a Not Found message.

What I recommend that you do is to do the following regarding routing

e.Static("/dist", "dist")
e.File("/*", "dist/index.html")
e.Start(":3000")

And inside your index.html add "/dist" before the URLs of the requested resources.

Seaskyways
  • 3,630
  • 3
  • 26
  • 43
  • Oh also, you don't have to update/ edit your index.html if you just set it up like this way: `e := echo.New() e.Static("/", "../dist") e.File("/*", "../dist/index.html") e.Start(:1234)` – Carlo Nyte Feb 08 '22 at 15:46
3

There's also an HTML5 option in static middleware in echo: e.g.

e.Use(middleware.StaticWithConfig(middleware.StaticConfig{
        Root:   "dist",
        Index: "index.html",
        Browse: true,
        HTML5:  true,
    }))

which seems to handle SPA webpages.

N3R4ZZuRR0
  • 2,400
  • 4
  • 18
  • 32
Abhey Shah
  • 31
  • 1
0

@Seaskyways answer works great for me, but I also managed to find another solution after looking more into Echo. Echo has a NotFoundHandler which handles all the unknown routes, so what I do is just make it returns the 'index.html' everytime an undefined routes is requested. Here's my code:

echo.NotFoundHandler = func(c echo.Context) 
    return c.File("dist/index.html")
}
e := echo.New()
e.Static("/", "dist")
e.Start(":3000")

I'm not sure if this is a good way to do it but it's another option for anyone having the same problem like me

  • 1
    This forces Echo to look check all the options before going to `NotFoundHandler`. This may hinder performance. – Seaskyways Dec 26 '18 at 06:17
0

Leaving this here as an alternative, in case any one wants a complete example. Using "/*" will capture any routes starting with "/" or whatever prefix you want that hasn't been defined.

package main

import (
    "github.com/labstack/echo/v4"
    "github.com/labstack/echo/v4/middleware"
)

const (
    HTTP_PORT = ":1234"
)

func main() {
    e := echo.New()

    // Logger Middleware
    e.Use(middleware.Logger())

    // Setup routes
    SetupRoutes(e)

    e.Logger.Fatal(e.Start(HTTP_PORT))
}

func SetupRoutes(e *echo.Echo) {
    // Catpure route /
    e.GET("/", getWebApp)
    
    // Catpure route /test
    e.GET("/test", getTest)
    
    // Catpure all routes starting with / that haven't been defined 
    e.GET("/*", getWebApp)
    
    // Use e.Any() for all request methods GET, PUT, POST, PATCH and DELETE
    e.Any("/*", getWebApp)
}

func getWebApp(ctx echo.Context) error {
    return ctx.File("../website_src/index.html")
}

func getTest(ctx echo.Context) error {
    return ctx.String(http.StatusOK, "Hello")
}
Carlo Nyte
  • 665
  • 6
  • 17