Practice is the best way to learn

Learn the Go language by developing Web services

This article is suitable for students who have some programming background, but do not have the Go language foundation.

Also known as “trick you” to learn the Go language series.

This is a readable series, and I hope you can read it in the car, in the bathroom, in the restaurant, and the code is concise and practical.

Learning requires motivation

What can Go do? Why learn Go?

This series of articles will learn the Go language step by step, taking Web development as an example of programming development with the largest demand and most widely used. By the end of this series, you have a clear understanding of the basics of Web development in Go, and you’ll be amazed at the simplicity, efficiency, and freshness of the language.

Feedback is what you remember

Deliberate Practice says that learning requires immediate feedback in order to enhance the learning experience.

Each section of this series contains a working piece of code, and you can follow it step by step to experience each piece of code on your own computer.

Don’t learn what you don’t need

This paper introduces knowledge points around examples. The lack of syntax and keywords will distract you when you don’t know what they are for.

Hopefully, after reading this series, you will have more desire to learn the Go language and become a qualified Gopher

Gopher, the cutie from the Go Logo. This refers specifically to the nickname that Go programmers give themselves.

How to set up a Go development environment in 10 minutes

1. Download the Go language installation file

Visit the Go official website download page:

golang.org/dl

Microsoft Windows, Apple MacOS, Linux and Source are available for download.

Download the installation package of the corresponding OPERATING system.

2. Install the software as prompted

3. Configure environment variables

There is one more important “environment variable” that needs to be configured before you can actually code with Go: “$GOPATH”

The GOPATH environment variable specifies the location of the workspace. If GOPATH is not set, it is assumed to be $HOME/go on Unix systems and %USERPROFILE%\go on Windows. If you want to use a custom location as a workspace, you can set the GOPATH environment variable.

The GOPATH environment variable is used to set the working directory path necessary for Go compilation to execute files, package source code, and dependency packages. After Go1.11, the new wooden block management can no longer rely on $GOPATH/ SRC, but still need to use $GOPATH/ PKG path to store dependency packages.

First, create a directory to use as the GOPATH directory

Then set the environment variable GOPATH:

Linux & MacOS:


Importing environment variables

$ export GOPATH=$YOUR_PATH/go

Save environment variables

$ source ~/.bash_profile

Windows:


Control Panel -> System -> Advanced System Settings -> Advanced -> Environment Variable Settings

The directory specified by GOPATH generates three subdirectories:

  • Bin: depositgo installCompiled executable binaries
  • PKG: depositgo installThis is where the compiled package files are stored
  • SRC: depositgo getCommand to download the source package file

4. Check the environment

Open the command line tool and run

$ go env

If you see something like this, the Go locale is installed.


GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/zeta/Library/Caches/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/zeta/workspace/go"
GOPROXY="https://goproxy.io"
GORACE=""
GOROOT="/usr/local/go"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/7v/omg2000000000000019/T/go-build760324613=/tmp/go-build -gno-record-gcc-switches -fno-common"
Copy the code

5. Choose a handy editor or IDE

Many common editors or ides support Go for example

  • Atom
  • Visual Studio Code
  • Sublime Text2
  • ItelliJ Idea

There are ides dedicated to the Go language

  • LiteIDE
  • Goland

IDE for both configuration and use simpler than general editor/IDE, but I still recommend the use of general editor/IDE, because in the process of development will certainly need to write some other language program or script, dedicated IDE is weak in other languages, to switch back and forth different editor/IDE window can be very inefficient.

In addition, dedicated IDE provides a lot of efficient tools, in the compilation, debugging are very convenient, but learning stage, it is recommended that you manually execute command compilation, debugging, is conducive to master Go language.

Four lines of Hello World! The core of what can be expressed

The command line code only works on Linux and MacOS. For Windows, follow the instructions in Windows.

1. Create projects

Create a folder and go to it

$ mkdir gowebserver && cd gowebserver

