GoLang Tutorial - Web App Part 1
In this post, we'll learn how to make a simple application. This will have several parts and it's based on https://golang.org/doc/articles/wiki/.
This will be a good addition to our quick basic tutorials we've practiced so far.
We'll create a new directory called gowiki somewhere under $GOTPAH:
$ echo $GOPATH ~/go $ mkdir -p $GOPATH/src/github.com/einsteinish/go_tutorial/gowiki
Create a file wiki.go like this:
We're importing the fmt and ioutil packages from the Go standard library. Later, as we implement additional functionality, we will add more packages to this import declaration.
A wiki consists of a series of pages, each of which has a title and a body (the page content). Here, we define Page as a struct with two fields representing the title and body.
type Page struct { Title string Body []byte }
The type []byte means a byte slice.
The Body element is a []byte rather than string because that is the type expected by the io libraries we will use.
The Page struct describes how page data will be stored in memory. But what about persistent storage? We can address that by creating a save() method on Page:
func (p *Page) save() error { filename := p.Title + ".txt" return ioutil.WriteFile(filename, p.Body, 0600) }
The save() method tells "This is a method named save that takes as its receiver p, a pointer to Page. It takes no parameters, and returns a value of type error."
Note that the (p *Page) takes the following syntax:
This method will save the Page's Body to a text file. For simplicity, we will use the Title as the file name.
The save method returns an error value because that is the return type of WriteFile (a standard library function that writes a byte slice to a file). The save method returns the error value, to let the application handle it should anything go wrong while writing the file. If all goes well, Page.save() will return nil (the zero-value for pointers, interfaces, and some other types).
The octal integer literal 0600, passed as the third parameter to WriteFile, indicates that the file should be created with read-write permissions for the current user only.
In addition to saving pages, we will want to load pages as well:
func loadPage(title string) *Page { filename := title + ".txt" body, _ := ioutil.ReadFile(filename) return &Page{Title: title, Body: body} }
The function loadPage constructs the file name from the title parameter, reads the file's contents into a new variable body, and returns a pointer to a Page literal constructed with the proper title and body values.
Functions can return multiple values. The standard library function io.ReadFile returns []byte and error. In loadPage, error isn't being handled yet; the "blank identifier" represented by the underscore (_) symbol is used to throw away the error return value (in essence, assigning the value to nothing).
But what happens if ReadFile encounters an error? For example, the file might not exist. We should not ignore such errors. Let's modify the function to return *Page and error.
func loadPage(title string) (*Page, error) { filename := title + ".txt" body, err := ioutil.ReadFile(filename) if err != nil { return nil, err } return &Page{Title: title, Body: body}, nil }
Callers of this function can now check the second parameter; if it is nil then it has successfully loaded a Page. If not, it will be an error that can be handled by the caller.
At this point we have a simple data structure and the ability to save to and load from a file. Let's write a main function to test what we've written:
func main() { p1 := &Page{Title: "TestPage", Body: []byte("This is a sample Page.")} p1.save() p2, _ := loadPage("TestPage") fmt.Println(string(p2.Body)) }
After compiling and executing this code, a file named TestPage.txt has been created, containing the contents of p1. The file would then be read into the struct p2, and its Body element printed to the screen.
Let's start with a simple web server a similar one we did before (Web):
The main function begins with a call to http.HandleFunc, which tells the http package to handle all requests to the web root ("/") with handler.
It then calls http.ListenAndServe, specifying that it should listen on port 8080 on any interface (":8080"). (Don't worry about its second parameter, nil, for now.) This function will block until the program is terminated.
ListenAndServe() always returns an error, since it only returns when an unexpected error occurs. In order to log that error we wrap the function call with log.Fatal. The Fatal function calls os.Exit(1) after writing the log message.
The function handler is of the type http.HandlerFunc. It takes an http.ResponseWriter and an http.Request as its arguments.
An http.ResponseWriter value assembles the HTTP server's response; by writing to it, we send data to the HTTP client.
An http.Request is a data structure that represents the client HTTP request. r.URL.Path is the path component of the request URL. The trailing [1:] means "create a sub-slice of Path from the 1st character to the end." This drops the leading "/" from the path name.
Run this program and access the URL:
Continues to Web Application Part 2 (Using net/http).
Go Tutorial
- GoLang Tutorial - HelloWorld
- Calling code in an external package & go.mod / go.sum files
- Workspaces
- Workspaces II
- Visual Studio Code
- Data Types and Variables
- byte and rune
- Packages
- Functions
- Arrays and Slices
- A function taking and returning a slice
- Conditionals
- Loops
- Maps
- Range
- Pointers
- Closures and Anonymous Functions
- Structs and receiver methods
- Value or Pointer Receivers
- Interfaces
- Web Application Part 0 (Introduction)
- Web Application Part 1 (Basic)
- Web Application Part 2 (Using net/http)
- Web Application Part 3 (Adding "edit" capability)
- Web Application Part 4 (Handling non-existent pages and saving pages)
- Web Application Part 5 (Error handling and template caching)
- Web Application Part 6 (Validating the title with a regular expression)
- Web Application Part 7 (Function Literals and Closures)
- Building Docker image and deploying Go application to a Kubernetes cluster (minikube)
- Serverless Framework (Serverless Application Model-SAM)
- Serverless Web API with AWS Lambda
- Arrays vs Slices with an array left rotation sample
- Variadic Functions
- Goroutines
- Channels ("<-")
- Channels ("<-") with Select
- Channels ("<-") with worker pools
- Defer
- GoLang Panic and Recover
- String Formatting
- JSON
- SQLite
- Modules 0: Using External Go Modules from GitHub
- Modules 1 (Creating a new module)
- Modules 2 (Adding Dependencies)
- AWS SDK for Go (S3 listing)
- Linked List
- Binary Search Tree (BST) Part 1 (Tree/Node structs with insert and print functions)
- Go Application Authentication I (BasicAuth, Bearer-Token-Based Authentication)
- Go Application Authentication II (JWT Authentication)
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization