Welcome to the next part of the journey with Golang programming language. For those of you who are entirely new to Golang, I advise you to go to the official “Getting started” tutorial, which you can find here, or to my previous post about Golang: An introduction to programming in Golang.
There is no doubt that learning by doing is better than digging up documentation and trying to remember as much as possible. With this assumption, I would like to show you how to make something up and running in Golang. We will create a simple “app”, which will serve a different kind of files with respect to their types.
Creating App’s Architecture
First things first. To get started, create a new folder for a project and a few subfolders in the folder where you keep your Golang programs:
mkdir golang-blog && cd golang-blog
mkdir src && mkdir bin && mkdir pkg
cd src && mkdir main
cd main && touch main.go
When you create your Go applications, you should always remember about keeping a folder structure in the same manner – this is a Go convention. It is worth to get familiar with a presentation that deals with organizing Go code. You can find it here.
Back to the topic. After you execute all the commands concerning folder structure in your terminal, open your
main.go in your favorite IDE. We will now create some simple server for serving our code in a browser.
Let me now explain what is going on in the code. We are importing a
net/http standard library package. Quoting official documentation:
Package http provides HTTP client and server implementations. The
http.HandleFunc is used to add some handlers to a
DefaultServeMux. In our case
someFunc is our handler.
For those who don’t know,
handlers are responsible for writing response headers and bodies. As long as some
code satisfies the
http.Handler interface, it can be a
handler. Or, in other words, a
ServerHTTP method must be specified in that
code with the following signature:
We use forward slash
http.HandleFunc for URL requests. We are simply telling Go to match every request to the most specific route registered. So if somebody navigates to the browser with your GO app running and tries to navigate to
, then this request will be matched with the closest defined, which in this case is
Then there is
starts an HTTP server with a given address and handler. It listens on port 8000 and
nil means we will be using
DefaultServerHTTP. You can find more about this package, in the official documentation.
Moving on. There is
func someFunc which takes the writer,
w , and writes back some request with
http.ResponseWriter to the user. The
req *http.Request is the incoming request.
w.Write(byte("Hello universe")) means that for the proper response to the incoming request, it dumps a string (or just bytes).
Now, from your project main folder, use:
go run src/main/main.go
and navigate to
localhost:8000 to see a message.
It is very important that you remember to use the command
go run from the main folder which is the root of the application. In our case, for now, everything will be just fine. That is because all of the code sits in
main.go. But if our project grows, there will be much more code in many folders, e.g. templates folder with some HTML. Running go from some subfolder will make it unable to get all the files outside that folder.
Bonus: Adding a Custom “MUX”
Previously, we were using a
DefaultServerHTTP. Now we are creating a custom ServeMUX which is
myMux := http.NewServeMux(). Creating your own
ServeMUX gives you more flexibility and of course is fun. We can say that
ServeMUX is an HTTP request router. It compares incoming requests to the list of predefined URL paths and fires up associated handlers for this path.
There are plenty of
ServeMUX or so-called multiplexors or muxes to choose from. Just to mention a very popular Gorilla Mux. There is a nice article about ServeMuxes and handlers, and you can find it here – worth reading.
Methods Are Calling
Take a second and look at the gist. Fire it up and see what is happening now. Note that we are using
nil which stands for
It is worth mentioning that GO is case sensitive – using small or capital letters matters A LOT. When we are defining
type person struct, it becomes a private type. It is accessible only in a package where it was introduced. If we wrote
type Person struct, then it would become a public type. It would be accessible from any fragment of our code after being imported into the package.
personOne is an instance (Object Oriented word – not commonly used in Golang) of a type.
struct in GO is a typed collection of fields which are useful when grouping data to form some kind of records. In our case, we have a field
someName and it takes a string as a value.
(p *person) ServeHTTP means that we are attaching to any type of person
(p *person), a struct
ServeHTTP method. In other words, the
ServeHTTP has a receiver of any person and any person is receiving a
ServeHTTP method. If we delete
(p *person) then our
func ServeHTTP will end up as a function. In our case, it is a method of any
Serving Some Files
In this step, we are adding two more packages.
io/ioutil which we will use for some input/output actions. We will be using
log for logging some errors or successes.
First, we define
MyHandler struct (private or public) with nothing in it. It is receiving
Then we are defining a method
func (this *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request).What is its job? For each HTTP request,
r *http.Request, it gets a
path from URL and then it slices it, beginning at forward slash
r.URL.Path[1:] and prints data from
path in the console
:= construct is a shorthand for initializing,
var path string = r.URL.Path[1:] This shorthand style can only be used inside a function.
Now we have to deal with data and errors. We initialize variable
err. The assigned
ioutil.ReadFile(string(path)) is responsible for reading any
err that it receives on a given
path and dumping it to a terminal.
Next, we make a condition when no error occurs –
if err == nil. If there is no error, it will respond to the request with a received data –
else, it will write a message with a 404 status –
Now, to see what will happen if there is some real file on a given URL:
mkdir public && cd public
mkdir css && cd css
app.css files and add some basic stuff:
Now, run the app from the root folder and navigate to:
Everything is great but… Why didn’t it serve the stylesheet? Well, if you open your developer’s tools console, you’ll find this message:
Resource interpreted as Stylesheet but transferred with MIME type text/plain
When you switch to
Network and click
app.css, you will find out that the
Response Headers/ Content-Type for stylesheet is
text/plain. Simply, the browser has no idea how to parse this file.
Setting Content Type
Now we will handle the problem of dynamically setting “Content-Type” based on a type of the file which will be served.
First of all, I added a package
strings which tests whether the string ends with the declared suffix.
I added a variable
contentType string. I’m just declaring it – no assignment.
Next, I make a condition
if strings.HasSuffix which checks if a string assigned to the variable
path ends with i.e. .css. Then, if a path ends with .css I assign contentType
text/css. I am looping over a few types and if none of them are true, I assign .
In the end, I am adding the specific content type to the header
Add('Content Type', contentType) and then return the data of any file I open.
Now you can start your Go server again, refresh a browser and see that everything works fine there. Open Developer Tools and check Network. You will see that now files have the appropriate content type.
That is all for this time. I am very glad that I can share my knowledge with you. Go further with the ideas shown above and have a lot of fun.