Create a new file, main.go

$ touch main.go

2. Open the file with an editor and type the following code:

package main

import "fmt"

func main(a) {
    fmt.Println("Hello, world")}Copy the code

3. Open the CLI and run the following command

$ go run main.go

See the terminal output:

Hello, the world

The first Go code is done

This is a simple Hello World, but it contains many of the core elements of Go programming, which we’ll cover in more detail.

Packages and functions

packageThat package &importImport packages

Go programs are made up of packages.

The first line of code declares the program’s own package, using the package keyword. The package keyword must be the first line of code to appear.

In the sample code, declare the package name main

On the second line of the code, import the “FMT” package, using the import keyword. By default, the package name of the import package is the same as the last element of the import path, such as import “math/rand”. When using this package in code, rand is used directly, such as rand.new ().

Import packages can be written on multiple lines or in groups, for example:

import "fmt"
import "math/rand"
Copy the code

Or group

import (
    "fmt"
    "math/rand"
)
Copy the code

The FMT package is a package built into the Go language for output and printing.

funcKey word: define function

Func is short for function, which in the Go language is the keyword that defines a function.

Func defines a function of the format:

funcThe function name(Parameter 1 type, parameter 2 type){function body}Copy the code

In this example, we define a main function. The main function takes no arguments. I’m gonna call the FMT package Println inside the main body, and I’m gonna print the string “Hello, world” on the console.

The entry point for all Go programs is the main function main.main() in the main package, so every executable Go program should have a main package and a main function.

Now that we’ve covered just a few of those, let’s formally learn the Go language by building a simple Web service

0 dependencies to create a Web service

Start with the code

Open the previously created main.go file and modify the code as follows:

package main

import (
    "fmt"
    "net/http"
)

func myWeb(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "This is the beginning.")}func main(a) {
    http.HandleFunc("/", myWeb)

    fmt.Println("The server is about to start. Visit http://localhost:8080")

    err := http.ListenAndServe(": 8080".nil)
    iferr ! =nil {
        fmt.Println("Server startup error:", err)
    }
}
Copy the code

Save the file, and then enter the command in the command line tool to run the program

$ go run main.go

At this point, you’ll see a prompt printed out with fmt.println. If you go to http://localhost:8080 in your browser, you’ll get a page that says, “This is a start.”

Interpretation of the

We know how a program works from the order in which it runs

First, define Package Main, then import the package.

Here, a new package, net/ HTTP, is imported, which is official and implements various HTTP client and server functions. All of the Go language’s capabilities for developing Web services are based on this package (as are all other third-party Go Language Web frameworks without exception)

See firstmainWhat happens in the function

The first sentence matches the routing and processing functions

http.HandleFunc("/", myWeb)

Call the HandleFunc method of the HTTP package, matching a route to a handler function, myWeb.

This code means that when you access the address http://localhost/, you are calling the myWeb function.

Second sentence, print a sentence on the console with FMT, purely as a hint.

Third, start the service and listen on the port

err := http.ListenAndServe(": 8080".nil)
Copy the code

ListenAndServe takes two arguments, the first is to specify the port number to listen on, and the second is to specify the handler to handle the request. Usually this parameter is nil, indicating that the default ServeMux is used as the handler.

What is nil?

Nil is null in other languages.

What is the handler? What is ServeMux? ServeMux is an HTTP request multiroute multiplexer. It matches the URL of each incoming request with a list of registered patterns and invokes the handler of the pattern that best matches the URL. Familiar? Remember http.handlefunc? He is simply adding a URL to match the handler function to the DefaultServeMux in the HTTP package. The default ServeMux in HTTP packages is usually used, so supplying nil in the second argument to the http.listenandServe function is fine

The ListenAndServe function listens until forced exit or an error occurs.

If an error occurs, the function exits the listening and returns an object of type error, so the returned object is received with the err variable. Next, check whether err is empty, print out the error content, and the program ends.


Here are two things about Go

1. Define variables

Go language is static language, need to define variables, define variables with the keyword var

 var   str   string = "my string"
/ / ^ ^ ^
// Keyword variable name type
Copy the code

Go also offers a simple way to define variables :=, which automatically defines variable types based on the assigned object, much like a scripting language:

str := "my string"
Copy the code

2. Error handling

iferr ! =nil{
    / / processing...
}
Copy the code

In the Go language, this is a very common error handling operation. Another type of panic exception, it is recommended not to use it or to use it as little as possible.

The Go language specifies that if a function can fail, it should return an error object that contains at least one error () method error message.

Therefore, in Go, you do not see a try/catch statement, the function uses error to pass the error, and the if statement determines the error object and handles the error.


3. If statements

As in most languages, the only difference is that expressions don’t need to be wrapped in ().

In addition, if in Go can embed an expression with; For example, the code in the example could be changed to:

if err := http.ListenAndServe(": 8080".nil); err ! =nil {
    fmt.Println("Server startup error:", err)
}
Copy the code

The life cycle of the err variable is valid only in the if block.

Request processing myWeb function

In the main function, match myWeb with the route/with http.handlefunc.

The HandleFunc function defines two parameters w,r, of type http.ResponseWriter and *http.Request, w being the ResponseWriter and r being the pointer to the Request object.

Response flow writer W: Used to write HTTP response data

The request object * r: contains all the information about the HTTP request. Note that the pointer is used here. When defining parameters, use * to mark the type, indicating that this parameter needs a pointer to the object of this type.

When there is a request path /, the request object and the response flow writer are passed to the myWeb function, which handles the request.

Red pointer in Go language: In Go language, except for map, slice and chan, other functions are passed by value. Therefore, if the effect of passing reference is needed, the pointer of the object can be passed to achieve it. In Go, the pointer to an object is taken with & and the value is taken with *, for example:

mystring := "hi"
/ / pointer
mypointer := &mystring
/ / value
mystring2 := *mypointer

fmt.Println(mystring,mypointer,mystring2)
Copy the code

Put this code in the main function, $go run main.go and see

MyWeb function body

fmt.Fprintf(w, "This is the beginning.")
Copy the code

Once again I ran into my old acquaintance FMT, this time using his Fprintf function to write the string “This is a start” to the W response stream writer object. W The content written in the Response stream writer will be output to the user’s browser page in Response.

To summarize what you and it do, from coding to running:

  1. Define a function myWeb that takes two parameters, the response stream writer and the request object
  2. In the main function, route/is bound to myWeb in the default ServeMux
  3. Run the default ServeMux listening on local port 8080
  4. Access local port 8080/routing
  5. HTTP passes both the request object and the response writer to myWeb processing
  6. MyWeb concludes the request by writing a sentence to the response flow.

There is very little code, but this is a basic Go language Web service application.

The first step in Web interaction is to Go HTTP to get the request parameters

Let’s start with the code

Open the main.go file and modify the myWeb function as follows:

func myWeb(w http.ResponseWriter, r *http.Request) {

    r.ParseForm() // It also parses the request body into a Form to get the POST Form data, which must be called first

    for k, v := range r.URL.Query() {
        fmt.Println("key:", k, ", value:", v[0])}for k, v := range r.PostForm {
        fmt.Println("key:", k, ", value:", v[0])
    }

    fmt.Fprintln(w, "This is the beginning.")}Copy the code

To run the program

$ go run main.go

Then submit a POST request using any tool (Postman is recommended) with URL parameters, or using cURL on the command line

curl --request POST \ --url 'http://localhost:8080/? name=zeta' \ --header 'cache-control: no-cache' \ --header 'content-type: application/x-www-form-urlencoded' \ --data description=helloCopy the code

The page and terminal command line tools allow the following:

key: name , value: zeta
key: description , value: hello
Copy the code

Interpretation of the

All the content of the HTTP Request is stored in the HTTP. Request object, which is the parameter r obtained by myWeb.

First, we call r.parseForm (), which populates data into R.column and R.postForm

Next, each argument in the r.ul.query () function and the r.ostform value is iterated over separately.

R.ul.query () and r.ostform are URL parameter objects and form parameter objects, respectively. They are key-value pairs with keys of type string and values of type string array.

In HTTP, parameters of the same name form arrays regardless of the URL or form.

Loop through: for… range

The only loop in Go is the for keyword. Here are four for loops in Go


// loop indefinitely, block thread, use without stopping, use caution!
for{}// Conditional loop, if a
for a < b{

}

// if I is less than 10, I increases by 1 after each cycle
for i:=0; i<10; i++{

}

//for... Range traverses objs, which must be of the map, slice, and chan types
for k, v := range objs{

}

Copy the code

The first three, loops you can think of as variants of conditional loops (infinite loops are unconditional loops).

This example uses for… Range loop, iterating over the object that can be traversed, and each loop assigns the key and value to the variables K and v, respectively


Our page is still just saying “This is a start”. We need a page where we can see people. That’s okay

You might also wonder if you could hardcode an HTML string when you output it? Sure, but the Go HTTP package provides a better way, an HTML template.

Next, we will use the HTML template to create a real page

Dynamically respond to data to visitors, Go HTTP HTML template + data binding

Reading an HTML template file, replacing the corresponding tags with data, and generating a full HTML string that responds to the browser is routine for all Web development frameworks. Go does the same thing.

The Go HTML package provides this functionality:

html/template

Start with code

Add the import HTML /template package and modify the myWeb function as follows:

import (
    "fmt"
    "net/http"
    "text/template" // Import template package
)

func myWeb(w http.ResponseWriter, r *http.Request) {

    t := template.New("index")

    t.Parse("<div id='templateTextDiv'>Hi,{{.name}},{{.someStr}}</div>")

    data := map[string]string{
        "name":    "zeta"."someStr": "This is the beginning.",
    }

    t.Execute(w, data)

    // FMT.Fprintln(w, "This is a start ")
}
Copy the code

From the command line, run $go run main.go to http://localhost:8080

See,

Hi,{{.name}},{{.somestr}}

{{.name}} and {{.somestr}}} are replaced with Zeta and that’s a start. Also, FMT.Fprintln is no longer used to output data to Response

But… This is still hard-coding HTML strings in code…

Don’t worry, the template package parses the file and continues to modify the code:

  1. Create a subdirectory under the root directory to store the templates file, then go to that directory and create a fileindex.htmlAnd write some HTML code (I’m not a good front end)

<html>
<head></head>
<body>
    <div>Hello {{.name}}</div>
    <div>{{.someStr}}</div>
</body>
</html>

Copy the code
  1. Modify themyWebfunction
func myWeb(w http.ResponseWriter, r *http.Request) {

    //t := template.New("index")
    //t.Parse("<div>Hi,{{.name}},{{.someStr}}<div>")
    // Comment out the last two sentences and use the following sentence
    t, _ := template.ParseFiles("./templates/index.html")

    data := map[string]string{
        "name":    "zeta"."someStr": "This is the beginning.",
    }

    t.Execute(w, data)

    // FMT.Fprintln(w, "This is a start ")
}
Copy the code

{{.name}} and {{.somestr}} are also replaced, right?

Interpretation of the

As you can see, the core function of the Template package is to temporarily save the HTML string parsing and then replace the {{}} in the HTML string with the data when Execute is called

In the first way t:= template.new (“index”) initializes a template object variable and parses the string template with a call to t.arse.

Next, create a map object that will be used for rendering.

Finally, the function t. execute is called, which not only renders the template with data, but also replaces the work of FMT.Fprintln function, and outputs to the Response data stream writer.

In the second way, we call the ParseFiles function of the template package directly to parse the index.html file in the relative path and create object variables.

