Go + Angular ui-router - angularjs

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")
}

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)
}
}

How to set up a route to serve reactjs app?

I'm trying to set up a route to serve my reactjs application.
I have my index.html and bundle.js in the public folder
/public/index.html
/public/bundle.js
I'm using go as my backend API, and also to serve my reactjs app.
I created a subroute for my app like:
r := mux.NewRouter()
app := r.Host("app.example.com").Subrouter()
So any request with app as the subdomain will be for my Reactjs app.
So now I have to serve every request regardless of the URL to my reactjs app.
Is path prefix what I need here?
I tried this:
app.PathPrefix("/").Handler(serveReact)
func serveReact(w http.ResponseWriter, r *http.Request) {
}
But I get this error:
cannot use serveReact (type func() http.Handler) as type http.Handler
in argument to app.PathPrefix("/").Handler: func() http.Handler does
not implement http.Handler (missing ServeHTTP method)
Your http handler needs a ServeHTTP method. If you pass your function to http.HandlerFunc, that will be introduced for you:
app.PathPrefix("/").Handler(http.HandlerFunc(serveReact))
func serveReact(w http.ResponseWriter, r *http.Request) {
}
The HandlerFunc type is an adapter to allow the use of ordinary functions as HTTP handlers. If f is a function with the appropriate signature, HandlerFunc(f) is a Handler that calls f.
source
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
Similarly, you could use the mux router HandlerFunc instead:
app.PathPrefix("/").HandlerFunc(serveReact)
func serveReact(w http.ResponseWriter, r *http.Request) {
}
This essentially performs both steps for you in a combined single step.

More fluent cooperation of FileServe and Routing

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)
}

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.

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