Search icon
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletters
Free Learning
Arrow right icon
Go Programming - From Beginner to Professional - Second Edition

You're reading from  Go Programming - From Beginner to Professional - Second Edition

Product type Book
Published in Mar 2024
Publisher Packt
ISBN-13 9781803243054
Pages 680 pages
Edition 2nd Edition
Languages
Author (1):
Samantha Coyle Samantha Coyle
Profile icon Samantha Coyle

Table of Contents (30) Chapters

Preface Part 1: Scripts
Chapter 1: Variables and Operators Chapter 2: Command and Control Chapter 3: Core Types Chapter 4: Complex Types Part 2: Components
Chapter 5: Functions – Reduce, Reuse, and Recycle Chapter 6: Don’t Panic! Handle Your Errors Chapter 7: Interfaces Chapter 8: Generic Algorithm Superpowers Part 3: Modules
Chapter 9: Using Go Modules to Define a Project Chapter 10: Packages Keep Projects Manageable Chapter 11: Bug-Busting Debugging Skills Chapter 12: About Time Part 4: Applications
Chapter 13: Programming from the Command Line Chapter 14: File and Systems Chapter 15: SQL and Databases Part 5: Building For The Web
Chapter 16: Web Servers Chapter 17: Using the Go HTTP Client Part 6: Professional
Chapter 18: Concurrent Work Chapter 19: Testing Chapter 20: Using Go Tools Chapter 21: Go in the Cloud Index Other Books You May Enjoy

Web Servers

Overview

This chapter introduces you to different ways of creating an HTTP server to accept requests from the internet. You will be able to understand how a website can be accessed and how it can respond to a form. You will also learn how to respond to requests from another software program.

By the end of this chapter, you’ll be able to create an HTTP server that renders a simple message. You will also know how to create an HTTP server that renders complex data structures that serve local static files. Further, you know how to create an HTTP server that renders dynamic pages and works with different ways of routing. Finally, you will know how to create a REST service, accept data through a form, and accept JSON data.

Technical requirements

To complete and run the examples in this chapter, you will need your favorite IDE and the latest version of the Go compiler. At the time of writing, this is 1.21. All the examples will use the standard Go library. You can refer to this book’s GitHub repository for the code in this chapter: https://github.com/PacktPublishing/Go-Programming-From-Beginner-to-Professional-Second-Edition-/tree/main/Chapter16.

Introduction

In this chapter, we will dig into how a remote server is created, so if you already know how to request information, you will see how to reply to these requests.

A web server is a program that uses the HTTP protocol – hence, the HTTP server – to accept requests from any HTTP client (web browser, another program, and so on) and respond to them with an appropriate message. When we browse the internet with our browser, it will be an HTTP server that will send an HTML page to our browser and we will be able to see it. In some other cases, a server will not return an HTML page but a different message that’s appropriate to the client.

Some HTTP servers provide an API that can be consumed by another program. Think of when you want to register with a website, and you are asked if you want to sign up through Facebook or Google. This means that the website you want to register with will consume a Google or Facebook API to get your details. These APIs generally...

How to build a basic server

The simplest HTTP server that we can create is a Hello World server. This is a server that returns a simple message stating Hello World and will not do anything else. It is not very useful, but it is a starting point to see what default Go packages give us and is the basis for any other more complex server. The aim is to have a server that runs on a specific port on your machine’s local host and accepts any path under it. Accepting any path means that when you test the server with your browser, it will always return the Hello World message and a status code of 200. Of course, we could return any other message, but, for historical reasons, the simplest project you learn when you study programming is always some sort of software that returns a message stating Hello World. In this case, we will see how this can be done and then visualized in a normal browser, before perhaps being put on the internet and shared with billions of users, although users may...

HTTP handler

To react to an HTTP request, we need to write something that, we usually say, handles the request; hence, we call this something a handler. In Go, we have several ways to do that, and one way is to implement the handler interface of the http package. This interface has one pretty self-explanatory method, and this is as follows:

ServeHTTP(w http.ResponseWriter, r *http.Request)

So, whenever we need to create a handler for HTTP requests, we can create a struct that includes this method and we can use it to handle an HTTP request. Here’s an example:

type MyHandler struct {}
func(h MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {}

This is a valid HTTP handler and you can use it like so:

http.ListenAndServe(":8080", MyHandler{})

Here, ListenAndServe() is a function that will use our handler to serve the requests; any struct that implements the handler interface will be fine. However, we need to let our server do something.

As...

Simple routing

The server we built in the previous exercise doesn’t do much – it just responds with a message; we cannot ask anything else. Before we can make our server more dynamic, let’s imagine we want to create an online book and we want to be able to select a chapter just by changing the URL. At the moment, if we browse the following pages, we’ll always see the same message:

http://localhost:8080
http://localhost:8080/hello
http://localhost:8080/chapter1

Now, we want to associate different messages with these different paths on our server. We will do this by introducing some simple routing to our server.

A path is what you see after 8080 in the URL, where 8080 is the port number we chose to run the server on. This path can be one number, a word, a set of numbers, or character groups separated by a /. To do this, we will use another function of the net/http package:

HandleFunc(pattern string, handler func(ResponseWriter, *Request))

Here...

Handler versus handler function

As you may have noticed, we used two different functions before, http.Handle and http.HandleFunc, both of which have a path as their first parameter, but which differ in terms of the second parameter. These two functions both ensure that a specific path is handled by a function. http.Handle, however, expects http.Handler to handle the path, while http.HandleFunc expects a function to do the same.

As we’ve seen before, http.Handler is any struct that has a method with this signature:

ServeHTTP(w http.ResponseWriter, r *http.Request)

So, in both cases, there will always be a function with http.ResponseWriter and *http.Request as parameters that will handle the path. When one or the other might be chosen may just be a matter of personal preference in many cases, but it might be important – when creating a complex project, for example – to choose the right method. Doing so will ensure that the structure of the project is optimal...

Adding middleware

Sometimes, you will need to create a lot of functions to handle HTTP requests, maybe serving different paths in a URL, all performing different actions. You might need to create a function to handle a server returning a list of users, one with a list of projects, a route for updating some details, and all the functions doing different things. It might happen, however, that although these functions perform different actions, they will also have something in common. A common example is when these functions have to be performed on a secured environment, which means only for users that have been logged in. Let’s look at a very simple example and consider the following two functions:

http.HandleFunc(
  "/hello1",
  func(w http.ResponseWriter,
  r *http.Request,
){
  msg := "Hello there, this is function 1"
  w.Write([]byte(msg))
})
http.HandleFunc(
  "/hello2",
  ...

Dynamic content

A server that serves only static content is useful, but there is much more that can be done. An HTTP server can deliver content based on a more granular request, which is done by passing some parameters to the server. There are many ways to do so, but one simple way is to pass parameters to querystring. If the URL of the server is as follows:

http://localhost:8080

Then, we can add something like this:

http://localhost:8080?name=john

Here,?name=john is called a querystring string as it is a string representing a query. In this case, querystring sets a variable called name with a value of john. This way of passing parameters is generally used with GET requests, while a POST request will generally make use of the body of the request to send parameters. We will begin by looking at how to accept parameters for a GET request since this request is made by simply opening our browser on a specific address. We will see how to handle a POST request through a form later...

Templating

Although JSON can be the best choice when complex data structures have to be shared across software programs, in general, this is not the case when the HTTP server is supposed to be consumed by humans. In the previous exercises and activities, the chosen way to format a piece of text has been the fmt.Sprintf function, which is good for formatting texts, but is simply insufficient when more dynamic and complex text is required. As you will have noticed in the previous exercise, the message that was returned in case a name was passed as a parameter to the URL observed a specific pattern, and this is where a new concept comes in – the template. A template is a skeleton from which complex entities can be developed. Essentially, a template is like text with some blanks. A template engine will take some values and fill in the blanks, as shown in the following diagram:

Figure 16.16: Templating example

Figure 16.16: Templating example

As you can see, {{name}} is a placeholder...

Static resources

Everything you’ve learned so far in this book, up to the previous exercise, is sufficient to build web applications and dynamic websites; you just need to put all the pieces together.

What you’ve been doing in this chapter is returning messages that are different but all hardcoded as strings. Even dynamic messages have been based on templates hardcoded in the source file of the exercises and activities. Now, let’s consider something. In the case of the first hello world server, the message never changed. If we wanted to modify the message and return a Hello galaxy message, we would have to change the text in the code and then recompile and/or run the server again. What if you wanted to sell your simple “hello” server and give the option to everybody to specify a custom message? Of course, you should give the source code to everybody so that they can recompile and run the server.

Although you might want to embrace open source...

Getting some style

So far, you’ve seen how to serve one static page and you might consider serving a few pages with the same method, maybe creating a handler struct with the name of the file to serve as an attribute. This might be impractical for large numbers of pages, although, in some cases, it is necessary. A web page, however, does not include just HTML code – it may also include images and styles, as well as some frontend code.

It is not within the scope of this book to teach you how to build HTML pages, and even less how to write JavaScript code or CSS style sheets, but you need to know how to serve these documents as we use a small CSS file to build our example.

Serving static files and putting templates in different files, or generally using external resources, is a good way to separate concerns on our projects and make our projects more manageable and maintainable, so you should try to follow this approach in all your projects.

To add a style sheet...

Getting dynamic

Static assets are generally served as they are, but when you want to create a dynamic page, you might want to make use of an external template, which you can use on the fly, so that you can change the template without having to restart your server, or that you can load on startup, which means you will have to restart your server following any change (this is not strictly true, but we need some concepts of concurrent programming to make it happen). Loading a file at startup is done simply for performance reasons. Filesystem operations are always the slowest, and even if Go is a fairly fast language, you might want to take performance into account when you want to serve your pages, especially if you have many requests from multiple clients.

As you may recall, we used the standard Go templates to make dynamic pages. Now, we can use the template as an external resource, put our template code in an HTML file, and load it. The template engine can parse it and then fill...

Embedding external files

In the previous sections, you learned about a very interesting technique, but having external files to read can be problematic when deploying something to production, especially with Go, where one of its strong features is building a single executable. Fortunately, there is a package in Go called embed that allows us to add external files to our final binary so that we need the original file when we develop, but we do not need to share this file with anybody else as it will be compiled and added to our final binary. Let’s see how this works.

Let’s imagine that you have a simple template file and want to use it on your web server:

mytemplate.html
<h1>{{.Text}}</h1>

Let’s look at a small program that does exactly that, using what you’ve learned in the previous chapter:

package main
import (
  "html/template"
  "log"
  "net/http"
)
func main() {
 ...

Summary

In this chapter, you were introduced to the server side of web programming. You learned how to accept requests from HTTP clients and respond appropriately. You also learned how to separate the possible requests into different areas of an HTTP server via paths and sub-paths. For this, you used a simple routing mechanism with the standard Go HTTP package.

Then, you learned how to return your response to suit different consumers: JSON responses for synthetic clients, and HTML pages for human access.

Next, you learned how to use templates to format your plain text and HTML messages, using the standard templating package. You learned how to serve and use static resources, serving them directly through a default file server or a template object.

After that, you learned how to create a middleware and how to embed external files inside your binary for better portability. At this stage, you know all the basics for building production-grade HTTP servers, although you might want...

lock icon The rest of the chapter is locked
You have been reading a chapter from
Go Programming - From Beginner to Professional - Second Edition
Published in: Mar 2024 Publisher: Packt ISBN-13: 9781803243054
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 $15.99/month. Cancel anytime}