When trying to pass a value into .html
code I'm using the html/template
package.
However, I can't seem to set the .css
content type that is referenced in my .html
file. It is being served to the browser as plain text and thus formatting is ignored.
In a static .html
file I can use the built-in http.Fileserver
which takes care of content types but then templating doesn't work. I can't pass in the variable. It just displays as {{.}}
Is there a way to combine the content-type
convenience of the built-in fileserver http.Fileserver
with the template ability of http.HandleFunc
?
Here is my code as it stands without using http.Fileserver
. Note, my Go
file is in the starting directory and the index.html
and .css
files are in a subdirectory /hello/
:
package main
import (
"html/template"
"io/ioutil"
"log"
"net/http"
)
var Message string = "test page"
func servePipe(w http.ResponseWriter, req *http.Request) {
dat, err := ioutil.ReadFile("hello/index.html")
if err != nil {
log.Fatal("couldn't read file:", err)
}
templ, err := template.New("Test").Parse(string(dat))
if err != nil {
log.Fatal("couldn't parse page:", err)
}
templ.Execute(w, Message)
}
func main() {
http.HandleFunc("/hello/", servePipe)
http.ListenAndServe(":8080", nil)
}
Here is my .html
file. The html page is being served without any issues. It's the separate .css
file that is not being served (linked in the .html
file) thus the formatting doesn't occur.
<!DOCTYPE html>
<html>
<head>
<title>Test Page</title>
<link rel="stylesheet" type="text/css" href="style.css"/>
</head>
<body>
<p>{{.}}</p>
</body>
</html>
Template.Execute()
will "deliver" the content by calling the Write()
method of the passed io.Writer
which in your case is the http.ResponseWriter
.
If you don't set the content type prior to the first call to ResponseWriter.Write()
(you don't in your example), then the net/http
package will attempt to detect the content type (based on the first 512 bytes written), and will set it automatically for you (along with WriteHeader(http.StatusOK)
if ResponseWriter.WriteHeader()
has not been called).
This is documented at http.ResponseWriter.Write()
:
// Write writes the data to the connection as part of an HTTP reply.
//
// If WriteHeader has not yet been called, Write calls
// WriteHeader(http.StatusOK) before writing the data. If the Header
// does not contain a Content-Type line, Write adds a Content-Type set
// to the result of passing the initial 512 bytes of written data to
// DetectContentType.
//
// ...
Write([]byte) (int, error)
So if your index.html
would look like this:
<html>
<body>
This is the body!
</body>
</html>
Then this would be properly detected as an HTML document and thus text/html
content type would be automatically set. If this doesn't happen for you, that means your index.html
is not recognized as an HTML document. So just make sure you make your template files valid HTML / CSS / etc. documents and content type will be inferred automatically and properly.
Also note that if it wouldn't work in some "extreme" cases, you can always "override" it by manually setting it prior to calling Template.Execute()
like this:
w.Header().Set("Content-Type", "text/html")
templ.Execute(w, Message)
Side note: don't read and parse templates inside your handlers, for details see related question: It takes too much time when using "template" package to generate a dynamic web page to client in golang
If a template served by you references other templates, they will not be magically loaded and executed. In such cases you should have a "pre-loaded" templates containing all the templates. template.ParseFiles()
and template.ParseGlob()
are good "tools" to load multiple template files at once.
So if your index.html
references style.css
, then you have to take care about serving style.css
too. If it is not a template (but e.g. a static file), you may serve it using multiple options presented here: Include js file in Go template
If it is also a template, then you also have to load it and serve it by calling Template.ExecuteTemplate()
.
An example solution would be to load both using template.ParseFiles()
, and use the path to "designate" which template you want to have served (from the client / browser side).
E.g. requesting the path /hello/index.html
could serve the "index.html"
template, requesting the path /hello/style.css
(this will be done automatically by the browser after receiving and processing the index.html
) could serve the "style.css"
template. It could look like this (error checks omitted for brevity):
parts := strings.Split(req.URL.Path, "/") // Parts of the path
templName := parts[len(parts)-1] // The last part
templ.ExecuteTemplate(w, templName, someDataForTemplate)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With