Gorilla mux routes in separate files in subfolder? - go

I'm trying to build a very simple Go web application, and the golang "a folder per package" structure is making things difficult for me.
I'm using github.com/gorilla/mux as the router and github.com/unrolled/render for template rendering. This means that I need to create a new router and a new renderer when the app launches, and I need all my routes to access the renderer.
This is super easy to do in a single file:
func main() {
...
r := render.New(render.Options{
// a lot of app specific setup
})
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
r.HTML(w, http.StatusOK, "myTemplate", nil)
})
...
}
However, this is where I don't understand Go. Because I want the routes in separate files in a subfolder (my project will grow), that forces them to be in a routes package. Of course that makes the renderer variable inaccesssible. I can't just create the renderer in the routes package, because the render.New() call relies on a me passing in a ton of app specific stuff like the template folder, and helpers for asset paths.
I went down the route of making my handler functions work on a struct with an already initialized renderer...
func (app *App) Hello2(w http.ResponseWriter, r *http.Request) {
app.Renderer.HTML(w, http.StatusOK, "myTemplate", nil)
}
But I'm still confused as to how I'm going to access this app *App in the routes package when it's initialized in main. Everything in Go seems super easy if you have a flat list of files, but as soon as you want a bit of folder structure, the package setup becomes problematic.
There's probably something I'm missing here, so any help is appreciated.

Here's general info on dealing with dependencies in Go. A key trick is, you just have to declare the Render variable in a package that your views can import. You could create a myapp/render package with a var Render that's either inited in the package itself (func init()) or set from main.
But the context thing you found sounds totally sane, though it might be more than this app needs. The neat thing about it is that because the context is set in per-request code, later you could extend it to do sneaky things like use the Host: header to provide a different Layout for people loading the app via different domains. If the Layout is baked into a global, you can't. This can be a real advantage--I've tried to retrofit per-request changes onto big codebases whose config was sprayed around various global variables, and it's a pain.

Related

Should I call template.ParseFiles(...) on each http request or only once from the main function?