knowledge

Two new things appear in this section: map type and assignment to “_”

The map type

Map type: Dictionary type (key-value pair). The URL /values type shown in the fetch request parameters section was extended from map

Map initialization can be made:


var data = make(map[string]string)
data = map[string]string{}

Copy the code

Make is a built-in function that can only be used to initialize map, slice, and chan, and differs from the other built-in function new in that it does not return a pointer, but only a type.

Map assigns the same value to dictionary objects in other languages. There are two ways to assign the value, as shown in the following code:


data["name"] ="zeta" / / assignment

name := data["name"] // Method 1. Common value

name,ok := data["name"] // Method 2. If no name key exists, ok is false

Copy the code

The variable OK in the code can be used to determine whether this item has been set. If the item does not exist, it will not be abnormal. The value taken out is the zero value of the type, such as the value of int, the non-existent item is 0. A value of string is an empty string if it does not exist, so you cannot tell if the item is set by a value of 0. Ok, will get true or false, check whether the item is set, true means it exists, false means it does not exist in map.

There are a few other features of MAPS in Go that you need to know:

  1. mapThe order of the items is not fixed, each traversal of the order is different, so can not use the order to judge the content
  2. mapYou can usefor... rangetraverse
  3. mapIn the function parameters are passed by reference (Go language, only map, slice, chan are passed by reference, the other are passed by value)

Assigned to “_”

A feature of Go is that if a variable is not used after it is defined, an error will be reported and it cannot be compiled. In general, this is fine, but in the rare case where we call a function that doesn’t need to use the return value, but doesn’t use it and can’t compile, what do we do?

“_” is used to solve this problem, and _ is used to discard the return value of the function. In this example, template.parsefiles (“./templates/index.html”) will return an error object in addition to the template object, but in this simple example, the probability of an error is very low, so I don’t want to deal with errors. Discard the error return value with an “_”.

Caution Caution: In a real project, do not discard errors. Any accident is possible, and throwing errors can make debugging very difficult when rare unexpected conditions occur. All errors should be handled, at least written to the log or printed to the console. (Remember, don’t throw away error, a lot of Gopher’s have a long history with this.)

OK, so far, the core of a simple web page in Go is done.

What about.js,.CSS, images?

right The template in this example is full of HTML code, a beautiful web page must also use images, JS scripts and CSS style files, but… Unlike PHP, the request path is matched to the handler function through HandleFunc. Would you want to output js, CSS, and images through the function and then use HandleFunc to match the URL path?

Handle well JS, CSS and images to make beautiful web pages, Go HTTP static file processing method

Take the example of referencing an index.js file in an index.html file.

Start with code

func main(a) {
     http.HandleFunc("/", myWeb)

     // static indicates the file service path
     staticHandle := http.FileServer(http.Dir("./static"))
     // match requests under /js/ to./static/js/
     http.Handle("/js/", staticHandle)

     fmt.Println("The server is about to start. Visit http://localhost:8080")
     err := http.ListenAndServe(": 8080".nil)
     iferr ! =nil {
          fmt.Println("Server startup error:", err)
     }
}
Copy the code

Create the static directory in the root directory of your project, go to the static directory, create the js directory, and create an index.js file in the js directory.

alert("Javascript running...");
Copy the code

Open the previous index.html file and add

Run $go run main.go and visit http://localhost:8080. A dialog box is displayed.

Interpretation of the

When the page is run in the browser, the browser will request /js/index.js

The program checks that the layer 1 route matches /js/, so it processes the request with the file service, matching the relative path of the program’s running path.

The Settings that match are these two sentences in the main.go file

     // static indicates the file service path
     staticHandle := http.FileServer(http.Dir("./static"))
     // match requests under /js/ to./static/js/
     http.Handle("/js/", staticHandle)
Copy the code

You can also write it as a sentence, which is easier to understand

