One of my favorite features of Go is the ability to easily add handlers inside of packages. This greatly simplifies the processes of writing modular code.
For Example:
File Structure
|-- app.yaml
|-- app
| +-- http.go
|-- templates
| +-- base.html
+-- github.com
+-- storeski
+-- appengine
|-- products
| |-- http.go
| +-- templates
| |-- list.html
| +-- detail.html
+-- account
|-- http.go
+-- templates
|-- overview.html
+-- notifications.html
Each packages has a http.go file that takes ownership of a url prefix. For example the products
package under github.com/storeski/appengine/products
would own any inbound url starting with /products
.
With this modular approach it is beneficial to store the templates within the products
package. If you would like to maintain a consistant base template for the site you can establish a convention where you extend templates/base.html
.
Example
templates/base.html
<!DOCTYPE HTML>
<html>
<head>
<title>{{.Store.Title}}</title>
</head>
<body>
<div id="content">
{{template "content" .}}
</div>
</body>
</html>
github.com/storeski/appengine/products/templates/list.html
{{define "content"}}
<h1> Products List </h1>
{{end}}
github.com/storeski/appengine/products/http.go
func init() {
http.HandleFunc("/products", listHandler)
}
var listTmpl = template.Must(template.ParseFiles("templates/base.html",
"github.com/storeski/appengine/products/templates/list.html"))
func listHandler(w http.ResponseWriter, r *http.Request) {
tc := make(map[string]interface{})
tc["Store"] = Store
tc["Products"] = Products
if err := listTmpl.Execute(w, tc); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
This approach is very exciting because it makes the sharing of apps/package trivial. If I write a package that handles authentication which takes ownership of the /auth
url. Any developer that, then, adds the package to their product root instantly has all of the functionality. All they have to do is create a base template (templates/base.html
) and direct their users to /auth
.