Search code examples
gogo-templatesfileservergo-html-templategopath

golang error when serving static files


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


Solution

  • 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.