// the browser will access the directory as a static file./static/js
http.Handle("/js/", http.FileServer(http.Dir("./static")))
Copy the code

Is simple… However, it may not satisfy the demand, because if

Http.handle (“/js/”, http.fileserver (http.dir (“./static”))) corresponds to./static/js

Http.handle (“/ CSS /”, http.fileserver (http.dir (“./static”))) corresponds to./static/ CSS

Http.handle (“/img/”, http.fileserver (http.dir (“./static”))) corresponds to./static/img

Http.handle (“/upload/”, http.fileserver (http.dir (“./static”))) corresponds to./static/upload

Thus all requested paths must match a subdirectory under a static directory.

What if I want to access static files or js, CSS, img, upload directories in the project root directory?

The HTTP package also provides a function http.StripPrefix to strip the prefix as follows:

    //http.Handle("/js/", http.FileServer(http.Dir("./static")))
    // Add http.stripprefix to:
    http.Handle("/js/", http.StripPrefix("/js/", http.FileServer(http.Dir("./static"))))
Copy the code

In this way, when accessing /js/ in the browser, it directly corresponds to the./static directory, without the need to add a /js/ subdirectory.

So, if you need to add more static directories to the root directory that match the path of the URL, you can do this:

HTTP. Handle (“/js/”, HTTP StripPrefix (“/js/”, HTTP FileServer (HTTP. Dir (“. / js “)))) corresponding to the/js

HTTP. Handle (“/CSS/”, HTTP StripPrefix (“/CSS/”, HTTP FileServer (HTTP. Dir (“. / CSS “)))) corresponding to the/CSS

HTTP. Handle (“/img/”, HTTP StripPrefix (“/img/”, HTTP FileServer (HTTP. Dir (“. / img “)))) corresponding to the/img

Http. Handle(“/upload/”, http.stripprefix (“/upload/”, http.fileserver (http.dir (“./upload”)))) corresponds to./upload

At this point, a complete Web service application is presented in terms of flow.

To sort out the basic flow of a Go Web application:

  1. Define the request handler function
  2. The HTTP packet’s HandleFunc matches the handler and route
  3. ListenAndServe Enables listening

When there is an HTTP request:

  1. The port on which the HTTP request is listened
  2. The request object and response writer are passed to the matching handler based on the route
  3. After some operation, the handler writes data to the response writer
  4. Responds to the requested browser

Last compiler

Previous debugging used the go run command to run the program.

You’ll notice that the source code is recompiled every time you run Go Run. How do you run your program on a machine without a GO environment?

Using the go build command, it compiles the source to produce an executable binary.

The simplest go build command takes no arguments. It automatically looks for the main() function in the main package in the directory and then compiles the dependencies into an executable file.

The relative paths of other dependent files, such as the templates folder and the static folder in the example, need to be the same as the compiled executable.

By default, go Builds are compiled into executables for the developing operating system. Cross-compilation is required to compile executables for other operating systems.

Examples include compiling Linux and MacOSX systems to Windows

GOOS=windows GOARCH=amd64 go build

On Windows, you need to use the SET command, such as compiling to Linux on Windows

SET GOOS=linux
SET GOARCH=amd64
go build main.go
Copy the code

Conclusion, what did you learn? What else is there to learn?

What did you learn?

  1. Quickly and easily build the Go development environment
  2. Import package, declare package
  3. Func defines functions
  4. Declaration methods for variables
  5. Go language exception handling
  6. The for loop
  7. The map type
  8. Use HTTP package, write a website program

This series is short and concise, and I hope you’ll learn a little more about Go and get a little more interested in it.

Other knowledge not covered

There’s a lot more to be a proper Gopher

  1. Struct structure
  2. Define methods for structs
  3. Interface Defines and implements an interface
  4. Chan type
  5. Slice type
  6. goroutine
  7. Panic processing

Future articles will cover more about Go programming

Welcome to pay attention to xiao code public number, and we learn together