I am doing some web develoment using the go programming language using the package html/template. At some point of the code, I need to call the function template.ParseFiles(...) so I can create a template from those files ad then execute it using temp.Execute(w,data). I would like to know if it is better to create the template on each request or to do it once in the main and declare a global variable.
Right now I do it on each request on my handle functions, like most tutorials do. However, I don't know If I'm wasting resources by doing it on each request instead of having them as global variables.
This is how it looks on each request
func ViewStats(w http.ResponseWriter, r *http.Request) {
//Get stuff from db and put them in data
//...
//return data to user
tmp, err := template.ParseFiles("views/guest-layout.html",
"views/stats.html")
if err != nil {
fmt.Println(err)
} else {
tmp.Execute(w,data)
}
}
I would like to know if this is better:
var temp1 template.Template
func main() {
temp1, err = template.ParseFiles("file1","file2")
//...
}
As usual: It depends.
But first some nuance:
You should never do template parsing (or anything else interesting) in your main() function. Instead, your main() function should call methods (or a single method) that kicks off the chain of interesting things in your program.
Go doesn't have globals, so it's not actually an option to store your parsed templates in a global variable in the first place. The closest Go has to global variables is package variables. You could store your parsed templates in a package variable in the main package, but this is also bad form, as your main package (except for tiny, trivial programs), should just be an entry point, and otherwise nearly empty.
But now, on to the core of your question:
Should you parse templates per request, or per run?
And here it depends.
If you have templates that change frequently (i.e. during development, when you're constantly editing your HTML files), once per request can be best.
But this is far less efficient than just parsing once, so in production, you may wish to parse the templates once on startup only. Then you can store the templates in a package variable, or better, in a struct that is initialized at runtime. But I leave that to you.
But what may be best is actually a bit of a compromise between the two approaches. It may be best to load your templates at start-up, and re-load them occasionally, either automatically (say, every 5 minutes), or watch your filesystem, and reload them whenever the on-disk representation of the templates changes.
How to do this is left as an exercise for the reader.

Global variables and Go

I am currently trying to work on a small Go project, and I have a problem I am trying to solve.
I'm currently using github.com/jinzhu/gorm to handle database operations for the backend of a GraphQL server, and I wanted to be able to store the DB connection in a global variable accessible throughout the entire project (including sub-packages).
My first attempt was at creating a variable named db by doing the following in my main.go file:
var db *gorm.DB
var err error
then inside the init() function:
func init() {
db, err = gorm.Open("postgres", "credential stuff here")
db.AutoMigrate(&modelStructHere)
defer db.Close()
}
There isn't any crashing, but I would assume due to scoping, the db variable is only usable inside main.go, but I also need to be able to use this inside gql/gql.go, where my GraphQL resolver is currently located.
Perhaps I should move this chunk of code (DB init) to the actual resolver file, since there's really no use for DB operations outside of such a thing anyway, so maybe that's the problem?
Thanks in advance!
Alex's comment is spot on. Create a folder named "database" and inside put a file called "database.go" containing this:
package database
// the imports go here
var DB *gorm.DB
Now you can use it wherever you like with database.DB. You should not make the error variable global, handle it in the same function that initializes the DB. The init function can be in any place, usually you want it at the beginning of your program in the main function.

Local subdomains for a standalone application

I have a website, which is composed by three smaller 'independent' subsites:
mysite
index.html
favicons
images
doc
index.html
css
img
js
...
editor
index.html
images
js
src
...
Where doc is a site created with Hugo :: A fast and modern static website engine, editor is the mxgraph Graphditor example; and the remaining files make a hand-made landing page.
Besides deploying to any web server, I'd like to distribute the site as an 'standalone application'. To allow so, I wrote this really simple server in go:
package main
import (
flag "github.com/ogier/pflag"
"fmt"
"net/http"
"net/http/httputil"
"os"
"path/filepath"
)
var port = flag.IntP("port", "p", 80, "port to serve at")
var dir = flag.StringP("dir", "d", "./", "dir to serve from")
var verb = flag.BoolP("verbose", "v", false, "")
func init() {
flag.Parse();
}
type justFilesFilesystem struct {
fs http.FileSystem;
}
type neuteredReaddirFile struct {
http.File
}
func (fs justFilesFilesystem) Open(name string) (http.File, error) {
f, err := fs.fs.Open(name)
if err != nil { return nil, err; }
return neuteredReaddirFile{f}, nil
}
func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) {
return nil, nil;
}
func loggingHandler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requestDump, err := httputil.DumpRequest(r, true)
if err != nil { fmt.Println(err); }
fmt.Println(string(requestDump))
h.ServeHTTP(w, r)
})
}
func main() {
str, err := filepath.Abs(*dir)
if err != nil { os.Exit(1); }
fmt.Printf("Serving at port %d from dir %s\n\n",*port,str)
http.ListenAndServe(fmt.Sprintf(":%d",*port), loggingHandler(http.FileServer(justFilesFilesystem{http.Dir(*dir)})))
}
As a result, I can run simpleserver -d <path-to-mysite> and browse the sites through localhost, localhost/doc and localhost/editor.
Then, I'd like to use custom (sub)domain(s) such as mylocal.app, doc.mylocal.app and editor.mylocal.app. So, I added the following line to my the /etc/hosts file: 127.0.0.1 mylocal.app. Therefore, I can browse mylocal.app, mylocal.app/editor and mylocal.app/doc. Moreover, I was able to change it to mylocal.app, mylocal.app:<editor-port> and mylocal.app:<doc-port> with different packages.
However, when I try to use a subdomain, it is not properly resolved, so any reverse-proxy strategy won't work. Since wildcards are not supported, I can add additional entries in the /etc/hosts file, but I'd prefer to avoid it.
Although, an alternative solution is to run dnsmasq, I'd like to keep the application standalone. I found some equivalent golang packages. However, I feel that many features are supported which I don't really need.
Furthermore, since I don't really have to resolve any IP, but to provide an alias of localhost, I think that a proxy could suffice. This would also be easier to configure, since the user could configure the browser only, and no system-wide modification would be required.
Yet, all the traffic from the user would be 'filtered' by my app. Is this correct? If so, can you point me to any reference to implement it in the most clean way. I know this is quite subjective, but I mean a relatively short (say 10 lines of code) snippet so that users can easily check what is going on.
EDIT
I'd like to use something like:
func main() {
mymux := http.NewServeMux()
mymux.HandleFunc("*.mylocal.app", myHandler)
mymux.HandleFunc("*", <systemDefaultHandler>)
http.ListenAndServe(":8080", mymux)
}
or
func main() {
mymux := http.NewServeMux()
mymux.HandleFunc("editor.mylocal.app", editorHandler)
mymux.HandleFunc("doc.mylocal.app", docHandler)
mymux.HandleFunc("*.mylocal.app", rootHandler)
mymux.HandleFunc("*", <systemDefaultHandler>)
http.ListenAndServe(":8080", mymux)
}
These are only snippets. A complete example is this, which was referenced in the comments by #Steve101.
However, at now, I don't know what systemDefaultHandler is. And that is not solved there.
Apart from that, #faraz suggested using goproxy. I think that the HTTP/HTTPS transparent proxy is the default handler I am looking for. But, using a package only to do that seems excessive to me. Can I achieve the same functionality with built-in resources?
Unfortunately, there's no dead simple way to do this through Go. You'll need to intercept your system's DNS requests just like dnsmasq, and that's inevitably going to require some modification the system DNS config (/etc/resolv.conf in Linux, /etc/resolver on a Mac, or firewall rule) to route your DNS requests to your app. Going the DNS has the downside that you'd need to build a DNS server inside your app similar to pow.cx, which seems unnecessarily complicated.
Since mucking with system config is inevitable, I'd vote for making changes to the hosts file on boot or when a directory is added/removed (via fsnotify.) On shutdown, you can clear the added entries too.
If you're looking to isolate these changes to a specific browser instead of make a system-wide change, you could always run your application through a proxy server like goproxy and tell your browser to use that proxy for requests. For example, you can do this in Chrome through its preferences or by setting the --proxy-server flag:
--proxy-server=<scheme>=<uri>[:<port>][;...] | <uri>[:<port>] | "direct://"
See Chrome's network settings docs for more details.
Also, if you're willing to much with browser configs, you could just use an extension to handle the requests as needed.
there is only solution that would work without proxy is that you register an domain for this and make an offical dns entry with wildcard to 127.0.0.1 like *.myApp.suche.org. So any client that resolve the ip get 127.0.0.1 and work local. this way you do not need administrator rights to modify the /etc/hosts or equivalent.
If it should work behind proxy without modify the resolver (etc/hosts etc) you can provide an wpad.dat (Javascript for Proxy detection) that say for your domain all traffic goes you server and the rest to the real proxy. If this is served in you server the script can automaticaly contain the real proxy as default.

Type conflicts between projects

I want to move code used by a specific subdomain its own project, which will be imported by the main code base which is currently resides in. I am able to import code from the subdomain into the main project successfully, until I add the Gorilla Mux code. For example, this works:
// imports and non-relevant routes removed for simplicity
r := mux.NewRouter()
// Primary site routes here...
s := r.Host("subdomain-regex-here").Subrouter()
s.HandleFunc("/", people.Index)
http.ListenAndServe("localhost:8080", r)
But when I move the subdomain to its own project and import it, then call the LoadRoutes() function which passes in the mux.Router object from the primary site, I receive an error. Here's the code:
// Primary Project
r := mux.NewRouter()
// Primary site routes here...
// function located in the subdomain go project, which is imported
func LoadRoutes(host string, r *m.Router) {
s := r.Host(host).Subrouter()
s.HandleFunc("/", people.Index)
s.HandleFunc("/people", people.Index)
s.HandleFunc("/person/new", people.New)
}
# command-line-arguments
./main.go:25: cannot use r (type *"primary_site/vendor/github.com/gorilla/mux".Router) as type *"subdomain_site/vendor/github.com/gorilla/mux".Router in argument to routers.LoadRoutes
It looks like I have two instances of the Gorilla Mux, from two separate projects, that are conflicting. I only import packages from the subdomain site to the primary site, not the other way around. This exact code works perfectly as long as I have it in a single project, but when I try to separate the projects, it breaks.
Since I pass in the instance of mux.NewRouter(), why am I having a conflict?
You have 2 vendor directories in your project. You need to flatten them into a single vendor directory at the top level in order to share vendored types between packages. Remove the subdomain_site/vendor and only use the vendor directory in your main pacakage.

Golang import based on config variable

I'm currently in the process of still learning go but I recently got to a point where in one of my tests I wanted to create a quick backup application that will reference a config file and switch what "plugin" to use for the backup. So what I got to at this point is to create (as example, written from my head and syntax may be incorrect):
type BackupStorage interface{
Put (d []byte) (n int, err Error)
Get (l []byte) (d []byte, err Error)
}
At this point I would assume I should use reflection to switch on the type and return the specific backup function, although that does not seem right.
The second alternative I came to was to still use my BackupStorage interface and create "plugin" packages to dictate which import will be used, but how do I switch that then based on a config file variable. And I'd like to do this because some stuff on one machine may backup only to s3 where as others may backup to both s3 and google drive etc.
With the basic example above what I have in mind is this:
Implement 2 BackupStorage "plugins" (Google Drive / S3) with the flexibility to add more at any time, but have my code be generic enough to execute on whatever storage backend is selected in config files.
As I mentioned above I'm still learning and any explanations would be appreciated or examples on how to accomplish this. I don't mind the "your doing it wrong" as long as there is a proper explanation on why it's wrong and how to do it right.
You have the right idea to start, implement everything you need via an interface, and then you can plug in any concrete backup "plugin" that implements that interface.
Once you can run your backup via an interface, you can simply assign an instance of the backend you want based on whatever conditions you set.
var storage Backupper
type Backupper interface {
Backup()
}
type GDrive struct {
config string
}
func (g *GDrive) Backup() {
fmt.Println("Doing backup to Google Drive")
}
func main() {
storage = &GDrive{}
storage.Backup()
}
Or with multiple options: http://play.golang.org/p/RlmXjf55Yh

Resources