What is the difference between running a function in a .go file and invoking it in Go template? - go

With template.FuncMap from the text/template package, you can directly access functions from within your Go template files.
Suppose the following scenario: In your handler for a user overview page, you call a function GetAllUsers and pass your user objects to the template with ExecuteTemplate:
func index(w http.ResponseWriter, r *http.Request) {
users, err := model.GetAllUsers()
if err != nil {
render50x()
return
}
data := make(map[string]interface{})
data["userList"] = users
render(w, r, data, "layout", "index")
}
Is this the same as passing the function to the template and executing it there?
var funcs = template.FuncMap{
"getAllUsers": model.GetAllUsers,
}
// func render
t := template.New("render").Funcs(funcs)
if err := template.Must(t.ParseFS(ViewsFS, files...)).ExecuteTemplate(w, layout, data); err != nil {
log.Println("Error executing template:", err.Error())
}
{{ range getAllUsers }}
{{ .DisplayName }}
{{ end }}
Is there a difference between the two approaches?

If you can call the function from the template, it's the same. Some differences:
If you call it in Go, you do not need to register the function. Sometimes you don't have access to template parsing to register functions, so this is the only way (don't forget: you have to register functions before parsing the template).
Also if you call it in Go, you have more "control" over it: you can recover from panics, you can pre-process the results, and you can re-use it in other Go code. You can also choose not to execute the template based on the result, or do something else which may not be (easily) expressible in templates.
The result of the function may also not be something that can be rendered easily. E.g. it may not be a string, or may not have a String() string method. So some additional (Go) logic may be needed to convert the result to a human-readable format, which may not be available in templates, or may require further functions to be registered.
Also note that not all functions can be registered and called from templates. Callable functions may have 2 return types at most, the seconds which may only be error. From Go you can call "any" functions and pass only the results you need. If the function has parameters, you also have to pass those as data to the template execution (so you can pass them in the template when calling the function).

Related

How to pass objects around middleware(s) and handler in go echo?

I am using echo to build a web server in go. What is the best way to pass objects around middlewares and a handler apart from using context.Set method?
For example, let's consider the following case:
func mw(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Conext) error {
var value TypeX = // load value of type TypeX based on query parameters
c.Set("key", value)
return next(c)
}
}
func h1(c echo.context) error {
value := c.Get("key").(TypeX)
return c.JSON(http.StatusOk, value.H1())
}
func h2(c echo.context) error {
value := c.Get("key").(TypeX)
return c.JSON(http.StatusOk, value.H2())
}
func registerRoute(e *echo.Echo) {
e.Get("/test", h)
}
Is there any way to get rid of get/set methods? It seems like an unclean way of doing this operation. I am open to complete refactoring, creating new structures/interfaces to make this happen.
To take this example further, lets say my apis can fall into bucket1, bucket2, bucket3. Value would call the method b1, b2, b3 based on which bucked it falls in. These buckets can be identified by adding them as middleware mwB1, mwB2, mwB3 each of which calls the respective method. Hence, all my apis would first call the middleware mw; then one of mwB1, mwB2, mwB3 and finally the actual handler, something like, e.Get("/test", h, mw, mwB2). I don't want to load value everywhere and get/set seems unclean to me (unless that is the standard way of doing this).

Code design for handle funcs in go web app

I'm learning go and ran into some design issues while developing web app. The app has main route "/" where user can submit a simple form. With those form values I am calling external API and unmarshaling response into some struct. Now from here I want to make another call based on retrieved values to another external API and I'm not sure what's the proper way of doing this. Here is a snippet for better understandment:
func main() {
http.HandleFunc("/", mainHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func mainHandler(w http.ResponseWriter, r *http.Request) {
//renders form template
//makes post and retrieves data from api
//here with retrieved data I want to make another call to different API,
// but mainHandler would get too big and complex. I'm not sure how should I pass this data to
// another handler or redirect to another handler with this data.
}
The handlers' semantics should be designed to match the desired HTTP behavior, regardless of the code complexity. If you want to handle a single client request by doing a bunch of stuff, that should be a single handler. If the handler becomes too complex, break it up. Handlers are just functions and can be broken up exactly like any other function - by extracting some part of it into another function and calling that new function. To take you pseudocode:
func mainHandler(w http.ResponseWriter, r *http.Request) {
err := renderTemplate(w)
if err != nil { ... }
err, data := postToApi()
if err != nil { ... }
err, data2 := postToApi2(data)
if err != nil { ... }
}
There's no reason for those functions to be handlers themselves or to get the client involved with a redirect. Just break up your logic the way you normally break up logic - it doesn't matter that it's an HTTP handler.
Hi golearner, in the mainHandler just render the form and make another handler kinda "/formaction" to handle the form, in that way you can easily organize your code.

sensulib package interface as function param

I am trying to make use of this golang package: https://github.com/jefflaplante/sensulib
I want to get all the events from the sensu API. I've followed the example code and modified it slightly so it works:
config := sensu.DefaultConfig()
config.Address = "sensu-url:port"
onfig.Username = "admin"
config.Password = "password"
// Create a new API Client
sensuAPI, err := sensu.NewAPIClient(config)
if err != nil {
// do some stuff
}
Now I want to grab all the events from the API, and there's a neat function do to that, GetEvents
However, the function expects a parameter, out, which is an interface. Here's the function itself:
func (c *API) GetEvents(out interface{}) (*http.Response, error) {
resp, err := c.get(EventsURI, out)
return resp, err
}
What exactly is it expecting me to pass here? I guess the function wants to write the results to something, but I have no idea what I'm supposed to call the function with
I've read a bunch of stuff about interfaces, but it's not getting any clearer. Any help would be appreciated!
The empty interface interface{} is just a placeholder for anything. It's roughly the equivalent of object in Java or C# for instance. It means the library doesn't care about the type of the parameter you are going to pass. For hints about what the library does with that parameter, I suggest you look at the source code.

Read template in init or in handler function?

I'm writing a basic server for a website. Now I face a (for me) difficult performance question. Is it better to read the template file in the init() function?
// Initialize all pages of website
func init(){
indexPageData, err := ioutil.ReadFile("./tpl/index.tpl")
check(err)
}
Or in the http.HandlerFunc?
func index(w http.ResponseWriter, req *http.Request){
indexPageData, err := ioutil.ReadFile("./tpl/index.tpl")
check(err)
indexPageTpl := template.Must(template.New("index").Parse(string(indexPageData)))
indexPageTpl.Execute(w, "test")
}
I think in the first example, after the server is started you have no need to access the disk and increase the performance of the request.
But during development I want to refresh the browser and see the new content. That can be done with the second example.
Does someone have a state-of-the-art solution? Or what is the right from the performance point of view?
Let's analyze the performance:
We name your first solution (with slight changes, see below) a and your second solution b.
One request:
a: One disk access
b: One disk access
Ten requests:
a: One disk access
b: Ten disk accesses
10 000 000 requests:
a: One disk access
b: 10 000 000 disk accesses (this is slow)
So, performance is better with your first solution. But what about your concern regarding up-to-date data? From the documentation of func (t *Template) Execute(wr io.Writer, data interface{}) error:
Execute applies a parsed template to the specified data object, writing the output to wr. If an error occurs executing the template or writing its output, execution stops, but partial results may already have been written to the output writer. A template may be executed safely in parallel.
So, what happens is this:
You read a template from disk
You parse the file into a template
You choose the data to fill in the blanks with
You Execute the template with that data, the result is written out into an io.Writer
Your data is as up-to-date as you choose it. This has nothing to do with re-reading the template from disk, or even re-parsing it. This is the whole idea behind templates: One disk access, one parse, multiple dynamic end results.
The documentation quoted above tells us another thing:
A template may be executed safely in parallel.
This is very useful, because your http.HandlerFuncs are ran in parallel, if you have multiple requests in parallel.
So, what to do now?
Read the template file once,
Parse the template once,
Execute the template for every request.
I'm not sure if you should read and parse in the init() function, because at least the Must can panic (and don't use some relative, hard coded path in there!) - I would try to do that in a more controlled environment, e.g. provide a function (like New()) to create a new instance of your server and do that stuff in there.
EDIT: I re-read your question and I might have misunderstood you:
If the template itself is still in development then yes, you would have to read it on every request to have an up-to-date result. This is more convenient than to restart the server every time you change the template. For production, the template should be fixed and only the data should change.
Sorry if I got you wrong there.
Never read and parse template files in the request handler in production, that is as bad as it can get (you should like always avoid this). During development it is ok of course.
Read this question for more details:
It takes too much time when using "template" package to generate a dynamic web page to client in golang
You could approach this in multiple ways. Here I list 4 with example implementation.
1. With a "dev mode" setting
You could have a constant or variable telling if you're running in development mode which means templates are not to be cached.
Here's an example to that:
const dev = true
var indexTmpl *template.Template
func init() {
if !dev { // Prod mode, read and cache template
indexTmpl = template.Must(template.New("index").ParseFiles(".tpl/index.tpl"))
}
}
func getIndexTmpl() *template.Template {
if dev { // Dev mode, always read fresh template
return template.Must(template.New("index").ParseFiles(".tpl/index.tpl"))
} else { // Prod mode, return cached template
return indexTmpl
}
}
func indexHandler(w http.ResponseWriter, r *http.Request) {
getIndexTmpl().Execute(w, "test")
}
2. Specify in the request (as a param) if you want a fresh template
When you develop, you may specify an extra URL parameter indicating to read a fresh template and not use the cached one, e.g. http://localhost:8080/index?dev=true
Example implementation:
var indexTmpl *template.Template
func init() {
indexTmpl = getIndexTmpl()
}
func getIndexTmpl() *template.Template {
return template.Must(template.New("index").ParseFiles(".tpl/index.tpl"))
}
func indexHandler(w http.ResponseWriter, r *http.Request) {
t := indexTmpl
if r.FormValue("dev") != nil {
t = getIndexTmpl()
}
t.Execute(w, "test")
}
3. Decide based on host
You can also check the host name of the request URL, and if it is "localhost", you can omit the cache and use a fresh template. This requires the smallest extra code and effort. Note that you may want to accept other hosts as well e.g. "127.0.0.1" (up to you what you want to include).
Example implementation:
var indexTmpl *template.Template
func init() {
indexTmpl = getIndexTmpl()
}
func getIndexTmpl() *template.Template {
return template.Must(template.New("index").ParseFiles(".tpl/index.tpl"))
}
func indexHandler(w http.ResponseWriter, r *http.Request) {
t := indexTmpl
if r.URL.Host == "localhost" || strings.HasPrefix(r.URL.Host, "localhost:") {
t = getIndexTmpl()
}
t.Execute(w, "test")
}
4. Check template file last modified
You could also store the last modified time of the template file when it is loaded. Whenever the template is requested, you can check the last modified time of the source template file. If it has changed, you can reload it before executing it.
Example implementation:
type mytempl struct {
t *template.Template
lastmod time.Time
mutex sync.Mutex
}
var indexTmpl mytempl
func init() {
// You may want to call this in init so first request won't be slow
checkIndexTempl()
}
func checkIndexTempl() {
nm := ".tpl/index.tpl"
fi, err := os.Stat(nm)
if err != nil {
panic(err)
}
if indexTmpl.lastmod != fi.ModTime() {
// Changed, reload. Don't forget the locking!
indexTmpl.mutex.Lock()
defer indexTmpl.mutex.Unlock()
indexTmpl.t = template.Must(template.New("index").ParseFiles(nm))
indexTmpl.lastmod = fi.ModTime()
}
}
func indexHandler(w http.ResponseWriter, r *http.Request) {
checkIndexTempl()
indexTmpl.t.Execute(w, "test")
}

calling function from map[string]interface{} by giving key

I want to be able to pass function name to gin.Engine route handler. I have the following code;
// status service
type StatusService struct {
App *gin.Engine
}
func (s *StatusService) Ping(ctx *gin.Context) {
ctx.JSON(200, gin.H{
"message": "pong",
})
}
app := gin.Default()
// define services
statusService := &services.StatusService{
App: app,
}
ss := make(map[string]interface{})
ss["auth"] = statusService
app.GET("/ping", ss["auth"].Ping)
The compiler gives the following error;
./app.go:60: ss["auth"].Ping undefined (type interface {} has no field or method Ping)
Any ideas about how to fix that?
interface{} works for just about any type, the problem is that you've failed to assert which type the thing is. In this case you would need something like... ss["auth"].(*StatusService).Ping(myCtxInstance). This answer has a more thorough example which I'll refrain from duplicating; Go map of functions
Couple other things; if your real use case is as simple as your example just stop what you're doing and add func(ctx *gin.Context) as a second argument. Also, depending on the nature of the functions you want to use (like if they all have the same args and return types) then you might want to use a second arg for the delegates, map[string]func(argumentType) would be more appropriate.
The design you currently have pushes all errors to runtime which obviously is less desirable than the compile time safety you'd get from either of the options I touched on above.

Resources