<!DOCTYPE> html/template - html

I've got this simple go lang webserver that does nothing more but parsing some data into an external HTML file and serve that file to the webserver.
package main
import (
"html/template"
"net/http"
)
type Event struct {
Name string
}
func handler(w http.ResponseWriter, r *http.Request) {
e := Event{ Name: "Melt! Festival" }
t, _ := template.ParseFiles("events.html")
t.Execute(w, e)
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":1337", nil)
}
But whenever I try to parse the HTML file with the set it parses my html-page as text in stead of rendering the HTML in the browser
<!DOCTYPE>
<html>
<head>
<title>Event</title>
</head>
<body>
<p>
Event: {{.Name}}
</p>
</body>
</html>
When I leave the <!DOCTYPE> out of the HTML-file it renders it just fine.
Can anyone tell me why this is because I'm really curious? I spent two hours searching for the cause of my go code not working.

Your doctype declaration is incorrect, thus having an effect opposite from the desired one : it is probably interpreted by the browser as signifying the document isn't HTML.
Use this :
<!DOCTYPE html>
See reference.

Have you tried using DOCTYPE html in your file instead?

Related

Why is my script reading in files linked in my HTML which aren't being specified when reading in with ioutil.ReadFile() in GoLang?

I have a GoLang script that is meant to dynamically construct webpages based on an input query in my browser that looks like this http://localhost:8000/blog/post#, the post# portion is what is used to identify which JSON data file to parse into the HTML template I created; for example, if I put http://localhost:8000/blog/post1 then it creates an index.html file from my post1.json file. Currently, my script when run allows me to load a single page in my browser before it exits with the error in my terminal stdout log:
2022/03/18 00:32:02 error: open jsofun.css.json: no such file or directory
exit status 1
this is my current script:
package main
import (
"encoding/json"
"html/template"
"io/ioutil"
"log"
"net/http"
"os"
)
func blogHandler(w http.ResponseWriter, r *http.Request) {
blogstr := r.URL.Path[len("/blog/"):]
blogstr = blogstr + ".json"
// read the file in locally
json_file, err := ioutil.ReadFile(blogstr)
if err != nil {
log.Fatal("error: ", err)
}
// define a data structure
type BlogPost struct {
// In order to see these elements later, these fields must be exported
// this means capitalized naming and the json field identified at the end
Title string `json:"title"`
Timestamp string `json:"timestamp"`
Main string `json:"main"`
ContentInfo string `json:"content_info"`
}
// json data
var obj BlogPost
err = json.Unmarshal(json_file, &obj)
if err != nil {
log.Fatal("error: ", err)
}
tmpl, err := template.ParseFiles("./blogtemplate.html")
HTMLfile, err := os.Create("index.html")
if err != nil {
log.Fatal(err)
}
defer HTMLfile.Close()
tmpl.Execute(HTMLfile, obj)
http.ServeFile(w, r, "./index.html")
}
func main() {
http.HandleFunc("/blog/", blogHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
I have done some basic debugging and identified that the issue is coming from the lines:
json_file, err := ioutil.ReadFile(blogstr)
if err != nil {
log.Fatal("error: ", err)
}
What confuses me is why the ioutil.ReadFile is also trying to read files linked within my HTML? Shouldn't the browser be handling that linkage and not my Handler? For reference this is my HTML where the file jsofun.css is linked; the error referenced in my console output shows me my script is trying to access this file as jsofun.css.json during the call to ioutil.ReadFile:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dynamic JSON Events</title>
<link rel="stylesheet" href="./jsofun.css"></style>
</head>
<body>
<section id="title">
<h1 id="text-title">{{.Title}}</h1>
<time id="timestamp">
{{.Timestamp}}
</time>
</section>
<nav role="navigation" id="site-nav">
<ul id="sitemap">
</ul>
</nav>
<main role="main" id="main">
{{.Main}}
</main>
<footer role="contentinfo" id="footer">
<section id="content-info" role="contentinfo">
{{.ContentInfo}}
</section>
<form id="contact-form" role="form">
<address>
Contact me by <a id="my-email" href="mailto:antonhibl11#gmail.com" class="my-email">e-mail</a>
</address>
</form>
</footer>
<script src="./jsofun.js">
</script>
</body>
</html>
I know that I have appended .json to my query string in the line: blogstr = blogstr + ".json", however, I do not see why this would also affect the links in my HTML. Is there something I am missing or why would it be reading links in my HTML template? All I want the ioutil.ReadFile to do is to read my JSON file which is parsed into my template.
EDIT: I wanted to add that when I load the page initially into the browser it loads successfully with my text content from my JSON but fails to have any styling. When I view the source the links also are correct which confuses me why the stylesheet wouldn't apply because it is in the right directory to be accessible. This is the source when I select view-source in browser:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dynamic JSON Events</title>
<link rel="stylesheet" href="./jsofun.css"></style>
</head>
<body>
<section id="title">
<h1 id="text-title">Second Post Test</h1>
<time id="timestamp">
Friday, March 18th, 12:21AM
</time>
</section>
<nav role="navigation" id="site-nav">
<ul id="sitemap">
</ul>
</nav>
<main role="main" id="main">
This is my second post where I am able to use dynamic URL routing
</main>
<footer role="contentinfo" id="footer">
<section id="content-info" role="contentinfo">
This post was used primarily to test and layout how the rest of my posts will appear.
</section>
<form id="contact-form" role="form">
<address>
Contact me by <a id="my-email" href="mailto:antonhibl11#gmail.com" class="my-email">e-mail</a>
</address>
</form>
</footer>
<script src="./jsofun.js">
</script>
</body>
</html>
For reference this is an example of how my JSON file looks:
{
"title" : "Second Post Test",
"timestamp": "Friday, March 18th, 12:21AM",
"main": "This is my second post where I am able to use dynamic URL routing",
"content_info": "This post was used primarily to test and layout how the rest of my posts will appear."
}
Your Go server is set up to only serve the /blog/ path and it does that by executing the blogHandler. There is nothing else in your Go server that is set up to serve assets like css, js, or image files.
For such things you generally need to register a separate FileServer handler at a separate path. Example:
func main() {
http.HandleFunc("/blog/", blogHandler)
// To serve a directory on disk (/path/to/assets/on/my/computer)
// under an alternate URL path (/assets/), use StripPrefix to
// modify the request URL's path before the FileServer sees it:
http.Handle("/assets/", http.StripPrefix("/assets/",
http.FileServer(http.Dir("/path/to/assets/on/my/computer"))))
log.Fatal(http.ListenAndServe(":8080", nil))
}
The other thing you need to fix are the links to those asset fiels in the HTML, they should be absolute, not relative.
...
<link rel="stylesheet" href="/assets/jsofun.css"></style>
...
<script src="/assets/jsofun.js">
The above will of course work only if the assets are located in the /path/to/assets/on/my/computer directory, e.g.
/path/to/assets/on/my/computer
├── jsofun.css
└── jsofun.js
You blogHandler is unnecessarily creating a new file for every request without removing it, this has the potential to very quickly fill up your disk to its max capacity. To serve a template you do not need to create a new file, you can instead execute the template directly into the http.ResposeWriter. It is also advisable to parse the template only once, especially in production code, to avoid unnecessary waste of resources:
type BlogPost struct {
Title string `json:"title"`
Timestamp string `json:"timestamp"`
Main string `json:"main"`
ContentInfo string `json:"content_info"`
}
var blogTemplate = template.Must(template.ParseFiles("./blogtemplate.html"))
func blogHandler(w http.ResponseWriter, r *http.Request) {
blogstr := r.URL.Path[len("/blog/"):] + ".json"
f, err := os.Open(blogstr)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
defer f.Close()
var post BlogPost
if err := json.NewDecoder(f).Decode(&post); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := blogTemplate.Execute(w, post); err != nil {
log.Println(err)
}
}
Lets work through what happens when you request http://localhost:8000/blog/post#.
The browser requests the page; your code successfully builds and returns some html - this will include:
<link rel="stylesheet" href="./jsofun.css"></style>
The browser receives and processes the HTML; as part of that process it requests the css above. Now the original request was in the folder /blog/post# so ./jsofun.css becomes http://localhost:8000/blog/jsofun.css.
When your go application receives this request blogHandler will be called (due to the request path); it strips off the /blog/ then adds .json to get the filename jsofun.css.json. You then try to open this file and get the error because it does not exist.
There are a few ways you can fix this; changing the template to use <link rel="stylesheet" href="/jsofun.css"></style> might be a start (but I don't know where jsofun.css is stored and you don't show any code that would serve that file). I think it's also worth noting that you do not have to create the file index.html on disk (unless there is some other reason for doing this).
(See mkopriva's answer for other issues and further steps - was half way through entering this when that answer was posted and felt the walk through might still be of use).

Serve html,css,js files under subpath in go mux

I want to display a compiled angular project under a subpath (eg http://localhost:3000/app/) with go mux
It works as expected without the subpath "app/"
But when I add the subpath with http.StripPrexix("/app/, ..) Handler only index html is found and rendered but without all css, js files..
Test code
package main
import (
"log"
"net/http"
"github.com/gorilla/mux"
)
func main() {
mux := mux.NewRouter()
fs := http.FileServer(http.Dir("./static/"))
// Serve static files
mux.PathPrefix("/app/").Handler(http.StripPrefix("/app/", fs))
log.Println("Listening...")
http.ListenAndServe(":3000", mux)
}
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>TestProject</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="styles.3ff695c00d717f2d2a11.css"></head>
<body>
<h1>Should work</h1>
<app-root></app-root>
<script src="runtime.359d5ee4682f20e936e9.js" defer></script><script src="polyfills.bf99d438b005d57b2b31.js" defer></script><script src="main.5dd083c1a27b2a7e410a.js" defer></script></body>
</html>
Working Project Download
This project also includes the static files to serve
https://filehorst.de/d/dubJmmsz
Goal
Serve different projects under different paths
Following on this question
What am I doing wrong?
How to serve the whole project under e.g.: localhost:3000/app/
The gorilla mux docs mention how to handle Angular SPA applications.
However since you want to base your SPA root to a sub directory, you also need to do the changes in HTML/JS. The base directory should be a variable that will be same as that of prefix of SPA. This is more of a design problem I believe
Otherwise since your rest of the files other than index are resolved at root you need to fallback to "/" for rest of the files & have both configured.
router.PathPrefix("/app").Handler(spa)
router.PathPrefix("/").Handler(spa)
So try like below. I tried your static folder with following:
package main
import (
"log"
"net/http"
"os"
"path/filepath"
"time"
"github.com/gorilla/mux"
)
type spaHandler struct {
staticPath string
indexPath string
}
func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// get the absolute path to prevent directory traversal
path, err := filepath.Abs(r.URL.Path)
if err != nil {
// if we failed to get the absolute path respond with a 400 bad request
// and stop
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// prepend the path with the path to the static directory
path = filepath.Join(h.staticPath, path)
// check whether a file exists at the given path
_, err = os.Stat(path)
if os.IsNotExist(err) {
// file does not exist, serve index.html
http.ServeFile(w, r, filepath.Join(h.staticPath, h.indexPath))
return
} else if err != nil {
// if we got an error (that wasn't that the file doesn't exist) stating the
// file, return a 500 internal server error and stop
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// otherwise, use http.FileServer to serve the static dir
http.FileServer(http.Dir(h.staticPath)).ServeHTTP(w, r)
}
func main() {
router := mux.NewRouter()
spa := spaHandler{staticPath: "static", indexPath: "index.html"}
router.PathPrefix("/app").Handler(spa)
srv := &http.Server{
Handler: router,
Addr: ":3000",
// Good practice: enforce timeouts for servers you create!
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
}
log.Fatal(srv.ListenAndServe())
}

Serving static html using Gorilla mux

I'm using gorilla serve mux to serve static html files.
r := mux.NewRouter()
r.PathPrefix("/").Handler(http.FileServer(http.Dir("./public"))).Methods("GET")
I do have a Index.html file inside the public folder as well as other html files.
When browsing the site I get all the content of the folder instead of the default Index.html.
I came from C# and I know that IIS takes Index.html as default but it is possible to select any page as a default.
I wanted to know if there's a proper way to select a default page to serve in Gorilla mux without creating a custom handler/wrapper.
Maybe using a custom http.HandlerFunc would be easier:
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// Here you can check if path is empty, you can render index.html
http.ServeFile(w, r, r.URL.Path)
})
You do have to make a custom handler because you want a custom behavior. Here, I just wrapped the http.FileServer handler.
Try this one:
package main
import (
"log"
"net/http"
"github.com/gorilla/mux"
)
func main() {
handler := mux.NewRouter()
fs := http.FileServer(http.Dir("./public"))
handler.PathPrefix("/").Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
//your default page
r.URL.Path = "/my_default_page.html"
}
fs.ServeHTTP(w, r)
})).Methods("GET")
log.Fatal(http.ListenAndServe(":8080", handler))
}
So, from the code, if the visited path is the root (/) then you rewrite the r.URL.Path to your default page, in this case my_default_page.html.
After grabthefish mentioned it i decided to check the actual code of gorilla serve mux.
This code is taken from net/http package which Gorilla mux is based on.
func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string,
redirect bool) {
const indexPage = "/index.html"
// redirect .../index.html to .../
// can't use Redirect() because that would make the path absolute,
// which would be a problem running under StripPrefix
if strings.HasSuffix(r.URL.Path, indexPage) {
localRedirect(w, r, "./")
return
}
the code request the index file to be index.html in lower case so renaming my index file solved it.
thank you grabthefish!

What method is called when I display a variable in a golang html template

I am trying to figure out what method is called when I show a variable in an "html/template" template in Go via {{ .SomeVariable }}.
I am using the package "html/template".
I am using this function to render a template:
func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
t, _ := template.ParseFiles("public/" + tmpl + ".html")
t.Execute(w, p)
}
with
type Page struct {
Title string
Users []*model.User
}
and an example template
<html>
<head>
<title>Listing Users</title>
</head>
<body>
<h1>Listing users</h1>
{{range .Params}}
<h3>
id {{.Id }}
email {{.Email}}
name {{.Name}}
</h3>
{{end}}
</body>
</html>
We store Id in a certain way and have a .String() method defined for displaying it.
When we use the package "text/template" the Id attribute displays correctly but when we use "html/template" it does not. My guess is that the former is calling .String() when displaying a variable and latter is not. I haven't been able to glean from the documentation what method is being called.
This is the first SO question I've ever written for Go. Hopefully made it clear but feel free to ask for additional info as I am a complete Go noob.
Thanks.

