Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
.Go Programming Blueprints

You're reading from   .Go Programming Blueprints Build real-world, production-ready solutions in Go using cutting-edge technology and techniques

Arrow left icon
Product type Paperback
Published in Oct 2016
Publisher Packt
ISBN-13 9781786468949
Length 394 pages
Edition 2nd Edition
Languages
Arrow right icon
Author (1):
Arrow left icon
Mat Ryer Mat Ryer
Author Profile Icon Mat Ryer
Mat Ryer
Arrow right icon
View More author details
Toc

Table of Contents (13) Chapters Close

Preface 1. Chat Application with Web Sockets 2. Adding User Accounts FREE CHAPTER 3. Three Ways to Implement Profile Pictures 4. Command-Line Tools to Find Domain Names 5. Building Distributed Systems and Working with Flexible Data 6. Exposing Data and Functionality through a RESTful Data Web Service API 7. Random Recommendations Web Service 8. Filesystem Backup 9. Building a Q&A Application for Google App Engine 10. Micro-services in Go with the Go kit Framework 11. Deploying Go Applications Using Docker Appendix. Good Practices for a Stable Go Environment

A simple web server

The first thing our chat application needs is a web server that has two main responsibilities:

  • Serving the HTML and JavaScript chat clients that run in the user's browser
  • Accepting web socket connections to allow the clients to communicate

Note

The GOPATH environment variable is covered in detail in Appendix, Good Practices for a Stable Go environment. Be sure to read that first if you need help getting set up.

Create a main.go file inside a new folder called chat in your GOPATH and add the following code:

package main 
import ( 
  "log" 
  "net/http" 
) 
func main() { 
  http.HandleFunc("/", func(w http.ResponseWriter, r  *http.Request) { 
    w.Write([]byte(` 
      <html> 
        <head> 
          <title>Chat</title> 
        </head> 
        <body> 
          Let's chat! 
        </body> 
      </html> 
    )) 
  }) 
  // start the web server 
  if err := http.ListenAndServe(":8080", nil); err != nil { 
    log.Fatal("ListenAndServe:", err) 
  } 
} 

This is a complete, albeit simple, Go program that will:

  • Listen to the root path using the net/http package
  • Write out the hardcoded HTML when a request is made
  • Start a web server on port :8080 using the ListenAndServe method

The http.HandleFunc function maps the path pattern / to the function we pass as the second argument, so when the user hits http://localhost:8080/, the function will be executed. The function signature of func(w http.ResponseWriter, r *http.Request) is a common way of handling HTTP requests throughout the Go standard library.

Tip

We are using package main because we want to build and run our program from the command line. However, if we were building a reusable chatting package, we might choose to use something different, such as package chat.

In a terminal, run the program by navigating to the main.go file you just created and execute the following command:

go run main.go

Tip

The go run command is a helpful shortcut for running simple Go programs. It builds and executes a binary in one go. In the real world, you usually use go build yourself to create and distribute binaries. We will explore this later.

Open the browser and type http://localhost:8080 to see the Let's chat! message.

Having the HTML code embedded within our Go code like this works, but it is pretty ugly and will only get worse as our projects grow. Next, we will see how templates can help us clean this up.

Separating views from logic using templates

Templates allow us to blend generic text with specific text, for instance, injecting a user's name into a welcome message. For example, consider the following template:

Hello {{name}}, how are you? 

We are able to replace the {{name}} text in the preceding template with the real name of a person. So if Bruce signs in, he might see:

Hello Bruce, how are you? 

The Go standard library has two main template packages: one called text/template for text and one called html/template for HTML. The html/template package does the same as the text version except that it understands the context in which data will be injected into the template. This is useful because it avoids script injection attacks and resolves common issues such as having to encode special characters for URLs.

Initially, we just want to move the HTML code from inside our Go code to its own file, but won't blend any text just yet. The template packages make loading external files very easy, so it's a good choice for us.

Create a new folder under our chat folder called templates and create a chat.html file inside it. We will move the HTML from main.go to this file, but we will make a minor change to ensure our changes have taken effect:

<html> 
  <head> 
    <title>Chat</title> 
  </head> 
  <body> 
    Let's chat (from template) 
  </body> 
</html> 

Now, we have our external HTML file ready to go, but we need a way to compile the template and serve it to the user's browser.

Tip

Compiling a template is a process by which the source template is interpreted and prepared for blending with various data, which must happen before a template can be used but only needs to happen once.

We are going to write our own struct type that is responsible for loading, compiling, and delivering our template. We will define a new type that will take a filename string, compile the template once (using the sync.Once type), keep the reference to the compiled template, and then respond to HTTP requests. You will need to import the text/templatepath/filepath, and sync packages in order to build your code.

In main.go, insert the following code above the func main() line:

// templ represents a single template 
type templateHandler struct { 
  once     sync.Once 
  filename string 
  templ    *template.Template 
} 
// ServeHTTP handles the HTTP request. 
func (t *templateHandler) ServeHTTP(w http.ResponseWriter, r  *http.Request) { 
  t.once.Do(func() { 
    t.templ =  template.Must(template.ParseFiles(filepath.Join("templates",
      t.filename))) 
  }) 
  t.templ.Execute(w, nil) 
} 

Tip

Did you know that you could automate the adding and removing of imported packages? See Appendix, Good Practices for a Stable Go Environment, on how to do this.

The templateHandler type has a single method called ServeHTTP whose signature looks suspiciously like the method we passed to http.HandleFunc earlier. This method will load the source file, compile the template and execute it, and write the output to the specified http.ResponseWriter method. Because the ServeHTTP method satisfies the http.Handler interface, we can actually pass it directly to http.Handle.

Tip

A quick look at the Go standard library source code, which is located at http://golang.org/pkg/net/http/#Handler, will reveal that the interface definition for http.Handler specifies that only the ServeHTTP method need be present in order for a type to be used to serve HTTP requests by the net/http package.

Doing things once

We only need to compile the template once, and there are a few different ways to approach this in Go. The most obvious is to have a NewTemplateHandler function that creates the type and calls some initialization code to compile the template. If we were sure the function would be called by only one goroutine (probably the main one during the setup in the main function), this would be a perfectly acceptable approach. An alternative, which we have employed in the preceding section, is to compile the template once inside the ServeHTTP method. The sync.Once type guarantees that the function we pass as an argument will only be executed once, regardless of how many goroutines are calling ServeHTTP. This is helpful because web servers in Go are automatically concurrent and once our chat application takes the world by storm, we could very well expect to have many concurrent calls to the ServeHTTP method.

Compiling the template inside the ServeHTTP method also ensures that our code does not waste time doing work before it is definitely needed. This lazy initialization approach doesn't save us much in our present case, but in cases where the setup tasks are time- and resource-intensive and where the functionality is used less frequently, it's easy to see how this approach would come in handy.

Using your own handlers

To implement our templateHandler type, we need to update the main body function so that it looks like this:

func main() { 
  // root 
  http.Handle("/", &templateHandler{filename: "chat.html"}) 
  // start the web server 
  if err := http.ListenAndServe(":8080", nil); err != nil { 
    log.Fatal("ListenAndServe:", err) 
  } 
} 

The templateHandler structure is a valid http.Handler type so we can pass it directly to the http.Handle function and ask it to handle requests that match the specified pattern. In the preceding code, we created a new object of the type templateHandler,  specifying the filename as chat.html that we then take the address of (using the & address of the operator) and pass it to the http.Handle function. We do not store a reference to our newly created templateHandler type, but that's OK because we don't need to refer to it again.

In your terminal, exit the program by pressing Ctrl + C and re-run it, then refresh your browser and notice the addition of the (from template) text. Now our code is much simpler than an HTML code and free from its ugly blocks.

Properly building and executing Go programs

Running Go programs using a go run command is great when our code is made up of a single main.go file. However, often we might quickly need to add other files. This requires us to properly build the whole package into an executable binary before running it. This is simple enough, and from now on, this is how you will build and run your programs in a terminal:

go build -o {name}
./{name}

The go build command creates the output binary using all the .go files in the specified folder, and the -o flag indicates the name of the generated binary. You can then just run the program directly by calling it by name.

For example, in the case of our chat application, we could run:

go build -o chat
./chat

Since we are compiling templates the first time the page is served, we will need to restart your web server program every time anything changes in order to see the changes take effect.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime