Commit dad1228c authored by Jimmy Zelinskie's avatar Jimmy Zelinskie Committed by Andrew Gerrand

doc/articles/wiki: numerous fixes

Fixes #3733
Fixes #2149
Updated Syntax
Added part3.go example program
Added part3-errorhandling.go example program
Improved wording in some places

R=golang-dev, adg, minux.ma
CC=golang-dev
https://golang.org/cl/6636048
parent a3d116cf
...@@ -115,22 +115,24 @@ types). ...@@ -115,22 +115,24 @@ types).
</p> </p>
<p> <p>
The octal integer constant <code>0600</code>, passed as the third parameter to The octal integer literal <code>0600</code>, passed as the third parameter to
<code>WriteFile</code>, indicates that the file should be created with <code>WriteFile</code>, indicates that the file should be created with
read-write permissions for the current user only. (See the Unix man page read-write permissions for the current user only. (See the Unix man page
<code>open(2)</code> for details.) <code>open(2)</code> for details.)
</p> </p>
<p> <p>
We will want to load pages, too: In addition to saving pages, we will want to load pages, too:
</p> </p>
{{code "doc/articles/wiki/part1-noerror.go" `/^func loadPage/` `/^}/`}} {{code "doc/articles/wiki/part1-noerror.go" `/^func loadPage/` `/^}/`}}
<p> <p>
The function <code>loadPage</code> constructs the file name from The function <code>loadPage</code> constructs the file name from
<code>Title</code>, reads the file's contents into a new the title parameter, reads the file's contents into a new
<code>Page</code>, and returns a pointer to that new <code>page</code>. variable <code>body</code>, and returns two values: a pointer to a
<code>Page</code> literal constructed with the proper title and body
values and <code>nil</code> for the error value.
</p> </p>
<p> <p>
...@@ -225,7 +227,7 @@ to it, we send data to the HTTP client. ...@@ -225,7 +227,7 @@ to it, we send data to the HTTP client.
<p> <p>
An <code>http.Request</code> is a data structure that represents the client An <code>http.Request</code> is a data structure that represents the client
HTTP request. The string <code>r.URL.Path</code> is the path component HTTP request. <code>r.URL.Path</code> is the path component
of the request URL. The trailing <code>[1:]</code> means of the request URL. The trailing <code>[1:]</code> means
"create a sub-slice of <code>Path</code> from the 1st character to the end." "create a sub-slice of <code>Path</code> from the 1st character to the end."
This drops the leading "/" from the path name. This drops the leading "/" from the path name.
...@@ -249,13 +251,14 @@ To use the <code>net/http</code> package, it must be imported: ...@@ -249,13 +251,14 @@ To use the <code>net/http</code> package, it must be imported:
<pre> <pre>
import ( import (
"fmt" "fmt"
<b>"net/http"</b>
"io/ioutil" "io/ioutil"
<b>"net/http"</b>
) )
</pre> </pre>
<p> <p>
Let's create a handler to view a wiki page: Let's create a handler, <code>viewHandler</code> that will allow users to
view a wiki page. It will handle URLs prefixed with "/view/".
</p> </p>
{{code "doc/articles/wiki/part2.go" `/^const lenPath/`}} {{code "doc/articles/wiki/part2.go" `/^const lenPath/`}}
...@@ -269,7 +272,7 @@ the path component of the request URL. The global constant ...@@ -269,7 +272,7 @@ the path component of the request URL. The global constant
component of the request path. component of the request path.
The <code>Path</code> is re-sliced with <code>[lenPath:]</code> to drop the The <code>Path</code> is re-sliced with <code>[lenPath:]</code> to drop the
first 6 characters of the string. This is because the path will invariably first 6 characters of the string. This is because the path will invariably
begin with <code>"/view/"</code>, which is not part of the page title. begin with <code>"/view/"</code>, which is not part of the page's title.
</p> </p>
<p> <p>
...@@ -284,8 +287,8 @@ and generally considered bad practice. We will attend to this later. ...@@ -284,8 +287,8 @@ and generally considered bad practice. We will attend to this later.
</p> </p>
<p> <p>
To use this handler, we create a <code>main</code> function that To use this handler, we rewrite our <code>main</code> function to
initializes <code>http</code> using the <code>viewHandler</code> to handle initialize <code>http</code> using the <code>viewHandler</code> to handle
any requests under the path <code>/view/</code>. any requests under the path <code>/view/</code>.
</p> </p>
...@@ -310,6 +313,11 @@ $ go build wiki.go ...@@ -310,6 +313,11 @@ $ go build wiki.go
$ ./wiki $ ./wiki
</pre> </pre>
<p>
(If you're using Windows you must type "<code>wiki</code>" without the
"<code>./</code>" to run the program.)
</p>
<p> <p>
With this web server running, a visit to <code><a With this web server running, a visit to <code><a
href="http://localhost:8080/view/test">http://localhost:8080/view/test</a></code> href="http://localhost:8080/view/test">http://localhost:8080/view/test</a></code>
...@@ -354,15 +362,15 @@ underlying Go code. ...@@ -354,15 +362,15 @@ underlying Go code.
</p> </p>
<p> <p>
First, we must add <code>html/template</code> to the list of imports: First, we must add <code>html/template</code> to the list of imports. We
also won't be using <code>fmt</code> anymore, so we have to remove that.
</p> </p>
<pre> <pre>
import ( import (
<b>"html/template"</b> <b>"html/template"</b>
"http"
"io/ioutil" "io/ioutil"
"os" "net/http"
) )
</pre> </pre>
...@@ -405,12 +413,7 @@ HTML. ...@@ -405,12 +413,7 @@ HTML.
</p> </p>
<p> <p>
Now that we've removed the <code>fmt.Fprintf</code> statement, we can remove Since we're working with templates now, let's create a template for our
<code>"fmt"</code> from the <code>import</code> list.
</p>
<p>
While we're working with templates, let's create a template for our
<code>viewHandler</code> called <code>view.html</code>: <code>viewHandler</code> called <code>view.html</code>:
</p> </p>
...@@ -428,25 +431,28 @@ handlers. Let's remove this duplication by moving the templating code ...@@ -428,25 +431,28 @@ handlers. Let's remove this duplication by moving the templating code
to its own function: to its own function:
</p> </p>
{{code "doc/articles/wiki/final-template.go" `/^func renderTemplate/` `/^}/`}}
{{code "doc/articles/wiki/final-template.go" `/^func viewHandler/` `/^}/`}} {{code "doc/articles/wiki/final-template.go" `/^func viewHandler/` `/^}/`}}
{{code "doc/articles/wiki/final-template.go" `/^func editHandler/` `/^}/`}} {{code "doc/articles/wiki/final-template.go" `/^func editHandler/` `/^}/`}}
{{code "doc/articles/wiki/final-template.go" `/^func renderTemplate/` `/^}/`}}
<p> <p>
The handlers are now shorter and simpler. If we comment out the registration of our unimplemented save handler in
<code>main</code>, we can once again build and test our program.
<a href="part3.go">Click here to view the code we've written so far.</a>
</p> </p>
<h2>Handling non-existent pages</h2> <h2>Handling non-existent pages</h2>
<p> <p>
What if you visit <a href="http://localhost:8080/view/APageThatDoesntExist"> What if you visit <a href="http://localhost:8080/view/APageThatDoesntExist">
<code>/view/APageThatDoesntExist</code></a>? The program will crash. This is <code>/view/APageThatDoesntExist</code></a>? You'll see a page containing
because it ignores the error return value from <code>loadPage</code>. Instead, HTML. This is because it ignores the error return value from
if the requested Page doesn't exist, it should redirect the client to the edit <code>loadPage</code> and continues to try and fill out the template
Page so the content may be created: with no data. Instead, if the requested Page doesn't exist, it should
redirect the client to the edit Page so the content may be created:
</p> </p>
{{code "doc/articles/wiki/final-noclosure.go" `/^func viewHandler/` `/^}/`}} {{code "doc/articles/wiki/part3-errorhandling.go" `/^func viewHandler/` `/^}/`}}
<p> <p>
The <code>http.Redirect</code> function adds an HTTP status code of The <code>http.Redirect</code> function adds an HTTP status code of
...@@ -457,7 +463,9 @@ header to the HTTP response. ...@@ -457,7 +463,9 @@ header to the HTTP response.
<h2>Saving Pages</h2> <h2>Saving Pages</h2>
<p> <p>
The function <code>saveHandler</code> will handle the form submission. The function <code>saveHandler</code> will handle the submission of forms
located on the edit pages. After uncommenting the related line in
<code>main</code>, let's implement the the handler:
</p> </p>
{{code "doc/articles/wiki/final-template.go" `/^func saveHandler/` `/^}/`}} {{code "doc/articles/wiki/final-template.go" `/^func saveHandler/` `/^}/`}}
...@@ -481,9 +489,9 @@ the conversion. ...@@ -481,9 +489,9 @@ the conversion.
<p> <p>
There are several places in our program where errors are being ignored. This There are several places in our program where errors are being ignored. This
is bad practice, not least because when an error does occur the program will is bad practice, not least because when an error does occur the program will
crash. A better solution is to handle the errors and return an error message have unintended behavior. A better solution is to handle the errors and return
to the user. That way if something does go wrong, the server will continue to an error message to the user. That way if something does go wrong, the server
function and the user will be notified. will function exactly how we want and the user can be notified.
</p> </p>
<p> <p>
...@@ -502,7 +510,7 @@ Already the decision to put this in a separate function is paying off. ...@@ -502,7 +510,7 @@ Already the decision to put this in a separate function is paying off.
Now let's fix up <code>saveHandler</code>: Now let's fix up <code>saveHandler</code>:
</p> </p>
{{code "doc/articles/wiki/final-noclosure.go" `/^func saveHandler/` `/^}/`}} {{code "doc/articles/wiki/part3-errorhandling.go" `/^func saveHandler/` `/^}/`}}
<p> <p>
Any errors that occur during <code>p.save()</code> will be reported Any errors that occur during <code>p.save()</code> will be reported
...@@ -536,10 +544,10 @@ can't be loaded the only sensible thing to do is exit the program. ...@@ -536,10 +544,10 @@ can't be loaded the only sensible thing to do is exit the program.
</p> </p>
<p> <p>
A <code>for</code> loop is used with a <code>range</code> statement to iterate A <code>for</code> loop is used with a <code>range</code> statement
over an array constant containing the names of the templates we want parsed. to iterate over an array constant containing the names of the templates we want
If we were to add more templates to our program, we would add their names to parsed. If we were to add more templates to our program, we would add their
that array. names to that array.
</p> </p>
<p> <p>
...@@ -579,8 +587,9 @@ an <code>error</code> as a second parameter. ...@@ -579,8 +587,9 @@ an <code>error</code> as a second parameter.
</p> </p>
<p> <p>
Now, let's write a function that extracts the title string from the request Now, let's write a function, <code>getTitle</code>, that extracts the title
URL, and tests it against our <code>TitleValidator</code> expression: string from the request URL, and tests it against our
<code>TitleValidator</code> expression:
</p> </p>
{{code "doc/articles/wiki/final-noclosure.go" `/func getTitle/` `/^}/`}} {{code "doc/articles/wiki/final-noclosure.go" `/func getTitle/` `/^}/`}}
...@@ -589,7 +598,8 @@ URL, and tests it against our <code>TitleValidator</code> expression: ...@@ -589,7 +598,8 @@ URL, and tests it against our <code>TitleValidator</code> expression:
If the title is valid, it will be returned along with a <code>nil</code> If the title is valid, it will be returned along with a <code>nil</code>
error value. If the title is invalid, the function will write a error value. If the title is invalid, the function will write a
"404 Not Found" error to the HTTP connection, and return an error to the "404 Not Found" error to the HTTP connection, and return an error to the
handler. handler. To create a new error, we have to import the <code>errors</code>
package.
</p> </p>
<p> <p>
......
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"html/template"
"io/ioutil"
"net/http"
)
type Page struct {
Title string
Body []byte
}
func (p *Page) save() error {
filename := p.Title + ".txt"
return ioutil.WriteFile(filename, p.Body, 0600)
}
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
}
const lenPath = len("/view/")
func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
t, _ := template.ParseFiles(tmpl + ".html")
t.Execute(w, p)
}
func viewHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:]
p, err := loadPage(title)
if err != nil {
http.Redirect(w, r, "/edit/"+title, http.StatusFound)
return
}
renderTemplate(w, "view", p)
}
func editHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:]
p, err := loadPage(title)
if err != nil {
p = &Page{Title: title}
}
renderTemplate(w, "edit", p)
}
func saveHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:]
body := r.FormValue("body")
p := &Page{Title: title, Body: []byte(body)}
err := p.save()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/view/"+title, http.StatusFound)
}
func main() {
http.HandleFunc("/view/", viewHandler)
http.HandleFunc("/edit/", editHandler)
http.HandleFunc("/save/", saveHandler)
http.ListenAndServe(":8080", nil)
}
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"html/template"
"io/ioutil"
"net/http"
)
type Page struct {
Title string
Body []byte
}
func (p *Page) save() error {
filename := p.Title + ".txt"
return ioutil.WriteFile(filename, p.Body, 0600)
}
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
}
const lenPath = len("/view/")
func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
t, _ := template.ParseFiles(tmpl + ".html")
t.Execute(w, p)
}
func viewHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:]
p, _ := loadPage(title)
renderTemplate(w, "view", p)
}
func editHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:]
p, err := loadPage(title)
if err != nil {
p = &Page{Title: title}
}
renderTemplate(w, "edit", p)
}
func main() {
http.HandleFunc("/view/", viewHandler)
http.HandleFunc("/edit/", editHandler)
//http.HandleFunc("/save/", saveHandler)
http.ListenAndServe(":8080", nil)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment