-1

I can't figure out this error in go lang when executing a template

panic: open templates/*.html: The system cannot find the path specified.

another problem is that my public folder can't be served from css and I don't know why.

code:

package main

import (
    "net/http"
    "github.com/gorilla/mux"
    "html/template"
    "log"
)

var tpl *template.Template

func init() {
    tpl = template.Must(template.ParseGlob("templates/*.html"))
}

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/",home)
    http.Handle("/public/", http.StripPrefix("/public/", 
    http.FileServer(http.Dir("/pub"))))
    http.ListenAndServe(":8080", r)
}

func home(writer http.ResponseWriter, request *http.Request) {
    err := tpl.ExecuteTemplate(writer, "index.html", nil)
    if err != nil {
        log.Println(err)
        http.Error(writer, "Internal server error", http.StatusInternalServerError)
    }
}

my folder is like that:

bin
pkg
pub
   css
     home.css
   js
src
   github.com
        gorila/mux
templates
        index.html

my GOROOT is pointing to the folder project which contains the bin pkg src

when I make the templates folder outside src it works fine but I thinks that's not right everything must be in src right ?

index.html

    <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test</title>
    <link href="../public/css/home.css" type="text/css" rel="stylesheet" />
</head>
<body>
<h1>welcome! hi</h1>
</body>
</html>

here i can't serve the css

UPDATE::

the first problem is solved of the templates folder .

but I am still cannot solve the second issue

Ahmed Nader
  • 151
  • 5
  • 19
  • templates folder only needs to be near the binary you make when you build to a binary. the src folder is only required for sources. since the templates are loaded at runtime *not* compile time it doesn't need to be in the src folder. – gabeio Aug 26 '17 at 22:09
  • so you mean that when i go to production i will be using the bin folder not the src folder ? so i am sorry to ask but what the src is used for in production then ? i mean if someone used my code how will he use my codes ? from package ? – Ahmed Nader Aug 26 '17 at 22:16
  • so you mean that i get out the folder from `src`and put it under the `project` folder and then change the `parseGlob` from `templates/*.html` to `src/templates/*.html` ? – Ahmed Nader Aug 26 '17 at 22:22
  • `templates/*.html` is relative to where you launch the binary file it has nothing to do with the rest of the golang setup. If you are going to build and leave the binary in bin then you want to copy the templates folder there. If you are going to put it in pkg then you need to have templates there. – gabeio Aug 26 '17 at 22:27
  • do i make it for public folder too ? i put public and template folders all outside src and i put them under the project folder beside the bin ? – Ahmed Nader Aug 26 '17 at 22:38

1 Answers1

3

I personally find the standard http library very good and simple for such tasks. However, if you insist on Gorilla's mux here is the problem I found in your code:

func main() {
    ...
    http.Handle("/public/", http.StripPrefix("/public/", http.FileServer(http.Dir("/pub"))))
    http.ListenAndServe(":8080", r)
}

On one hand you are using http.Handle to serve static files but on the other hand you're giving r to http.ListenAndServe.

To fix this just change that line into:

r.PathPrefix("/public/").Handler(
       http.StripPrefix("/public/",
           http.FileServer(http.Dir("/pub/"))))

If I may, I would like to suggest using flag to set a different path to the templates directory and to the public directory so it would be easy to deploy and run your server.

In other words, instead of hardcoding the path to these directories you can run:

./myserver -templates=/loca/templates -public=/home/root/public

To do that just add the following:

import "flag"
....
func main() {
    var tpl *template.Template
    var templatesPath, publicPath string
    flag.StringVar(&templatesPath, "templates", "./templates", "Path to templates")
    flag.StringVar(&publicPath, "public", "./public", "Path to public")

    flag.Parse()
    tpl = template.Must(template.ParseGlob(templatesPath+"/*.html"))
    r.HandleFunc("/", handlerHomeTemplates(tpl))
    r.PathPrefix("/public/").Handler(http.StripPrefix("/public/", http.FileServer(http.Dir(publicPath))))
    ...
}

func handlerHomeTemplates(tpl *template.Template) http.HandlerFunc {
    return http.HandlerFunc(func((writer http.ResponseWriter, request *http.Request) {
        err := tpl.ExecuteTemplate(writer, "index.html", nil)
        if err != nil {
            ...
        }
    })
}

Even thought the init function is a good place to do part of it, I think it's a good practice to avoid it unless it's needed. That's why I did all the initialization in the main function and used handlerHomeTemplates to return a new http.HandleFunc that uses templatesPath.

boaz_shuster
  • 2,825
  • 20
  • 26