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
...@@ -46,7 +46,7 @@ $ cd gowiki ...@@ -46,7 +46,7 @@ $ cd gowiki
</pre> </pre>
<p> <p>
Create a file named <code>wiki.go</code>, open it in your favorite editor, and Create a file named <code>wiki.go</code>, open it in your favorite editor, and
add the following lines: add the following lines:
</p> </p>
...@@ -60,8 +60,8 @@ import ( ...@@ -60,8 +60,8 @@ import (
</pre> </pre>
<p> <p>
We import the <code>fmt</code> and <code>ioutil</code> packages from the Go We import the <code>fmt</code> and <code>ioutil</code> packages from the Go
standard library. Later, as we implement additional functionality, we will standard library. Later, as we implement additional functionality, we will
add more packages to this <code>import</code> declaration. add more packages to this <code>import</code> declaration.
</p> </p>
...@@ -77,7 +77,7 @@ the title and body. ...@@ -77,7 +77,7 @@ the title and body.
{{code "doc/articles/wiki/part1.go" `/^type Page/` `/}/`}} {{code "doc/articles/wiki/part1.go" `/^type Page/` `/}/`}}
<p> <p>
The type <code>[]byte</code> means "a <code>byte</code> slice". The type <code>[]byte</code> means "a <code>byte</code> slice".
(See <a href="/doc/articles/slices_usage_and_internals.html">Slices: usage and (See <a href="/doc/articles/slices_usage_and_internals.html">Slices: usage and
internals</a> for more on slices.) internals</a> for more on slices.)
The <code>Body</code> element is a <code>[]byte</code> rather than The <code>Body</code> element is a <code>[]byte</code> rather than
...@@ -86,8 +86,8 @@ libraries we will use, as you'll see below. ...@@ -86,8 +86,8 @@ libraries we will use, as you'll see below.
</p> </p>
<p> <p>
The <code>Page</code> struct describes how page data will be stored in memory. The <code>Page</code> struct describes how page data will be stored in memory.
But what about persistent storage? We can address that by creating a But what about persistent storage? We can address that by creating a
<code>save</code> method on <code>Page</code>: <code>save</code> method on <code>Page</code>:
</p> </p>
...@@ -96,11 +96,11 @@ But what about persistent storage? We can address that by creating a ...@@ -96,11 +96,11 @@ But what about persistent storage? We can address that by creating a
<p> <p>
This method's signature reads: "This is a method named <code>save</code> that This method's signature reads: "This is a method named <code>save</code> that
takes as its receiver <code>p</code>, a pointer to <code>Page</code> . It takes takes as its receiver <code>p</code>, a pointer to <code>Page</code> . It takes
no parameters, and returns a value of type <code>error</code>." no parameters, and returns a value of type <code>error</code>."
</p> </p>
<p> <p>
This method will save the <code>Page</code>'s <code>Body</code> to a text This method will save the <code>Page</code>'s <code>Body</code> to a text
file. For simplicity, we will use the <code>Title</code> as the file name. file. For simplicity, we will use the <code>Title</code> as the file name.
</p> </p>
...@@ -110,35 +110,37 @@ that is the return type of <code>WriteFile</code> (a standard library function ...@@ -110,35 +110,37 @@ that is the return type of <code>WriteFile</code> (a standard library function
that writes a byte slice to a file). The <code>save</code> method returns the that writes a byte slice to a file). The <code>save</code> method returns the
error value, to let the application handle it should anything go wrong while error value, to let the application handle it should anything go wrong while
writing the file. If all goes well, <code>Page.save()</code> will return writing the file. If all goes well, <code>Page.save()</code> will return
<code>nil</code> (the zero-value for pointers, interfaces, and some other <code>nil</code> (the zero-value for pointers, interfaces, and some other
types). 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>
Functions can return multiple values. The standard library function Functions can return multiple values. The standard library function
<code>io.ReadFile</code> returns <code>[]byte</code> and <code>error</code>. <code>io.ReadFile</code> returns <code>[]byte</code> and <code>error</code>.
In <code>loadPage</code>, error isn't being handled yet; the "blank identifier" In <code>loadPage</code>, error isn't being handled yet; the "blank identifier"
represented by the underscore (<code>_</code>) symbol is used to throw away the represented by the underscore (<code>_</code>) symbol is used to throw away the
error return value (in essence, assigning the value to nothing). error return value (in essence, assigning the value to nothing).
</p> </p>
<p> <p>
...@@ -152,7 +154,7 @@ function to return <code>*Page</code> and <code>error</code>. ...@@ -152,7 +154,7 @@ function to return <code>*Page</code> and <code>error</code>.
<p> <p>
Callers of this function can now check the second parameter; if it is Callers of this function can now check the second parameter; if it is
<code>nil</code> then it has successfully loaded a Page. If not, it will be an <code>nil</code> then it has successfully loaded a Page. If not, it will be an
<code>error</code> that can be handled by the caller (see the <code>error</code> that can be handled by the caller (see the
<a href="/ref/spec#Errors">language specification</a> for details). <a href="/ref/spec#Errors">language specification</a> for details).
</p> </p>
...@@ -172,7 +174,7 @@ printed to the screen. ...@@ -172,7 +174,7 @@ printed to the screen.
</p> </p>
<p> <p>
You can compile and run the program like this: You can compile and run the program like this:
</p> </p>
<pre> <pre>
...@@ -182,7 +184,7 @@ This is a sample page. ...@@ -182,7 +184,7 @@ This is a sample page.
</pre> </pre>
<p> <p>
(If you're using Windows you must type "<code>wiki</code>" without the (If you're using Windows you must type "<code>wiki</code>" without the
"<code>./</code>" to run the program.) "<code>./</code>" to run the program.)
</p> </p>
...@@ -199,10 +201,10 @@ Here's a full working example of a simple web server: ...@@ -199,10 +201,10 @@ Here's a full working example of a simple web server:
{{code "doc/articles/wiki/http-sample.go"}} {{code "doc/articles/wiki/http-sample.go"}}
<p> <p>
The <code>main</code> function begins with a call to The <code>main</code> function begins with a call to
<code>http.HandleFunc</code>, which tells the <code>http</code> package to <code>http.HandleFunc</code>, which tells the <code>http</code> package to
handle all requests to the web root (<code>"/"</code>) with handle all requests to the web root (<code>"/"</code>) with
<code>handler</code>. <code>handler</code>.
</p> </p>
<p> <p>
...@@ -219,20 +221,20 @@ its arguments. ...@@ -219,20 +221,20 @@ its arguments.
</p> </p>
<p> <p>
An <code>http.ResponseWriter</code> value assembles the HTTP server's response; by writing An <code>http.ResponseWriter</code> value assembles the HTTP server's response; by writing
to it, we send data to the HTTP client. to it, we send data to the HTTP client.
</p> </p>
<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.
</p> </p>
<p> <p>
If you run this program and access the URL: If you run this program and access the URL:
</p> </p>
<pre>http://localhost:8080/monkeys</pre> <pre>http://localhost:8080/monkeys</pre>
<p> <p>
...@@ -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/`}}
...@@ -264,28 +267,28 @@ Let's create a handler to view a wiki page: ...@@ -264,28 +267,28 @@ Let's create a handler to view a wiki page:
<p> <p>
First, this function extracts the page title from <code>r.URL.Path</code>, First, this function extracts the page title from <code>r.URL.Path</code>,
the path component of the request URL. The global constant the path component of the request URL. The global constant
<code>lenPath</code> is the length of the leading <code>"/view/"</code> <code>lenPath</code> is the length of the leading <code>"/view/"</code>
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>
The function then loads the page data, formats the page with a string of simple The function then loads the page data, formats the page with a string of simple
HTML, and writes it to <code>w</code>, the <code>http.ResponseWriter</code>. HTML, and writes it to <code>w</code>, the <code>http.ResponseWriter</code>.
</p> </p>
<p> <p>
Again, note the use of <code>_</code> to ignore the <code>error</code> Again, note the use of <code>_</code> to ignore the <code>error</code>
return value from <code>loadPage</code>. This is done here for simplicity return value from <code>loadPage</code>. This is done here for simplicity
and generally considered bad practice. We will attend to this later. 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>
...@@ -326,14 +334,14 @@ form. ...@@ -326,14 +334,14 @@ form.
</p> </p>
<p> <p>
First, we add them to <code>main()</code>: First, we add them to <code>main()</code>:
</p> </p>
{{code "doc/articles/wiki/final-noclosure.go" `/^func main/` `/^}/`}} {{code "doc/articles/wiki/final-noclosure.go" `/^func main/` `/^}/`}}
<p> <p>
The function <code>editHandler</code> loads the page The function <code>editHandler</code> loads the page
(or, if it doesn't exist, create an empty <code>Page</code> struct), (or, if it doesn't exist, create an empty <code>Page</code> struct),
and displays an HTML form. and displays an HTML form.
</p> </p>
...@@ -343,7 +351,7 @@ and displays an HTML form. ...@@ -343,7 +351,7 @@ and displays an HTML form.
This function will work fine, but all that hard-coded HTML is ugly. This function will work fine, but all that hard-coded HTML is ugly.
Of course, there is a better way. Of course, there is a better way.
</p> </p>
<h2>The <code>html/template</code> package</h2> <h2>The <code>html/template</code> package</h2>
<p> <p>
...@@ -354,20 +362,20 @@ underlying Go code. ...@@ -354,20 +362,20 @@ 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>
<p> <p>
Let's create a template file containing the HTML form. Let's create a template file containing the HTML form.
Open a new file named <code>edit.html</code>, and add the following lines: Open a new file named <code>edit.html</code>, and add the following lines:
</p> </p>
...@@ -381,8 +389,8 @@ HTML: ...@@ -381,8 +389,8 @@ HTML:
{{code "doc/articles/wiki/final-noerror.go" `/^func editHandler/` `/^}/`}} {{code "doc/articles/wiki/final-noerror.go" `/^func editHandler/` `/^}/`}}
<p> <p>
The function <code>template.ParseFiles</code> will read the contents of The function <code>template.ParseFiles</code> will read the contents of
<code>edit.html</code> and return a <code>*template.Template</code>. <code>edit.html</code> and return a <code>*template.Template</code>.
</p> </p>
<p> <p>
...@@ -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,28 +431,31 @@ handlers. Let's remove this duplication by moving the templating code ...@@ -428,28 +431,31 @@ 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
<code>http.StatusFound</code> (302) and a <code>Location</code> <code>http.StatusFound</code> (302) and a <code>Location</code>
header to the HTTP response. header to the HTTP response.
</p> </p>
...@@ -457,22 +463,24 @@ header to the HTTP response. ...@@ -457,22 +463,24 @@ 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/` `/^}/`}}
<p> <p>
The page title (provided in the URL) and the form's only field, The page title (provided in the URL) and the form's only field,
<code>Body</code>, are stored in a new <code>Page</code>. <code>Body</code>, are stored in a new <code>Page</code>.
The <code>save()</code> method is then called to write the data to a file, The <code>save()</code> method is then called to write the data to a file,
and the client is redirected to the <code>/view/</code> page. and the client is redirected to the <code>/view/</code> page.
</p> </p>
<p> <p>
The value returned by <code>FormValue</code> is of type <code>string</code>. The value returned by <code>FormValue</code> is of type <code>string</code>.
We must convert that value to <code>[]byte</code> before it will fit into We must convert that value to <code>[]byte</code> before it will fit into
the <code>Page</code> struct. We use <code>[]byte(body)</code> to perform the <code>Page</code> struct. We use <code>[]byte(body)</code> to perform
the conversion. the conversion.
</p> </p>
...@@ -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>
...@@ -493,7 +501,7 @@ First, let's handle the errors in <code>renderTemplate</code>: ...@@ -493,7 +501,7 @@ First, let's handle the errors in <code>renderTemplate</code>:
{{code "doc/articles/wiki/final-parsetemplate.go" `/^func renderTemplate/` `/^}/`}} {{code "doc/articles/wiki/final-parsetemplate.go" `/^func renderTemplate/` `/^}/`}}
<p> <p>
The <code>http.Error</code> function sends a specified HTTP response code The <code>http.Error</code> function sends a specified HTTP response code
(in this case "Internal Server Error") and error message. (in this case "Internal Server Error") and error message.
Already the decision to put this in a separate function is paying off. Already the decision to put this in a separate function is paying off.
</p> </p>
...@@ -502,18 +510,18 @@ Already the decision to put this in a separate function is paying off. ...@@ -502,18 +510,18 @@ 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
to the user. to the user.
</p> </p>
<h2>Template caching</h2> <h2>Template caching</h2>
<p> <p>
There is an inefficiency in this code: <code>renderTemplate</code> calls There is an inefficiency in this code: <code>renderTemplate</code> calls
<code>ParseFiles</code> every time a page is rendered. <code>ParseFiles</code> every time a page is rendered.
A better approach would be to call <code>ParseFiles</code> once at program A better approach would be to call <code>ParseFiles</code> once at program
initialization, parsing all templates into a single <code>*Template</code>. initialization, parsing all templates into a single <code>*Template</code>.
Then we can use the Then we can use the
...@@ -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>
...@@ -571,25 +579,27 @@ Then we can create a global variable to store our validation regexp: ...@@ -571,25 +579,27 @@ Then we can create a global variable to store our validation regexp:
{{code "doc/articles/wiki/final-noclosure.go" `/^var titleValidator/`}} {{code "doc/articles/wiki/final-noclosure.go" `/^var titleValidator/`}}
<p> <p>
The function <code>regexp.MustCompile</code> will parse and compile the The function <code>regexp.MustCompile</code> will parse and compile the
regular expression, and return a <code>regexp.Regexp</code>. regular expression, and return a <code>regexp.Regexp</code>.
<code>MustCompile</code> is distinct from <code>Compile</code> in that it will <code>MustCompile</code> is distinct from <code>Compile</code> in that it will
panic if the expression compilation fails, while <code>Compile</code> returns panic if the expression compilation fails, while <code>Compile</code> returns
an <code>error</code> as a second parameter. 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/` `/^}/`}}
<p> <p>
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>
...@@ -604,10 +614,10 @@ Let's put a call to <code>getTitle</code> in each of the handlers: ...@@ -604,10 +614,10 @@ Let's put a call to <code>getTitle</code> in each of the handlers:
<p> <p>
Catching the error condition in each handler introduces a lot of repeated code. Catching the error condition in each handler introduces a lot of repeated code.
What if we could wrap each of the handlers in a function that does this What if we could wrap each of the handlers in a function that does this
validation and error checking? Go's validation and error checking? Go's
<a href="/ref/spec#Function_declarations">function <a href="/ref/spec#Function_declarations">function
literals</a> provide a powerful means of abstracting functionality literals</a> provide a powerful means of abstracting functionality
that can help us here. that can help us here.
</p> </p>
...@@ -654,19 +664,19 @@ Now we can take the code from <code>getTitle</code> and use it here ...@@ -654,19 +664,19 @@ Now we can take the code from <code>getTitle</code> and use it here
<p> <p>
The closure returned by <code>makeHandler</code> is a function that takes The closure returned by <code>makeHandler</code> is a function that takes
an <code>http.ResponseWriter</code> and <code>http.Request</code> (in other an <code>http.ResponseWriter</code> and <code>http.Request</code> (in other
words, an <code>http.HandlerFunc</code>). words, an <code>http.HandlerFunc</code>).
The closure extracts the <code>title</code> from the request path, and The closure extracts the <code>title</code> from the request path, and
validates it with the <code>TitleValidator</code> regexp. If the validates it with the <code>TitleValidator</code> regexp. If the
<code>title</code> is invalid, an error will be written to the <code>title</code> is invalid, an error will be written to the
<code>ResponseWriter</code> using the <code>http.NotFound</code> function. <code>ResponseWriter</code> using the <code>http.NotFound</code> function.
If the <code>title</code> is valid, the enclosed handler function If the <code>title</code> is valid, the enclosed handler function
<code>fn</code> will be called with the <code>ResponseWriter</code>, <code>fn</code> will be called with the <code>ResponseWriter</code>,
<code>Request</code>, and <code>title</code> as arguments. <code>Request</code>, and <code>title</code> as arguments.
</p> </p>
<p> <p>
Now we can wrap the handler functions with <code>makeHandler</code> in Now we can wrap the handler functions with <code>makeHandler</code> in
<code>main</code>, before they are registered with the <code>http</code> <code>main</code>, before they are registered with the <code>http</code>
package: package:
</p> </p>
...@@ -698,7 +708,7 @@ $ ./wiki ...@@ -698,7 +708,7 @@ $ ./wiki
<p> <p>
Visiting <a href="http://localhost:8080/view/ANewPage">http://localhost:8080/view/ANewPage</a> Visiting <a href="http://localhost:8080/view/ANewPage">http://localhost:8080/view/ANewPage</a>
should present you with the page edit form. You should then be able to should present you with the page edit form. You should then be able to
enter some text, click 'Save', and be redirected to the newly created page. enter some text, click 'Save', and be redirected to the newly created page.
</p> </p>
...@@ -710,11 +720,11 @@ Here are some simple tasks you might want to tackle on your own: ...@@ -710,11 +720,11 @@ Here are some simple tasks you might want to tackle on your own:
<ul> <ul>
<li>Store templates in <code>tmpl/</code> and page data in <code>data/</code>. <li>Store templates in <code>tmpl/</code> and page data in <code>data/</code>.
<li>Add a handler to make the web root redirect to <li>Add a handler to make the web root redirect to
<code>/view/FrontPage</code>.</li> <code>/view/FrontPage</code>.</li>
<li>Spruce up the page templates by making them valid HTML and adding some <li>Spruce up the page templates by making them valid HTML and adding some
CSS rules.</li> CSS rules.</li>
<li>Implement inter-page linking by converting instances of <li>Implement inter-page linking by converting instances of
<code>[PageName]</code> to <br> <code>[PageName]</code> to <br>
<code>&lt;a href="/view/PageName"&gt;PageName&lt;/a&gt;</code>. <code>&lt;a href="/view/PageName"&gt;PageName&lt;/a&gt;</code>.
(hint: you could use <code>regexp.ReplaceAllFunc</code> to do this) (hint: you could use <code>regexp.ReplaceAllFunc</code> to do this)
......
// 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