Hide HTML content if a user is logged in

I'm writing a web server in Go and was asking myself, what the conventional way of conditionally hiding a part of an HTML page is.
If I wanted a "sign in" button only to show up, when the user is NOT logged in, how would I achieve something like this?
Is it achieved with template engines or something else?
Thank you for taking the time to read and answer this :)
you just have to give a struct to your template and manage the rendering inside it.
Here is a working exemple to test:
package main
import (
"html/template"
"net/http"
)
func main() {
http.HandleFunc("/", helloHandler)
http.ListenAndServe(":8000", nil)
}
type User struct {
Name string
}
func helloHandler(w http.ResponseWriter, r *http.Request) {
t := template.New("logged exemple")
t, _ = t.Parse(`
<html>
<head>
<title>Login test</title>
</head>
<body>
{{if .Logged}}
It's me {{ .User.Name }}
{{else}}
-- menu --
{{end}}
</body>
</html>
`)
// Put the login logic in a middleware
p := struct {
Logged bool
User *User
}{
Logged: true,
User: &User{Name: "Mario"},
}
t.Execute(w, p)
}
To manage the connexion you can use http://www.gorillatoolkit.org/pkg/sessions with https://github.com/codegangsta/negroni and create the connection logic inside a middleware.
Simply start session on user login .Set the necessary values in session and then hide the html u want with if tag in javascript using session. Its very simple and best way to solve this type of problems.
You might try something like this
<?php
if($_SESSION['user']){}else{
echo '<button onclick="yourfunction();">sign in</button>';
}
?>
Thus, if they aren't logged in, this'll show up.