GoLang Tutorial - Web App 3 (Adding "edit" capability)
Continued from Web Application Part 2.
In this post, we'll be adding "EDIT" capability to the wiki pages we created in the previous tutorial.
So, let's create two new handlers: one named editHandler to display an 'edit page' form, and the other named saveHandler to save the data entered via the form:
func main() { http.HandleFunc("/view/", viewHandler) http.HandleFunc("/edit/", editHandler) http.HandleFunc("/save/", saveHandler) log.Fatal(http.ListenAndServe(":8080", nil)) }
The function editHandler loads the page (or, if it doesn't exist, create an empty Page struct), and displays an HTML form:
func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[len("/edit/"):] p, err := loadPage(title) if err != nil { p = &Page{Title: title} } fmt.Fprintf(w, "<h1>Editing %s</h1>"+ "<form action=\"/save/%s\" method=\"POST\">"+ "<textarea name=\"body\">%s</textarea><br>"+ "<input type=\"submit\" value=\"Save\">"+ "</form>", p.Title, p.Title, p.Body) }
Though this function will work fine, as we can see the hard-coded HTML looks ugly. So, let's use html/template package instead.
The html/template package is part of the Go standard library. We can use it to keep the HTML in a separate file, allowing us to change the layout of our edit page without modifying the underlying Go code.
First, we must add html/template to the list of imports. We also won't be using fmt anymore, so we have to remove that.
import ( "html/template" "io/ioutil" "net/http" )
Let's create a template file containing the HTML form (edit.html):
<h1>Editing {{.Title}}</h1> <form action="/save/{{.Title}}" method="POST"> <div><textarea name="body" rows="20" cols="80">{{printf "%s" .Body}}</textarea></div> <div><input type="submit" value="Save"></div> </form>
So, we need to modify editHandler to use the template, instead of the hard-coded HTML:
func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[len("/edit/"):] p, err := loadPage(title) if err != nil { p = &Page{Title: title} } t, _ := template.ParseFiles("edit.html") t.Execute(w, p) }
The function template.ParseFiles will read the contents of edit.html and return a *template.Template.
The method t.Execute executes the template, writing the generated HTML to the http.ResponseWriter. The .Title and .Body dotted identifiers refer to p.Title and p.Body.
Template directives are enclosed in double curly braces. The printf "%s" .Body instruction is a function call that outputs .Body as a string instead of a stream of bytes, the same as a call to fmt.Printf.
The html/template package helps guarantee that only safe and correct-looking HTML is generated by template actions. For instance, it automatically escapes any greater than sign (>), replacing it with >, to make sure user data does not corrupt the form HTML.
Since we're working with templates now, let's create a template for our viewHandler called view.html:
<h1>{{.Title}}</h1> <p>[<a href="/edit/{{.Title}}">edit</a>]</p> <div>{{printf "%s" .Body}}</div>
Modify viewHandler accordingly:
func viewHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[len("/view/"):] p, _ := loadPage(title) t, _ := template.ParseFiles("view.html") t.Execute(w, p) }
Notice that we've used almost exactly the same templating code in both handlers. Let's remove this duplication by moving the templating code to its own function:
func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { t, _ := template.ParseFiles(tmpl + ".html") t.Execute(w, p) }
And modify the handlers to use that function:
func viewHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[len("/view/"):] p, _ := loadPage(title) renderTemplate(w, "view", p) } func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[len("/edit/"):] p, err := loadPage(title) if err != nil { p = &Page{Title: title} } renderTemplate(w, "edit", p) }
This file is available: wiki-app-3.go
With view.text like this, while the web server running, a visit to http://localhost:8080/view/test should show a page titled "test" containing the words "Hello GoLang - I am using html/template package!":
Hello GoLang - I am using html/template package!
As we can see from the browser, we now have [Edit] menu.
And "save" button as well:
Well, we commented out the registration of our unimplemented save handler in main, the "save" button is not working yet. If we click the "save", we get:
Continues to Web Application Part 4 (Handling non-existent pages and saving pages).
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