8 minutes
Go and the World Wide Web
Introduction
In this episode I’m going to show how to create a basic, bare-bones website using golang as the backend and very basic css and html on the front-end. I will try to cover as much detail as possible but may miss a few things, check the Go Docs or w3c for more information.
Prerequistes
- GoLang
- Code Editor
- Web Browser
Repository
Reading the Go Code
Lets Start with the Config
GoLang has a particular structure, so once the code has a main, the compiled executable will be the same as the folder, yes, you can override that. So in the codebase, there’s a “main.go” and a “config.go”. The “config.go” just handles the default port just incase you want to change it, and can be expanded to hold any options you may need.
func readConfigFile() ConfigFile {
p := "./config.json"
_, err := os.Stat(p)
var cf ConfigFile
if os.IsNotExist(err) {
log.Println("Could not find config file, creating it")
cf = createConfigFile()
} else if err == nil {
b, _ := ioutil.ReadFile(p)
json.Unmarshal(b, &cf)
}
return cf
}
The function just checks if the “config.json” exist, if not it creates it with default values. Variables can be auto defined in Go with ":=". Functions can return more than one element, if you are not interested in a particular variable you can put a "_" to indicate that.
The function returns a ConfigFile structure which is defined as;
type ConfigFile struct {
Port string `json:"port"`
}
There’s a few things unsual with this structure especially if you’re a C guy/gal. So the struct is defined by saying it’s a type then it’s variable name and finally it’s defined type. There’s something else unusual, as the variable starts with a capital letter it means it’s an exported type. This becomes especially important when you separate your code into a number of libraries.
We define that the json file has one element Port and it’s defined to be a string. Again this is a reverse notation for C but you do get used to it. The json:"port"
is just a helper for the json library which creates a mapping between the GoLang structure and the actual json.
Into the Main
The main function is the actual webserver, it listens on the port you define, default 8080 and is local only. You may want to expand the config to handle the ip also.
func main() {
cook()
cf := myinit()
r := mux.NewRouter()
hs := http.FileServer(http.Dir("./resources/"))
r.PathPrefix("/resources/").Handler(http.StripPrefix("/resources/", hs))
mainH := mainpageHandler{staticPath: "public", indexPath: "index.html"}
r.PathPrefix("/").Handler(mainH)
fmt.Println("Listening on port: " + cf.Port)
srv := &http.Server{
Handler: r,
Addr: "127.0.0.1:" + cf.Port,
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
}
log.Fatal(srv.ListenAndServe())
}
Templates
The first thing you’ll see is the function cook(). This takes all the templates that are in the templates folder and turns them into standalone html documents. This server isn’t dynamic it only serves raw html, which makes it ready for a cdn. You can modify the code to provide dynamic content but it would be better to separate to a seperate server so it can be extended.
To cook we use golang’s template processing. It’s a seperate processing language that allows you to expand documents. It looks like this:
<html>
<head>
<script src="/resources/javascript/jquery-3.5.1.min.js"></script>
<script src="/resources/javascript/js.cookie-2.2.1.min.js"></script>
<script src="/resources/javascript/jquery.webfonts.2018.js"></script>
</head>
<body>
{{template "header.xhtml"}}
{{template "menu.xhtml"}}
<div class="Content">
<h1>One, Two</h1>
<p>One, Two, One, Two, this is just a test!</p>
<p>"Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit..."</p>
<p>"There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain..."</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla consequat consectetur commodo. Mauris tincidunt turpis velit, ac tincidunt nisi blandit sed. Fusce eget urna ut eros feugiat mattis vestibulum quis erat. Curabitur eu ante urna. Suspendisse eget lectus tristique, placerat massa ut, lacinia sem. Aenean laoreet sem at odio placerat, et egestas mauris eleifend. Nam at ligula vehicula, interdum metus ut, tempor ligula. Suspendisse quis massa tellus. Mauris sit amet nibh dolor. Sed sagittis egestas ligula nec mattis. Etiam pharetra urna ut risus dignissim, vel interdum neque hendrerit.</p>
<p>Donec ut mauris dui. Morbi iaculis, urna eu finibus rutrum, ipsum felis lacinia ligula, id vestibulum orci odio molestie lorem. Sed ac ultrices turpis. Vestibulum suscipit ullamcorper sodales. Nunc sagittis egestas purus, et auctor justo sagittis et. Morbi feugiat tellus quis elit ullamcorper dignissim. Nunc ac dolor non felis tristique venenatis. Morbi molestie fringilla quam vel suscipit.</p>
<p>Maecenas in semper magna, sit amet congue ipsum. Mauris bibendum quis turpis vel malesuada. Vestibulum rutrum lorem suscipit sodales venenatis. Etiam eget imperdiet ipsum. Cras ullamcorper leo quam, in pretium dui tincidunt ac. Donec pulvinar nisl nunc, vel faucibus sem egestas tincidunt. Vivamus at consequat augue. Etiam a augue vitae odio mollis imperdiet. Phasellus fringilla finibus est non consectetur. Aliquam eget justo ac elit mattis dictum sed ac tortor. Nunc molestie ut tellus pulvinar pharetra. Maecenas ligula neque, commodo vel dui quis, volutpat venenatis nisl. Vivamus finibus vel erat et imperdiet.</p>
<p>Quisque elementum massa a lacinia aliquam. Ut interdum nisi purus, quis facilisis metus maximus at. Sed aliquet sollicitudin lectus ac congue. Fusce iaculis neque a elit placerat, sed pharetra elit imperdiet. Nulla gravida leo diam, in maximus ipsum blandit vel. Cras dapibus pulvinar ante. Vivamus sed massa nec turpis dignissim vulputate vel vitae odio. Pellentesque congue ac ligula ut convallis.</p>
<p>Proin nec tristique urna. Nulla a velit in leo convallis posuere. Morbi rutrum, mi rutrum porta hendrerit, nunc dolor facilisis lorem, nec ultricies enim mauris in eros. Mauris rhoncus nulla eu tincidunt vulputate. Suspendisse id aliquet diam. Nullam vestibulum molestie turpis, feugiat auctor tellus efficitur id. Curabitur vel arcu eget massa congue finibus vitae vitae ligula. Proin erat nisi, egestas sit amet ligula vel, imperdiet pretium nisi. Mauris pharetra malesuada arcu, sed imperdiet ipsum maximus et. Integer sed sollicitudin diam, sed malesuada nisi. Praesent varius erat non egestas consequat. In sagittis pretium magna. Maecenas vulputate ipsum non arcu egestas aliquet.</p>
</div>
{{template "footer.xhtml"}}
</body>
</html>
The code is just using three templates, but these are basic includes. The template processing can get very involved and may need a topic on it’s own.
Gorilla Mux
The server uses Gorilla Mux, this is a very powerful extension to the http capabilities provided in Go. It’s not entirely necessary in this particular example but if you are starting a http server, it’s best to start with it.
I use mux to serve the cooked html:
hs := http.FileServer(http.Dir("./resources/"))
r.PathPrefix("/resources/").Handler(http.StripPrefix("/resources/", hs))
You could of course just use nginx or apache but with golang you have much more flexibilty on what’s dynamic.
HTML / CSS
Using the template system every page has a header, menu and footer.
The header/content is easy, just a basic div. The Menu and footer slightly more complicated so that’s what I’ll focus on.
CSS Menu
The menu is completely handled through css, the code was inspired by an article I found here but it’s been heavily modified to match the general feel of the template site.
#topmenubar {
padding: 4px;
width: 1024px;
height: 24px;
margin: auto;
background: #aaaaff;
color: #aaaa99;
}
#horizontalcssmenu ul {
padding:0;
margin:0;
list-style:none;
}
#horizontalcssmenu li {
float:left;
position:relative;
padding-right: 20px;
padding-left: 8px;
padding-top: 4px;
display:block;
border-right :4px solid #555599;
}
#horizontalcssmenu ul li a {
text-decoration: none;
color: black;
padding: 4px;
}
#horizontalcssmenu ul li a:hover {
text-decoration: none;
color: white;
background: #444499;
border-radius: 6px;
}
#horizontalcssmenu li ul {
display:none;
position:absolute;
}
#horizontalcssmenu li:hover ul{
display:block;
background:#aaaaff;
height:auto;
width: auto;
padding: 4px;
/*width:8em; */
}
#horizontalcssmenu li ul li{
clear:both;
border-style:none;
}
The html template:
<div id="topmenubar">
<div class="horizontalcssmenu" id="horizontalcssmenu">
<ul id="cssmenu">
<li><a href="/">Home</a></li>
<li><a href="/login">Login</a></li>
<li><a>Menu with option</a>
<ul>
<li><a href="#">Option #1</a></li>
<li><a href="#">Option #2</a></li>
</ul>
</li>
</ul>
</div>
</div>
<div style="clear:both;"></div>
The menu is an override of li and ul and handles hyperlinks a little different too. It makes it easy to extend and has no javascript overhead. Check the article I mentioned for a full breakdown.
Footer
.footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 50px;
}
The footer I find a useful thing to define. It will always show at the very bottom of the screen and is useful for things like a cookie notification.
Darkmode Toggle
I’m one of those people that can’t read bright sites, to me a darkmode is a necessary thing. I will show you how to handle it with css and a little javascript which can be toggled on and off.
The Toggle
<label class="switch">
<input type="checkbox" id="darkmode-cb">
<span class="slider round"></span>
</label>
.switch {
position: relative;
display: inline-block;
width: 40px;
height: 22px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #2196F3;
}
input:focus + .slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked + .slider:before {
-webkit-transform: translateX(16px);
-ms-transform: translateX(16px);
transform: translateX(16px);
}
.slider.round {
border-radius: 14px;
}
.slider.round:before {
border-radius: 50%;
}
This takes a html checkbox and re-styles it as a tactile toggle.
The javascript code
function darkmodeclick() {
var x=$("#darkmode-cb").prop('checked');
if(x) {
Cookies.set('darkmode', true, {SameSite:'Strict'});
} else {
Cookies.set('darkmode', false, {SameSite:'Strict'});
}
location.reload();
};
$(document).ready(function() {
console.log('ready');
var xc = Cookies.get("darkmode");
var x = (xc=="true")?true:false;
if(x == undefined) {
Cookies.set("darkmode", false, {SameSite:"Strict"});
x = false;
}
console.log("x: " + x)
if(x == true) {
$("#darkmode-cb").prop('checked', true);
}
var cb = $("#darkmode-cb").prop('checked')?true:false;
console.log("checked: " + cb);
if(x) {
console.log("Dark Mode");
$('head').append('<link rel="stylesheet" href="/resources/css/dark.css" type="text/css"></link>');
$('head').append('<link rel="stylesheet" href="/resources/css/menu.css" type="text/css"></link>');
$('head').append('<link rel="stylesheet" href="/resources/css/darkmode-slider.css" type="text/css"></link>');
} else {
console.log("Day Mode");
$('head').append('<link rel="stylesheet" href="/resources/css/bright.css" type="text/css"></link>');
$('head').append('<link rel="stylesheet" href="/resources/css/menu.css" type="text/css"></link>');
$('head').append('<link rel="stylesheet" href="/resources/css/darkmode-slider.css" type="text/css"></link>');
}
});
It basically initialises on page load using jQuery. It checks for a cookie “darkmode” and decides whether or not to inject either bright mode css or dark mode css.
golang go programming web html css javascript darkmode html darkmode javascript darkmode css menu css footer
1667 Words
2021-02-03 13:32