Skip to content

Instantly share code, notes, and snippets.

@taylortrimble
Last active August 29, 2015 14:06
Show Gist options
  • Save taylortrimble/df269ec71e5a034b65b7 to your computer and use it in GitHub Desktop.
Save taylortrimble/df269ec71e5a034b65b7 to your computer and use it in GitHub Desktop.
A Go web server that redirects URLs which don't correspond to a file to index.html or some other suitable page.

ng-html5mode-serve

Hey. When you guys find the thing that already does this, preferably in Grunt, will you tell me? kthxbye.

What this does

So, you're serving an Angular app at http://ngexampleapp.com/, aka index.html. If we want simple static serving to work with routes (like /dashboard), we have to accept non-HTML5 mode URLs http://ngexampleapp.com/#/dashboard. And that's garbage.

We need a server that will respond to URLs like http://ngexampleapp.com/dashboard with index.html. Then our app could take over and take us to the right view. But we also need to serve some paths (like /styles/ and /scripts/) with their real files: not index.html.

This server takes a configuration that tells it what port to serve on and what routes to serve real files for. It will serve index.html for everything else.

Example Config

{
    "port": 4000,
    "file_paths": ["bower_components", "styles", "scripts", "views"]
 }

Example routes from config

Path File served
/ index.html
/styles/main.css styles/main.css
/scripts/controllers/app.js scripts/controllers/app.js
/dashboard index.html
/experimental-dev-path index.html

See how nice that is?

It's written in Go, which is because I wanted something in 20 minutes and didn't know node yet. Hidden benefit: automatically sends correct Content-Type with the files via file extention or automatic content type detection.

package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
)
type Config struct {
Port int `json:"port"`
FilePaths []string `json:"file_paths"`
}
var config = new(Config)
func main() {
// Read config
configFile, err := os.Open("ng-html5mode-serve.json")
if err != nil {
log.Fatalln(err)
}
configDecoder := json.NewDecoder(configFile)
err = configDecoder.Decode(config)
if err != nil {
log.Fatalln(err)
}
// Handle file serve directories. Otherwise, serve index.html so Angular routes work.
for _, filePath := range config.FilePaths {
http.Handle(fmt.Sprintf("/%s/", filePath), http.FileServer(http.Dir("")))
}
http.HandleFunc("/", alwaysServeIndex)
log.Println("Will serve on port", config.Port)
err = http.ListenAndServe(fmt.Sprintf(":%d", config.Port), nil)
log.Fatalln(err)
}
func alwaysServeIndex(w http.ResponseWriter, r *http.Request) {
file, err := os.Open("index.html")
defer file.Close()
if err != nil {
log.Fatalln(err)
}
_, err = io.Copy(w, file)
if err != nil {
log.Fatalln(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment