More fluent cooperation of FileServe and Routing - angularjs

I have an issue where I want to serve my main AngularJS (Yeoman deployment) app folder on FileServe / but it will destroy all my router bindings. Is there any way I can reserve them and keep my routes in-tact?
In the code below I still have to go to /app and re-bind the other folders, because I don't want to temper with the Grunt file too much (yet), so added some extra backup path-bindings for the folders.
func initializeRoutes() {
// Handle all requests by serving a file of the same name
fileHandler := http.FileServer(http.Dir(*clFlagStaticDirectory))
bowerFileHandler := http.FileServer(http.Dir("../bower_components"))
imagesFileHandler := http.FileServer(http.Dir("../app/images"))
scriptsFileHandler := http.FileServer(http.Dir("../app/scripts"))
stylesFileHandler := http.FileServer(http.Dir("../app/styles"))
viewsFileHandler := http.FileServer(http.Dir("../app/views"))
// Setup routes
mainRoute := mux.NewRouter()
mainRoute.StrictSlash(true)
// mainRoute.Handle("/", http.RedirectHandler("/static/", 302))
mainRoute.PathPrefix("/app").Handler(http.StripPrefix("/app", fileHandler))
mainRoute.PathPrefix("/app/bower_components").Handler(http.StripPrefix("/bower_components", bowerFileHandler))
mainRoute.PathPrefix("/bower_components").Handler(http.StripPrefix("/bower_components", bowerFileHandler))
mainRoute.PathPrefix("/images").Handler(http.StripPrefix("/images", imagesFileHandler))
mainRoute.PathPrefix("/scripts").Handler(http.StripPrefix("/scripts", scriptsFileHandler))
mainRoute.PathPrefix("/styles").Handler(http.StripPrefix("/styles", stylesFileHandler))
mainRoute.PathPrefix("/views").Handler(http.StripPrefix("/views", viewsFileHandler))
// Basic routes
// User routes
userRoute := mainRoute.PathPrefix("/users").Subrouter()
userRoute.Handle("/login", handler(userDoLogin)).Methods("POST")
userRoute.Handle("/logout", handler(userDoLogout)).Methods("GET")
userRoute.Handle("/forgot_password", handler(forgotPassword)).Methods("POST")
// Bind API Routes
apiRoute := mainRoute.PathPrefix("/api").Subrouter()
apiProductModelRoute := apiRoute.PathPrefix("/productmodels").Subrouter()
apiProductModelRoute.Handle("/", handler(listProductModels)).Methods("GET")
apiProductModelRoute.Handle("/{id}", handler(getProductModel)).Methods("GET")
// Bind generic route
http.Handle("/", mainRoute)
}
So my goal is the serve /app as my / main path, but reserve all my Mux Routes to win over the FileServe. So if I'll have a /app/users/login folder, it won't load, but instead it will let the Router win.
Note: I'm serving purely over HTTPS and nothing over HTTP.
Thanks so much in advance! This is breaking my brain and it's the last thing I need to figure out before I can fully start on my front-end code :).

It looks for me like you want to change the order of how the routes are evaluated in a way that /users, /login, and similar are matched before the /. And the / should be server by the FileServer.
As far as I know, the routes will be matched in the order they are defined (added to the router). So you just have to move your API and other dynamic routes before /.
The following code works similar:
the /test match is evaluated before the file server and returns the string "OK"
then the file server returns the file under the path
Code:
package main
import (
"github.com/gorilla/mux"
"net/http"
)
func main() {
appFileHandler := http.FileServer(http.Dir("/Users/alex/Projects/tmp/so/app"))
r := mux.NewRouter()
r.PathPrefix("/test").HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("OK"))
})
r.PathPrefix("/").Handler(appFileHandler)
http.Handle("/", r)
http.ListenAndServe(":8080", nil)
}

Related

Go HTTP Server for React, Angular, etc

