Codelab Writing Web Applications - The Go Programming Language.htm 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027
  1. <!DOCTYPE html>
  2. <!-- saved from url=(0035)http://golang.org/doc/codelab/wiki/ -->
  3. <html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  4. <title>Codelab: Writing Web Applications - The Go Programming Language</title>
  5. <link rel="stylesheet" href="./Codelab Writing Web Applications - The Go Programming Language_files/all.css" type="text/css" media="all" charset="utf-8">
  6. <!--[if lt IE 8]>
  7. <link rel="stylesheet" href="/doc/ie.css" type="text/css">
  8. <![endif]-->
  9. <script type="text/javascript" async="" src="./Codelab Writing Web Applications - The Go Programming Language_files/ga.js"></script><script type="text/javascript" src="./Codelab Writing Web Applications - The Go Programming Language_files/godocs.js"></script>
  10. <script type="text/javascript">
  11. var _gaq = _gaq || [];
  12. _gaq.push(["_setAccount", "UA-11222381-2"]);
  13. _gaq.push(["_trackPageview"]);
  14. </script>
  15. </head>
  16. <body id="top">
  17. <div id="container">
  18. <div id="topnav">
  19. <h1 id="title">The Go Programming Language</h1>
  20. <div id="nav-main">
  21. <ul>
  22. <li><a href="http://golang.org/">Home</a></li><li><a href="http://golang.org/doc/install.html">Getting Started</a></li><li><a href="http://golang.org/doc/docs.html">Documentation</a></li><li><a href="http://golang.org/doc/contrib.html">Contributing</a></li><li><a href="http://golang.org/doc/community.html">Community</a></li>
  23. </ul>
  24. <div class="quickref">
  25. <form method="GET" action="http://golang.org/search">
  26. References:
  27. <a href="http://golang.org/pkg/">Packages</a> <span class="sep">|</span>
  28. <a href="http://golang.org/cmd/">Commands</a> <span class="sep">|</span>
  29. <a href="http://golang.org/doc/go_spec.html">Specification</a>
  30. <input id="search" type="search" name="q" value="" class="inactive" placeholder="code search" results="0">
  31. </form>
  32. </div>
  33. </div>
  34. <a id="logo-box" href="http://golang.org/"></a>
  35. </div>
  36. <div id="content">
  37. <!-- Menu is HTML-escaped elsewhere -->
  38. <h1 id="generatedHeader">Codelab: Writing Web Applications</h1>
  39. <!-- The Table of Contents is automatically inserted in this <div>.
  40. Do not delete this <div>. -->
  41. <div id="nav"><table class="unruled"><tbody><tr><td class="first"><dl><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_13">Introduction</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_23">Getting Started</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_39">Data Structures</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_87">Introducing the http package (an interlude)</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_111">Using http to serve wiki pages</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_139">Editing Pages</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_153">The template package</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_191">Handling non-existent pages</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_199">Saving Pages</a></dt></dl></td><td><dl><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_209">Error handling</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_225">Template caching</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_242">Validation</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_262">Introducing Function Literals and Closures</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_290">Try it out!</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_300">Other tasks</a></dt></dl></td></tr></tbody></table></div>
  42. <!-- Content is HTML-escaped elsewhere -->
  43. <!-- Codelab: Writing Web Applications -->
  44. <h2 id="tmp_13">Introduction<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
  45. <p>
  46. Covered in this codelab:
  47. </p>
  48. <ul>
  49. <li>Creating a data structure with load and save methods</li>
  50. <li>Using the <code>http</code> package to build web applications
  51. </li><li>Using the <code>template</code> package to process HTML templates</li>
  52. <li>Using the <code>regexp</code> package to validate user input</li>
  53. <li>Using closures</li>
  54. </ul>
  55. <p>
  56. Assumed knowledge:
  57. </p>
  58. <ul>
  59. <li>Programming experience</li>
  60. <li>Understanding of basic web technologies (HTTP, HTML)</li>
  61. <li>Some UNIX command-line knowledge</li>
  62. </ul>
  63. <h2 id="tmp_23">Getting Started<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
  64. <p>
  65. At present, you need to have a Linux, OS X, or FreeBSD machine to run Go. If
  66. you don't have access to one, you could set up a Linux Virtual Machine (using
  67. <a href="http://www.virtualbox.org/">VirtualBox</a> or similar) or a
  68. <a href="http://www.google.com/search?q=virtual+private+server">Virtual
  69. Private Server</a>.
  70. </p>
  71. <p>
  72. Install Go (see the <a href="http://golang.org/doc/install.html">Installation Instructions</a>).
  73. </p>
  74. <p>
  75. Make a new directory for this codelab and cd to it:
  76. </p>
  77. <pre>$ mkdir ~/gowiki
  78. $ cd ~/gowiki
  79. </pre>
  80. <p>
  81. Create a file named <code>wiki.go</code>, open it in your favorite editor, and
  82. add the following lines:
  83. </p>
  84. <pre>package main
  85. import (
  86. "fmt"
  87. "io/ioutil"
  88. "os"
  89. )
  90. </pre>
  91. <p>
  92. We import the <code>fmt</code>, <code>ioutil</code> and <code>os</code>
  93. packages from the Go standard library. Later, as we implement additional
  94. functionality, we will add more packages to this <code>import</code>
  95. declaration.
  96. </p>
  97. <h2 id="tmp_39">Data Structures<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
  98. <p>
  99. Let's start by defining the data structures. A wiki consists of a series of
  100. interconnected pages, each of which has a title and a body (the page content).
  101. Here, we define <code>Page</code> as a struct with two fields representing
  102. the title and body.
  103. </p>
  104. <pre>type Page struct {
  105. Title string
  106. Body []byte
  107. }
  108. </pre>
  109. <p>
  110. The type <code>[]byte</code> means "a <code>byte</code> slice".
  111. (See <a href="http://golang.org/doc/effective_go.html#slices">Effective Go</a>
  112. for more on slices.)
  113. The <code>Body</code> element is a <code>[]byte</code> rather than
  114. <code>string</code> because that is the type expected by the <code>io</code>
  115. libraries we will use, as you'll see below.
  116. </p>
  117. <p>
  118. The <code>Page</code> struct describes how page data will be stored in memory.
  119. But what about persistent storage? We can address that by creating a
  120. <code>save</code> method on <code>Page</code>:
  121. </p>
  122. <pre>func (p *Page) save() os.Error {
  123. filename := p.Title + ".txt"
  124. return ioutil.WriteFile(filename, p.Body, 0600)
  125. }
  126. </pre>
  127. <p>
  128. This method's signature reads: "This is a method named <code>save</code> that
  129. takes as its receiver <code>p</code>, a pointer to <code>Page</code> . It takes
  130. no parameters, and returns a value of type <code>os.Error</code>."
  131. </p>
  132. <p>
  133. This method will save the <code>Page</code>'s <code>Body</code> to a text
  134. file. For simplicity, we will use the <code>Title</code> as the file name.
  135. </p>
  136. <p>
  137. The <code>save</code> method returns an <code>os.Error</code> value because
  138. that is the return type of <code>WriteFile</code> (a standard library function
  139. that writes a byte slice to a file). The <code>save</code> method returns the
  140. error value, to let the application handle it should anything go wrong while
  141. writing the file. If all goes well, <code>Page.save()</code> will return
  142. <code>nil</code> (the zero-value for pointers, interfaces, and some other
  143. types).
  144. </p>
  145. <p>
  146. The octal integer constant <code>0600</code>, passed as the third parameter to
  147. <code>WriteFile</code>, indicates that the file should be created with
  148. read-write permissions for the current user only. (See the Unix man page
  149. <code>open(2)</code> for details.)
  150. </p>
  151. <p>
  152. We will want to load pages, too:
  153. </p>
  154. <pre>func loadPage(title string) *Page {
  155. filename := title + ".txt"
  156. body, _ := ioutil.ReadFile(filename)
  157. return &amp;Page{Title: title, Body: body}
  158. }
  159. </pre>
  160. <p>
  161. The function <code>loadPage</code> constructs the file name from
  162. <code>Title</code>, reads the file's contents into a new
  163. <code>Page</code>, and returns a pointer to that new <code>page</code>.
  164. </p>
  165. <p>
  166. Functions can return multiple values. The standard library function
  167. <code>io.ReadFile</code> returns <code>[]byte</code> and <code>os.Error</code>.
  168. In <code>loadPage</code>, error isn't being handled yet; the "blank identifier"
  169. represented by the underscore (<code>_</code>) symbol is used to throw away the
  170. error return value (in essence, assigning the value to nothing).
  171. </p>
  172. <p>
  173. But what happens if <code>ReadFile</code> encounters an error? For example,
  174. the file might not exist. We should not ignore such errors. Let's modify the
  175. function to return <code>*Page</code> and <code>os.Error</code>.
  176. </p>
  177. <pre>func loadPage(title string) (*Page, os.Error) {
  178. filename := title + ".txt"
  179. body, err := ioutil.ReadFile(filename)
  180. if err != nil {
  181. return nil, err
  182. }
  183. return &amp;Page{Title: title, Body: body}, nil
  184. }
  185. </pre>
  186. <p>
  187. Callers of this function can now check the second parameter; if it is
  188. <code>nil</code> then it has successfully loaded a Page. If not, it will be an
  189. <code>os.Error</code> that can be handled by the caller (see the <a href="http://golang.org/pkg/os/#Error">os package documentation</a> for
  190. details).
  191. </p>
  192. <p>
  193. At this point we have a simple data structure and the ability to save to and
  194. load from a file. Let's write a <code>main</code> function to test what we've
  195. written:
  196. </p>
  197. <pre>func main() {
  198. p1 := &amp;Page{Title: "TestPage", Body: []byte("This is a sample Page.")}
  199. p1.save()
  200. p2, _ := loadPage("TestPage")
  201. fmt.Println(string(p2.Body))
  202. }
  203. </pre>
  204. <p>
  205. After compiling and executing this code, a file named <code>TestPage.txt</code>
  206. would be created, containing the contents of <code>p1</code>. The file would
  207. then be read into the struct <code>p2</code>, and its <code>Body</code> element
  208. printed to the screen.
  209. </p>
  210. <p>
  211. You can compile and run the program like this:
  212. </p>
  213. <pre>$ 8g wiki.go
  214. $ 8l wiki.8
  215. $ ./8.out
  216. This is a sample page.
  217. </pre>
  218. <p>
  219. (The <code>8g</code> and <code>8l</code> commands are applicable to
  220. <code>GOARCH=386</code>. If you're on an <code>amd64</code> system,
  221. substitute 6's for the 8's.)
  222. </p>
  223. <p>
  224. <a href="http://golang.org/doc/codelab/wiki/part1.go">Click here to view the code we've written so far.</a>
  225. </p>
  226. <h2 id="tmp_87">Introducing the <code>http</code> package (an interlude)<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
  227. <p>
  228. Here's a full working example of a simple web server:
  229. </p>
  230. <pre>package main
  231. import (
  232. "fmt"
  233. "http"
  234. )
  235. func handler(w http.ResponseWriter, r *http.Request) {
  236. fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
  237. }
  238. func main() {
  239. http.HandleFunc("/", handler)
  240. http.ListenAndServe(":8080", nil)
  241. }
  242. </pre>
  243. <p>
  244. The <code>main</code> function begins with a call to
  245. <code>http.HandleFunc</code>, which tells the <code>http</code> package to
  246. handle all requests to the web root (<code>"/"</code>) with
  247. <code>handler</code>.
  248. </p>
  249. <p>
  250. It then calls <code>http.ListenAndServe</code>, specifying that it should
  251. listen on port 8080 on any interface (<code>":8080"</code>). (Don't
  252. worry about its second parameter, <code>nil</code>, for now.)
  253. This function will block until the program is terminated.
  254. </p>
  255. <p>
  256. The function <code>handler</code> is of the type <code>http.HandlerFunc</code>.
  257. It takes an <code>http.ResponseWriter</code> and an <code>http.Request</code> as
  258. its arguments.
  259. </p>
  260. <p>
  261. An <code>http.ResponseWriter</code> value assembles the HTTP server's response; by writing
  262. to it, we send data to the HTTP client.
  263. </p>
  264. <p>
  265. An <code>http.Request</code> is a data structure that represents the client
  266. HTTP request. The string <code>r.URL.Path</code> is the path component
  267. of the request URL. The trailing <code>[1:]</code> means
  268. "create a sub-slice of <code>Path</code> from the 1st character to the end."
  269. This drops the leading "/" from the path name.
  270. </p>
  271. <p>
  272. If you run this program and access the URL:
  273. </p>
  274. <pre>http://localhost:8080/monkeys</pre>
  275. <p>
  276. the program would present a page containing:
  277. </p>
  278. <pre>Hi there, I love monkeys!</pre>
  279. <h2 id="tmp_111">Using <code>http</code> to serve wiki pages<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
  280. <p>
  281. To use the <code>http</code> package, it must be imported:
  282. </p>
  283. <pre>import (
  284. "fmt"
  285. <b>"http"</b>
  286. "io/ioutil"
  287. "os"
  288. )
  289. </pre>
  290. <p>
  291. Let's create a handler to view a wiki page:
  292. </p>
  293. <pre>const lenPath = len("/view/")
  294. func viewHandler(w http.ResponseWriter, r *http.Request) {
  295. title := r.URL.Path[lenPath:]
  296. p, _ := loadPage(title)
  297. fmt.Fprintf(w, "&lt;h1&gt;%s&lt;/h1&gt;&lt;div&gt;%s&lt;/div&gt;", p.Title, p.Body)
  298. }
  299. </pre>
  300. <p>
  301. First, this function extracts the page title from <code>r.URL.Path</code>,
  302. the path component of the request URL. The global constant
  303. <code>lenPath</code> is the length of the leading <code>"/view/"</code>
  304. component of the request path.
  305. The <code>Path</code> is re-sliced with <code>[lenPath:]</code> to drop the
  306. first 6 characters of the string. This is because the path will invariably
  307. begin with <code>"/view/"</code>, which is not part of the page title.
  308. </p>
  309. <p>
  310. The function then loads the page data, formats the page with a string of simple
  311. HTML, and writes it to <code>w</code>, the <code>http.ResponseWriter</code>.
  312. </p>
  313. <p>
  314. Again, note the use of <code>_</code> to ignore the <code>os.Error</code>
  315. return value from <code>loadPage</code>. This is done here for simplicity
  316. and generally considered bad practice. We will attend to this later.
  317. </p>
  318. <p>
  319. To use this handler, we create a <code>main</code> function that
  320. initializes <code>http</code> using the <code>viewHandler</code> to handle
  321. any requests under the path <code>/view/</code>.
  322. </p>
  323. <pre>func main() {
  324. http.HandleFunc("/view/", viewHandler)
  325. http.ListenAndServe(":8080", nil)
  326. }
  327. </pre>
  328. <p>
  329. <a href="http://golang.org/doc/codelab/wiki/part2.go">Click here to view the code we've written so far.</a>
  330. </p>
  331. <p>
  332. Let's create some page data (as <code>test.txt</code>), compile our code, and
  333. try serving a wiki page:
  334. </p>
  335. <pre>$ echo "Hello world" &gt; test.txt
  336. $ 8g wiki.go
  337. $ 8l wiki.8
  338. $ ./8.out
  339. </pre>
  340. <p>
  341. With this web server running, a visit to <code><a href="http://localhost:8080/view/test">http://localhost:8080/view/test</a></code>
  342. should show a page titled "test" containing the words "Hello world".
  343. </p>
  344. <h2 id="tmp_139">Editing Pages<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
  345. <p>
  346. A wiki is not a wiki without the ability to edit pages. Let's create two new
  347. handlers: one named <code>editHandler</code> to display an 'edit page' form,
  348. and the other named <code>saveHandler</code> to save the data entered via the
  349. form.
  350. </p>
  351. <p>
  352. First, we add them to <code>main()</code>:
  353. </p>
  354. <pre>func main() {
  355. http.HandleFunc("/view/", viewHandler)
  356. http.HandleFunc("/edit/", editHandler)
  357. http.HandleFunc("/save/", saveHandler)
  358. http.ListenAndServe(":8080", nil)
  359. }
  360. </pre>
  361. <p>
  362. The function <code>editHandler</code> loads the page
  363. (or, if it doesn't exist, create an empty <code>Page</code> struct),
  364. and displays an HTML form.
  365. </p>
  366. <pre>func editHandler(w http.ResponseWriter, r *http.Request) {
  367. title := r.URL.Path[lenPath:]
  368. p, err := loadPage(title)
  369. if err != nil {
  370. p = &amp;Page{Title: title}
  371. }
  372. fmt.Fprintf(w, "&lt;h1&gt;Editing %s&lt;/h1&gt;"+
  373. "&lt;form action=\"/save/%s\" method=\"POST\"&gt;"+
  374. "&lt;textarea name=\"body\"&gt;%s&lt;/textarea&gt;&lt;br&gt;"+
  375. "&lt;input type=\"submit\" value=\"Save\"&gt;"+
  376. "&lt;/form&gt;",
  377. p.Title, p.Title, p.Body)
  378. }
  379. </pre>
  380. <p>
  381. This function will work fine, but all that hard-coded HTML is ugly.
  382. Of course, there is a better way.
  383. </p>
  384. <h2 id="tmp_153">The <code>template</code> package<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
  385. <p>
  386. The <code>template</code> package is part of the Go standard library. We can
  387. use <code>template</code> to keep the HTML in a separate file, allowing
  388. us to change the layout of our edit page without modifying the underlying Go
  389. code.
  390. </p>
  391. <p>
  392. First, we must add <code>template</code> to the list of imports:
  393. </p>
  394. <pre>import (
  395. "http"
  396. "io/ioutil"
  397. "os"
  398. <b>"template"</b>
  399. )
  400. </pre>
  401. <p>
  402. Let's create a template file containing the HTML form.
  403. Open a new file named <code>edit.html</code>, and add the following lines:
  404. </p>
  405. <pre>&lt;h1&gt;Editing {Title}&lt;/h1&gt;
  406. &lt;form action="/save/{Title}" method="POST"&gt;
  407. &lt;div&gt;&lt;textarea name="body" rows="20" cols="80"&gt;{Body|html}&lt;/textarea&gt;&lt;/div&gt;
  408. &lt;div&gt;&lt;input type="submit" value="Save"&gt;&lt;/div&gt;
  409. &lt;/form&gt;
  410. </pre>
  411. <p>
  412. Modify <code>editHandler</code> to use the template, instead of the hard-coded
  413. HTML:
  414. </p>
  415. <pre>func editHandler(w http.ResponseWriter, r *http.Request) {
  416. title := r.URL.Path[lenPath:]
  417. p, err := loadPage(title)
  418. if err != nil {
  419. p = &amp;Page{Title: title}
  420. }
  421. t, _ := template.ParseFile("edit.html", nil)
  422. t.Execute(w, p)
  423. }
  424. </pre>
  425. <p>
  426. The function <code>template.ParseFile</code> will read the contents of
  427. <code>edit.html</code> and return a <code>*template.Template</code>.
  428. </p>
  429. <p>
  430. The method <code>t.Execute</code> replaces all occurrences of
  431. <code>{Title}</code> and <code>{Body}</code> with the values of
  432. <code>p.Title</code> and <code>p.Body</code>, and writes the resultant
  433. HTML to the <code>http.ResponseWriter</code>.
  434. </p>
  435. <p>
  436. Note that we've used <code>{Body|html}</code> in the above template.
  437. The <code>|html</code> part asks the template engine to pass the value
  438. <code>Body</code> through the <code>html</code> formatter before outputting it,
  439. which escapes HTML characters (such as replacing <code>&gt;</code> with
  440. <code>&amp;gt;</code>).
  441. This will prevent user data from corrupting the form HTML.
  442. </p>
  443. <p>
  444. Now that we've removed the <code>fmt.Fprintf</code> statement, we can remove
  445. <code>"fmt"</code> from the <code>import</code> list.
  446. </p>
  447. <p>
  448. While we're working with templates, let's create a template for our
  449. <code>viewHandler</code> called <code>view.html</code>:
  450. </p>
  451. <pre>&lt;h1&gt;{Title}&lt;/h1&gt;
  452. &lt;p&gt;[&lt;a href="/edit/{Title}"&gt;edit&lt;/a&gt;]&lt;/p&gt;
  453. &lt;div&gt;{Body}&lt;/div&gt;
  454. </pre>
  455. <p>
  456. Modify <code>viewHandler</code> accordingly:
  457. </p>
  458. <pre>func viewHandler(w http.ResponseWriter, r *http.Request) {
  459. title := r.URL.Path[lenPath:]
  460. p, _ := loadPage(title)
  461. t, _ := template.ParseFile("view.html", nil)
  462. t.Execute(w, p)
  463. }
  464. </pre>
  465. <p>
  466. Notice that we've used almost exactly the same templating code in both
  467. handlers. Let's remove this duplication by moving the templating code
  468. to its own function:
  469. </p>
  470. <pre>func viewHandler(w http.ResponseWriter, r *http.Request) {
  471. title := r.URL.Path[lenPath:]
  472. p, _ := loadPage(title)
  473. renderTemplate(w, "view", p)
  474. }
  475. func editHandler(w http.ResponseWriter, r *http.Request) {
  476. title := r.URL.Path[lenPath:]
  477. p, err := loadPage(title)
  478. if err != nil {
  479. p = &amp;Page{Title: title}
  480. }
  481. renderTemplate(w, "edit", p)
  482. }
  483. func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
  484. t, _ := template.ParseFile(tmpl+".html", nil)
  485. t.Execute(w, p)
  486. }
  487. </pre>
  488. <p>
  489. The handlers are now shorter and simpler.
  490. </p>
  491. <h2 id="tmp_191">Handling non-existent pages<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
  492. <p>
  493. What if you visit <code>/view/APageThatDoesntExist</code>? The program will
  494. crash. This is because it ignores the error return value from
  495. <code>loadPage</code>. Instead, if the requested Page doesn't exist, it should
  496. redirect the client to the edit Page so the content may be created:
  497. </p>
  498. <pre>func viewHandler(w http.ResponseWriter, r *http.Request) {
  499. title, err := getTitle(w, r)
  500. if err != nil {
  501. return
  502. }
  503. p, err := loadPage(title)
  504. if err != nil {
  505. http.Redirect(w, r, "/edit/"+title, http.StatusFound)
  506. return
  507. }
  508. renderTemplate(w, "view", p)
  509. }
  510. </pre>
  511. <p>
  512. The <code>http.Redirect</code> function adds an HTTP status code of
  513. <code>http.StatusFound</code> (302) and a <code>Location</code>
  514. header to the HTTP response.
  515. </p>
  516. <h2 id="tmp_199">Saving Pages<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
  517. <p>
  518. The function <code>saveHandler</code> will handle the form submission.
  519. </p>
  520. <pre>func saveHandler(w http.ResponseWriter, r *http.Request) {
  521. title := r.URL.Path[lenPath:]
  522. body := r.FormValue("body")
  523. p := &amp;Page{Title: title, Body: []byte(body)}
  524. p.save()
  525. http.Redirect(w, r, "/view/"+title, http.StatusFound)
  526. }
  527. </pre>
  528. <p>
  529. The page title (provided in the URL) and the form's only field,
  530. <code>Body</code>, are stored in a new <code>Page</code>.
  531. The <code>save()</code> method is then called to write the data to a file,
  532. and the client is redirected to the <code>/view/</code> page.
  533. </p>
  534. <p>
  535. The value returned by <code>FormValue</code> is of type <code>string</code>.
  536. We must convert that value to <code>[]byte</code> before it will fit into
  537. the <code>Page</code> struct. We use <code>[]byte(body)</code> to perform
  538. the conversion.
  539. </p>
  540. <h2 id="tmp_209">Error handling<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
  541. <p>
  542. There are several places in our program where errors are being ignored. This
  543. is bad practice, not least because when an error does occur the program will
  544. crash. A better solution is to handle the errors and return an error message
  545. to the user. That way if something does go wrong, the server will continue to
  546. function and the user will be notified.
  547. </p>
  548. <p>
  549. First, let's handle the errors in <code>renderTemplate</code>:
  550. </p>
  551. <pre>func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
  552. t, err := template.ParseFile(tmpl+".html", nil)
  553. if err != nil {
  554. http.Error(w, err.String(), http.StatusInternalServerError)
  555. return
  556. }
  557. err = t.Execute(w, p)
  558. if err != nil {
  559. http.Error(w, err.String(), http.StatusInternalServerError)
  560. }
  561. }
  562. </pre>
  563. <p>
  564. The <code>http.Error</code> function sends a specified HTTP response code
  565. (in this case "Internal Server Error") and error message.
  566. Already the decision to put this in a separate function is paying off.
  567. </p>
  568. <p>
  569. Now let's fix up <code>saveHandler</code>:
  570. </p>
  571. <pre>func saveHandler(w http.ResponseWriter, r *http.Request) {
  572. title, err := getTitle(w, r)
  573. if err != nil {
  574. return
  575. }
  576. body := r.FormValue("body")
  577. p := &amp;Page{Title: title, Body: []byte(body)}
  578. err = p.save()
  579. if err != nil {
  580. http.Error(w, err.String(), http.StatusInternalServerError)
  581. return
  582. }
  583. http.Redirect(w, r, "/view/"+title, http.StatusFound)
  584. }
  585. </pre>
  586. <p>
  587. Any errors that occur during <code>p.save()</code> will be reported
  588. to the user.
  589. </p>
  590. <h2 id="tmp_225">Template caching<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
  591. <p>
  592. There is an inefficiency in this code: <code>renderTemplate</code> calls
  593. <code>ParseFile</code> every time a page is rendered.
  594. A better approach would be to call <code>ParseFile</code> once for each
  595. template at program initialization, and store the resultant
  596. <code>*Template</code> values in a data structure for later use.
  597. </p>
  598. <p>
  599. First we create a global map named <code>templates</code> in which to store
  600. our <code>*Template</code> values, keyed by <code>string</code>
  601. (the template name):
  602. </p>
  603. <pre>var templates = make(map[string]*template.Template)
  604. </pre>
  605. <p>
  606. Then we create an <code>init</code> function, which will be called before
  607. <code>main</code> at program initialization. The function
  608. <code>template.MustParseFile</code> is a convenience wrapper around
  609. <code>ParseFile</code> that does not return an error code; instead, it panics
  610. if an error is encountered. A panic is appropriate here; if the templates can't
  611. be loaded the only sensible thing to do is exit the program.
  612. </p>
  613. <pre>func init() {
  614. for _, tmpl := range []string{"edit", "view"} {
  615. templates[tmpl] = template.MustParseFile(tmpl+".html", nil)
  616. }
  617. }
  618. </pre>
  619. <p>
  620. A <code>for</code> loop is used with a <code>range</code> statement to iterate
  621. over an array constant containing the names of the templates we want parsed.
  622. If we were to add more templates to our program, we would add their names to
  623. that array.
  624. </p>
  625. <p>
  626. We then modify our <code>renderTemplate</code> function to call
  627. the <code>Execute</code> method on the appropriate <code>Template</code> from
  628. <code>templates</code>:
  629. </p><pre>func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
  630. err := templates[tmpl].Execute(w, p)
  631. if err != nil {
  632. http.Error(w, err.String(), http.StatusInternalServerError)
  633. }
  634. }
  635. </pre>
  636. <h2 id="tmp_242">Validation<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
  637. <p>
  638. As you may have observed, this program has a serious security flaw: a user
  639. can supply an arbitrary path to be read/written on the server. To mitigate
  640. this, we can write a function to validate the title with a regular expression.
  641. </p>
  642. <p>
  643. First, add <code>"regexp"</code> to the <code>import</code> list.
  644. Then we can create a global variable to store our validation regexp:
  645. </p>
  646. <pre>var titleValidator = regexp.MustCompile("^[a-zA-Z0-9]+$")
  647. </pre>
  648. <p>
  649. The function <code>regexp.MustCompile</code> will parse and compile the
  650. regular expression, and return a <code>regexp.Regexp</code>.
  651. <code>MustCompile</code>, like <code>template.MustParseFile</code>,
  652. is distinct from <code>Compile</code> in that it will panic if
  653. the expression compilation fails, while <code>Compile</code> returns an
  654. <code>os.Error</code> as a second parameter.
  655. </p>
  656. <p>
  657. Now, let's write a function that extracts the title string from the request
  658. URL, and tests it against our <code>TitleValidator</code> expression:
  659. </p>
  660. <pre>func getTitle(w http.ResponseWriter, r *http.Request) (title string, err os.Error) {
  661. title = r.URL.Path[lenPath:]
  662. if !titleValidator.MatchString(title) {
  663. http.NotFound(w, r)
  664. err = os.NewError("Invalid Page Title")
  665. }
  666. return
  667. }
  668. </pre>
  669. <p>
  670. If the title is valid, it will be returned along with a <code>nil</code>
  671. error value. If the title is invalid, the function will write a
  672. "404 Not Found" error to the HTTP connection, and return an error to the
  673. handler.
  674. </p>
  675. <p>
  676. Let's put a call to <code>getTitle</code> in each of the handlers:
  677. </p>
  678. <pre>func viewHandler(w http.ResponseWriter, r *http.Request) {
  679. title, err := getTitle(w, r)
  680. if err != nil {
  681. return
  682. }
  683. p, err := loadPage(title)
  684. if err != nil {
  685. http.Redirect(w, r, "/edit/"+title, http.StatusFound)
  686. return
  687. }
  688. renderTemplate(w, "view", p)
  689. }
  690. func editHandler(w http.ResponseWriter, r *http.Request) {
  691. title, err := getTitle(w, r)
  692. if err != nil {
  693. return
  694. }
  695. p, err := loadPage(title)
  696. if err != nil {
  697. p = &amp;Page{Title: title}
  698. }
  699. renderTemplate(w, "edit", p)
  700. }
  701. func saveHandler(w http.ResponseWriter, r *http.Request) {
  702. title, err := getTitle(w, r)
  703. if err != nil {
  704. return
  705. }
  706. body := r.FormValue("body")
  707. p := &amp;Page{Title: title, Body: []byte(body)}
  708. err = p.save()
  709. if err != nil {
  710. http.Error(w, err.String(), http.StatusInternalServerError)
  711. return
  712. }
  713. http.Redirect(w, r, "/view/"+title, http.StatusFound)
  714. }
  715. </pre>
  716. <h2 id="tmp_262">Introducing Function Literals and Closures<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
  717. <p>
  718. Catching the error condition in each handler introduces a lot of repeated code.
  719. What if we could wrap each of the handlers in a function that does this
  720. validation and error checking? Go's
  721. <a href="http://golang.org/doc/go_spec.html#Function_declarations">function
  722. literals</a> provide a powerful means of abstracting functionality
  723. that can help us here.
  724. </p>
  725. <p>
  726. First, we re-write the function definition of each of the handlers to accept
  727. a title string:
  728. </p>
  729. <pre>func viewHandler(w http.ResponseWriter, r *http.Request, title string)
  730. func editHandler(w http.ResponseWriter, r *http.Request, title string)
  731. func saveHandler(w http.ResponseWriter, r *http.Request, title string)
  732. </pre>
  733. <p>
  734. Now let's define a wrapper function that <i>takes a function of the above
  735. type</i>, and returns a function of type <code>http.HandlerFunc</code>
  736. (suitable to be passed to the function <code>http.HandleFunc</code>):
  737. </p>
  738. <pre>func makeHandler(fn func (http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
  739. return func(w http.ResponseWriter, r *http.Request) {
  740. // Here we will extract the page title from the Request,
  741. // and call the provided handler 'fn'
  742. }
  743. }
  744. </pre>
  745. <p>
  746. The returned function is called a closure because it encloses values defined
  747. outside of it. In this case, the variable <code>fn</code> (the single argument
  748. to <code>makeHandler</code>) is enclosed by the closure. The variable
  749. <code>fn</code> will be one of our save, edit, or view handlers.
  750. </p>
  751. <p>
  752. Now we can take the code from <code>getTitle</code> and use it here
  753. (with some minor modifications):
  754. </p>
  755. <pre>func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
  756. return func(w http.ResponseWriter, r *http.Request) {
  757. title := r.URL.Path[lenPath:]
  758. if !titleValidator.MatchString(title) {
  759. http.NotFound(w, r)
  760. return
  761. }
  762. fn(w, r, title)
  763. }
  764. }
  765. </pre>
  766. <p>
  767. The closure returned by <code>makeHandler</code> is a function that takes
  768. an <code>http.ResponseWriter</code> and <code>http.Request</code> (in other
  769. words, an <code>http.HandlerFunc</code>).
  770. The closure extracts the <code>title</code> from the request path, and
  771. validates it with the <code>TitleValidator</code> regexp. If the
  772. <code>title</code> is invalid, an error will be written to the
  773. <code>ResponseWriter</code> using the <code>http.NotFound</code> function.
  774. If the <code>title</code> is valid, the enclosed handler function
  775. <code>fn</code> will be called with the <code>ResponseWriter</code>,
  776. <code>Request</code>, and <code>title</code> as arguments.
  777. </p>
  778. <p>
  779. Now we can wrap the handler functions with <code>makeHandler</code> in
  780. <code>main</code>, before they are registered with the <code>http</code>
  781. package:
  782. </p>
  783. <pre>func main() {
  784. http.HandleFunc("/view/", makeHandler(viewHandler))
  785. http.HandleFunc("/edit/", makeHandler(editHandler))
  786. http.HandleFunc("/save/", makeHandler(saveHandler))
  787. http.ListenAndServe(":8080", nil)
  788. }
  789. </pre>
  790. <p>
  791. Finally we remove the calls to <code>getTitle</code> from the handler functions,
  792. making them much simpler:
  793. </p>
  794. <pre>func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
  795. p, err := loadPage(title)
  796. if err != nil {
  797. http.Redirect(w, r, "/edit/"+title, http.StatusFound)
  798. return
  799. }
  800. renderTemplate(w, "view", p)
  801. }
  802. func editHandler(w http.ResponseWriter, r *http.Request, title string) {
  803. p, err := loadPage(title)
  804. if err != nil {
  805. p = &amp;Page{Title: title}
  806. }
  807. renderTemplate(w, "edit", p)
  808. }
  809. func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
  810. body := r.FormValue("body")
  811. p := &amp;Page{Title: title, Body: []byte(body)}
  812. err := p.save()
  813. if err != nil {
  814. http.Error(w, err.String(), http.StatusInternalServerError)
  815. return
  816. }
  817. http.Redirect(w, r, "/view/"+title, http.StatusFound)
  818. }
  819. </pre>
  820. <h2 id="tmp_290">Try it out!<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
  821. <p>
  822. <a href="http://golang.org/doc/codelab/wiki/final.go">Click here to view the final code listing.</a>
  823. </p>
  824. <p>
  825. Recompile the code, and run the app:
  826. </p>
  827. <pre>$ 8g wiki.go
  828. $ 8l wiki.8
  829. $ ./8.out
  830. </pre>
  831. <p>
  832. Visiting <a href="http://localhost:8080/view/ANewPage">http://localhost:8080/view/ANewPage</a>
  833. should present you with the page edit form. You should then be able to
  834. enter some text, click 'Save', and be redirected to the newly created page.
  835. </p>
  836. <h2 id="tmp_300">Other tasks<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
  837. <p>
  838. Here are some simple tasks you might want to tackle on your own:
  839. </p>
  840. <ul>
  841. <li>Store templates in <code>tmpl/</code> and page data in <code>data/</code>.
  842. </li><li>Add a handler to make the web root redirect to
  843. <code>/view/FrontPage</code>.</li>
  844. <li>Spruce up the page templates by making them valid HTML and adding some
  845. CSS rules.</li>
  846. <li>Implement inter-page linking by converting instances of
  847. <code>[PageName]</code> to <br>
  848. <code>&lt;a href="/view/PageName"&gt;PageName&lt;/a&gt;</code>.
  849. (hint: you could use <code>regexp.ReplaceAllFunc</code> to do this)
  850. </li>
  851. </ul>
  852. </div>
  853. <div id="site-info">
  854. <p>release.r58.1 8699. Except as noted, this content is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 License</a>.</p>
  855. </div>
  856. </div>
  857. <script type="text/javascript">
  858. (function() {
  859. var ga = document.createElement("script"); ga.type = "text/javascript"; ga.async = true;
  860. ga.src = ("https:" == document.location.protocol ? "https://ssl" : "http://www") + ".google-analytics.com/ga.js";
  861. var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ga, s);
  862. })();
  863. </script>
  864. <!-- generated at Wed Jul 13 14:07:27 EST 2011 -->
  865. </body></html>