I created this small HTTP Server in GO for static files:
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)
}
}
It works perfectly with React and Angular dist files (after transpile them). However if I already selected a route e.g. http://example.org:8080/customers and click on refresh in the browser I got a 404 page not found. That is the only situation where my code is failing.
It happens because on React and Angular index.html acts as a front controller and can handle the routes. However to make it working I need to internally redirect all not found requests to index.html.
Since is the angular/react handling the route I wouldn't like to create a http.HandleFunc for each route created in the angular/react. I would like to something similar to express :
app.use(express.static(path.join(__dirname, 'static')));
app.use("*",function(req,res){
res.sendFile(path.join(__dirname, 'static/index.html'));
});
or NGINX:
try_files $uri $uri/ index.html;
Any clues on how to do it in go?
It's important to understand how Go's http package handles route matching as it's a bit different from other languages/frameworks. HandleFunc uses ServeMux under the hood and (from the 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]
Given this behavior, I would recommend creating explicit handlers for each folder in static (e.g. css/, js/), or putting all in a single subfolder of static, and then respond with your index.html file for all other requests (using the root route (/)). This works because requests with routes prefixed by /css/ or /js/ will more closely match the appropriate static handler routes, while all others will not and therefore only most closely match the root route. You just need to be sure not to create conflicting routes on your front-end.
This way any request explicitly for CSS/JS/image assets will be handled by serving the static directory and all other requests will be responded to with your React app.
Here's an example (leaving out your wrapHandler for simplicity):
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)
}
}
or if you want to be a bit more explicit:
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)
}
}
My website works pretty much the same way (using Vue instead of React).
An Improvement
As mentioned above, you might consider putting all you static assets in a sub folder of your current static/ folder. Consider structuring your files like this:
public/
index.html
static/
css/
js/
img/
Which is the default file structure for built React apps (but public is called build by default).
This will let you use the above approach in a more streamlined way since you'll only need one fileserver handler for all static assets. Then you can use the following code:
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)
}
}

Cross platform go code for appengine

What is the GO appropriate way to create a FetchUrl/GetURL function that works from the command line and works from google app engine with its custom way to fetch a url.
I have basic code that fetches and processes some data on a URL. I want to be able to call it from code I use on my desktop, and code deployed to app engine.
Hopefully thats clear, if not please let me know and Ill clarify.
If you have some code which works both on local machine and on AppEngine environment, you have nothing to do.
If you need to do something which should or must be done differently on AppEngine, then you need to detect the environment and write different code for the different environments.
This detection and code selection is easiest done using build constraints. You can put a special comment line in the beginning of your .go file, and it may or may not be compiled and run depending on the environment.
Quoting from The Go Blog: The App Engine SDK and workspaces (GOPATH):
The App Engine SDK introduces a new build constraint term: "appengine". Files that specify
// +build appengine
will be built by the App Engine SDK and ignored by the go tool. Conversely, files that specify
// +build !appengine
are ignored by the App Engine SDK, while the go tool will happily build them.
So for example you can have 2 separate .go files, one for AppEngine and one for local (non-AppEngine) environment. Define the same function in both (with same parameter list), so no matter in which environment the code is built, the function will have one declaration. We will use this signature:
func GetURL(url string, r *http.Request) ([]byte, error)
Note that the 2nd parameter (*http.Request) is only required for the AppEngine (in order to be able to create a Context), so in the implementation for local env it is not used (can even be nil).
An elegant solution can take advantage of the http.Client type which is available in both the standard environment and in AppEngine, and which can be used to do an HTTP GET request. An http.Client value can be acquired differently on AppEngine, but once we have an http.Client value, we can proceed the same way. So we will have a common code that receives an http.Client and can do the rest.
Example implementation can look like this:
url_local.go:
// +build !appengine
package mypackage
import (
"net/http"
)
func GetURL(url string, r *http.Request) ([]byte, error) {
// Local GetURL implementation
return GetClient(url, &http.Client{})
}
url_gae.go:
// +build appengine
package mypackage
import (
"google.golang.org/appengine"
"google.golang.org/appengine/urlfetch"
"net/http"
)
func GetURL(url string, r *http.Request) ([]byte, error) {
// Appengine GetURL implementation
ctx := appengine.NewContext(r)
c := urlfetch.Client(ctx)
return GetClient(url, c)
}
url_common.go:
// No build constraint: this is common code
package mypackage
import (
"net/http"
)
func GetClient(url string, c *http.Client) ([]byte, error) {
// Implementation for both local and AppEngine
resp, err := c.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return body, nil
}
You could get some clues in the golang/appengine project itself.
For instance, its remote_api/client.go provides the client for connecting remotely to a user's production application.

Go + Angular: loading base html

I'm trying to write an app in Go with Angular. I'm not sure if I got the concept right, but basically I should serve a simple html that loads angular and the app (js) itself and then the rest is handled by ajax requests. What I don't know is how to serve the html file on every non-ajax request on every path? I would like to use Gorilla mux but I couldn't find out how to do that.
Is this even the right direction?
On every request that is not any known url You should send index.html - or whatever is Your base angular app file.
Gorilla/mux has a NotFoundHandler, which is handler for everyting that is not matched by any other routes. You can assignt Your own handler for it like that:
solution with gorilla/mux is:
func main() {
r := mux.NewRouter()
r.HandleFunc("/foo", fooHandler)
r.NotFoundHandler = http.HandlerFunc(notFound)
http.Handle("/", r)
}
while notFound is:
func notFound(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "static/index.html")
}
assuming the Your base file is in static/index.html :).
Now all Your requests that are not any other requests (so, in that setup - not an ajax call defined in routes) will serve index file with url that can be handled by ngRoute or ui-router.
//serve static files from a folder 'public' which should be in the same dir as the executable.
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "no-cache")
http.ServeFile(w, r, "public"+r.URL.Path)
})
This will try to serve every non-matching URL from the public directory. Hope this helps.

Go + Angular ui-router

I'm a new Gopher trying to do a Go backend to serve my Angularjs frontend and also serve an API.
This is what I have so far.
package main
import (
"github.com/gorilla/mux"
"log"
"net/http"
)
func main() {
rtr := mux.NewRouter()
srtr := rtr.PathPrefix("/api").Subrouter()
srtr.HandleFunc("/hello", hello).Methods("GET")
rtr.PathPrefix("/").Handler(http.FileServer(http.Dir("./static/")))
http.Handle("/", rtr)
log.Println("Listening...")
http.ListenAndServe(":3000", nil)
}
func hello(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World"))
}
Everything works fine. /api/hello return "Hello World" and if I go to / it will serve my index.html. However since I'm trying to use angular ui-router so I need my go server to send all non-registered routes to angular so angular ui-router can handle them.
For example: If I go /random right now it will return a 404 since I don't have any file under ./static named random. But what I want is Go to forward that request to Angular so ui-router can handle the /random
In Your router You should serve index.html to all undefined elsewhere URLs. In mux package there is helpful handler:
http://www.gorillatoolkit.org/pkg/mux#Router
- look at NotFoundHandler
You can use it, to handle all 404's and serve index.html instead:
func main() {
r := mux.NewRouter()
r.HandleFunc("/foo", fooHandler)
r.NotFoundHandler = http.HandlerFunc(notFound)
http.Handle("/", r)
}
and define notFound function:
func notFound(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "static/index.html")
}

GAE Golang Gorilla mux - 404 page not found

I've got some problems to use gorilla mux within GAE.
When I try it, I've '404 page not found'. The rootHandler function is not called ( no traces generated)
Below is part of my code, any ideas?
thk in advance
...
func init() {
r := mux.NewRouter()
r.HandleFunc("/",rootHandler)
}
func rootHandler(w http.ResponseWriter, r *http.Request) {
var functionName = "rootHandler"
c := appengine.NewContext(r)
c.Infof(functionName+"-start")
defer c.Infof(functionName+"-end")
...
You have to route requests to your mux router. http package has DefaultServeMux which is used by AppEngine, but mux does not. (and it's not registering its routes with net/http by itself)
That is, all you have to do, is register your mux router with net/http:
func main() {
r := mux.NewRouter()
r.HandleFunc("/", HomeHandler)
r.HandleFunc("/products", ProductsHandler)
r.HandleFunc("/articles", ArticlesHandler)
http.Handle("/", r)
}
(straight from the docs)
Important part here is http.Handle("/", r).
You can also pass the router as the second argument to ListenAndServe since it implements the http.Handler interface.
router := mux.NewRouter()
router.HandleFunc("/", HomeHandler)
http.ListenAndServe(":8080", router) // pass the router here

Resources