LEFT | RIGHT |
(no file at all) | |
1 <!-- Codelab: Writing Web Applications --> | 1 <!-- Codelab: Writing Web Applications --> |
2 <h2>Introduction</h2> | 2 <h2>Introduction</h2> |
3 | 3 |
4 <p> | 4 <p> |
5 Covered in this codelab: | 5 Covered in this codelab: |
6 </p> | 6 </p> |
7 <ul> | 7 <ul> |
8 <li>Creating a data structure with load and save methods</li> | 8 <li>Creating a data structure with load and save methods</li> |
9 <li>Using the <code>http</code> package to build web applications | 9 <li>Using the <code>http</code> package to build web applications |
10 <li>Using the <code>template</code> package to process HTML templates</li> | 10 <li>Using the <code>template</code> package to process HTML templates</li> |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
91 libraries we will use, as you'll see below. | 91 libraries we will use, as you'll see below. |
92 </p> | 92 </p> |
93 | 93 |
94 <p> | 94 <p> |
95 The <code>Page</code> struct describes how page data will be stored in memory.· | 95 The <code>Page</code> struct describes how page data will be stored in memory.· |
96 But what about persistent storage? We can address that by creating a· | 96 But what about persistent storage? We can address that by creating a· |
97 <code>save</code> method on <code>Page</code>: | 97 <code>save</code> method on <code>Page</code>: |
98 </p> | 98 </p> |
99 | 99 |
100 <pre> | 100 <pre> |
101 func (p *Page) save() os.Error { | 101 func (p *Page) save() error { |
102 filename := p.Title + ".txt" | 102 filename := p.Title + ".txt" |
103 return ioutil.WriteFile(filename, p.Body, 0600) | 103 return ioutil.WriteFile(filename, p.Body, 0600) |
104 } | 104 } |
105 </pre> | 105 </pre> |
106 | 106 |
107 <p> | 107 <p> |
108 This method's signature reads: "This is a method named <code>save</code> that | 108 This method's signature reads: "This is a method named <code>save</code> that |
109 takes as its receiver <code>p</code>, a pointer to <code>Page</code> . It takes | 109 takes as its receiver <code>p</code>, a pointer to <code>Page</code> . It takes |
110 no parameters, and returns a value of type <code>os.Error</code>."· | 110 no parameters, and returns a value of type <code>os.Error</code>."· |
111 </p> | 111 </p> |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
158 error return value (in essence, assigning the value to nothing).· | 158 error return value (in essence, assigning the value to nothing).· |
159 </p> | 159 </p> |
160 | 160 |
161 <p> | 161 <p> |
162 But what happens if <code>ReadFile</code> encounters an error? For example, | 162 But what happens if <code>ReadFile</code> encounters an error? For example, |
163 the file might not exist. We should not ignore such errors. Let's modify the | 163 the file might not exist. We should not ignore such errors. Let's modify the |
164 function to return <code>*Page</code> and <code>os.Error</code>. | 164 function to return <code>*Page</code> and <code>os.Error</code>. |
165 </p> | 165 </p> |
166 | 166 |
167 <pre> | 167 <pre> |
168 func loadPage(title string) (*Page, os.Error) { | 168 func loadPage(title string) (*Page, error) { |
169 filename := title + ".txt" | 169 filename := title + ".txt" |
170 body, err := ioutil.ReadFile(filename) | 170 body, err := ioutil.ReadFile(filename) |
171 if err != nil { | 171 if err != nil { |
172 return nil, err | 172 return nil, err |
173 } | 173 } |
174 return &Page{Title: title, Body: body}, nil | 174 return &Page{Title: title, Body: body}, nil |
175 } | 175 } |
176 </pre> | 176 </pre> |
177 | 177 |
178 <p> | 178 <p> |
(...skipping 459 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
638 </p> | 638 </p> |
639 | 639 |
640 <p> | 640 <p> |
641 First, let's handle the errors in <code>renderTemplate</code>: | 641 First, let's handle the errors in <code>renderTemplate</code>: |
642 </p> | 642 </p> |
643 | 643 |
644 <pre> | 644 <pre> |
645 func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { | 645 func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { |
646 t, err := template.ParseFile(tmpl+".html", nil) | 646 t, err := template.ParseFile(tmpl+".html", nil) |
647 if err != nil { | 647 if err != nil { |
648 » » http.Error(w, err.String(), http.StatusInternalServerError) | 648 » » http.Error(w, err.Error(), http.StatusInternalServerError) |
649 return | 649 return |
650 } | 650 } |
651 err = t.Execute(w, p) | 651 err = t.Execute(w, p) |
652 if err != nil { | 652 if err != nil { |
653 » » http.Error(w, err.String(), http.StatusInternalServerError) | 653 » » http.Error(w, err.Error(), http.StatusInternalServerError) |
654 } | 654 } |
655 } | 655 } |
656 </pre> | 656 </pre> |
657 | 657 |
658 <p> | 658 <p> |
659 The <code>http.Error</code> function sends a specified HTTP response code· | 659 The <code>http.Error</code> function sends a specified HTTP response code· |
660 (in this case "Internal Server Error") and error message. | 660 (in this case "Internal Server Error") and error message. |
661 Already the decision to put this in a separate function is paying off. | 661 Already the decision to put this in a separate function is paying off. |
662 </p> | 662 </p> |
663 | 663 |
664 <p> | 664 <p> |
665 Now let's fix up <code>saveHandler</code>: | 665 Now let's fix up <code>saveHandler</code>: |
666 </p> | 666 </p> |
667 | 667 |
668 <pre> | 668 <pre> |
669 func saveHandler(w http.ResponseWriter, r *http.Request) { | 669 func saveHandler(w http.ResponseWriter, r *http.Request) { |
670 title, err := getTitle(w, r) | 670 title, err := getTitle(w, r) |
671 if err != nil { | 671 if err != nil { |
672 return | 672 return |
673 } | 673 } |
674 body := r.FormValue("body") | 674 body := r.FormValue("body") |
675 p := &Page{Title: title, Body: []byte(body)} | 675 p := &Page{Title: title, Body: []byte(body)} |
676 err = p.save() | 676 err = p.save() |
677 if err != nil { | 677 if err != nil { |
678 » » http.Error(w, err.String(), http.StatusInternalServerError) | 678 » » http.Error(w, err.Error(), http.StatusInternalServerError) |
679 return | 679 return |
680 } | 680 } |
681 http.Redirect(w, r, "/view/"+title, http.StatusFound) | 681 http.Redirect(w, r, "/view/"+title, http.StatusFound) |
682 } | 682 } |
683 </pre> | 683 </pre> |
684 | 684 |
685 <p> | 685 <p> |
686 Any errors that occur during <code>p.save()</code> will be reported· | 686 Any errors that occur during <code>p.save()</code> will be reported· |
687 to the user. | 687 to the user. |
688 </p> | 688 </p> |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
734 | 734 |
735 <p> | 735 <p> |
736 We then modify our <code>renderTemplate</code> function to call· | 736 We then modify our <code>renderTemplate</code> function to call· |
737 the <code>Execute</code> method on the appropriate <code>Template</code> from· | 737 the <code>Execute</code> method on the appropriate <code>Template</code> from· |
738 <code>templates</code>: | 738 <code>templates</code>: |
739 | 739 |
740 <pre> | 740 <pre> |
741 func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { | 741 func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { |
742 err := templates[tmpl].Execute(w, p) | 742 err := templates[tmpl].Execute(w, p) |
743 if err != nil { | 743 if err != nil { |
744 » » http.Error(w, err.String(), http.StatusInternalServerError) | 744 » » http.Error(w, err.Error(), http.StatusInternalServerError) |
745 } | 745 } |
746 } | 746 } |
747 </pre> | 747 </pre> |
748 | 748 |
749 <h2>Validation</h2> | 749 <h2>Validation</h2> |
750 | 750 |
751 <p> | 751 <p> |
752 As you may have observed, this program has a serious security flaw: a user | 752 As you may have observed, this program has a serious security flaw: a user |
753 can supply an arbitrary path to be read/written on the server. To mitigate | 753 can supply an arbitrary path to be read/written on the server. To mitigate |
754 this, we can write a function to validate the title with a regular expression. | 754 this, we can write a function to validate the title with a regular expression. |
(...skipping 15 matching lines...) Expand all Loading... |
770 panic if the expression compilation fails, while <code>Compile</code> returns | 770 panic if the expression compilation fails, while <code>Compile</code> returns |
771 an <code>os.Error</code> as a second parameter.· | 771 an <code>os.Error</code> as a second parameter.· |
772 </p> | 772 </p> |
773 | 773 |
774 <p> | 774 <p> |
775 Now, let's write a function that extracts the title string from the request· | 775 Now, let's write a function that extracts the title string from the request· |
776 URL, and tests it against our <code>TitleValidator</code> expression: | 776 URL, and tests it against our <code>TitleValidator</code> expression: |
777 </p> | 777 </p> |
778 | 778 |
779 <pre> | 779 <pre> |
780 func getTitle(w http.ResponseWriter, r *http.Request) (title string, err os.Erro
r) { | 780 func getTitle(w http.ResponseWriter, r *http.Request) (title string, err error)
{ |
781 title = r.URL.Path[lenPath:] | 781 title = r.URL.Path[lenPath:] |
782 if !titleValidator.MatchString(title) { | 782 if !titleValidator.MatchString(title) { |
783 http.NotFound(w, r) | 783 http.NotFound(w, r) |
784 » » err = os.NewError("Invalid Page Title") | 784 » » err = errors.New("Invalid Page Title") |
785 } | 785 } |
786 return | 786 return |
787 } | 787 } |
788 </pre> | 788 </pre> |
789 | 789 |
790 <p> | 790 <p> |
791 If the title is valid, it will be returned along with a <code>nil</code> | 791 If the title is valid, it will be returned along with a <code>nil</code> |
792 error value. If the title is invalid, the function will write a· | 792 error value. If the title is invalid, the function will write a· |
793 "404 Not Found" error to the HTTP connection, and return an error to the· | 793 "404 Not Found" error to the HTTP connection, and return an error to the· |
794 handler.· | 794 handler.· |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
826 | 826 |
827 func saveHandler(w http.ResponseWriter, r *http.Request) { | 827 func saveHandler(w http.ResponseWriter, r *http.Request) { |
828 title, err := getTitle(w, r) | 828 title, err := getTitle(w, r) |
829 if err != nil { | 829 if err != nil { |
830 return | 830 return |
831 } | 831 } |
832 body := r.FormValue("body") | 832 body := r.FormValue("body") |
833 p := &Page{Title: title, Body: []byte(body)} | 833 p := &Page{Title: title, Body: []byte(body)} |
834 err = p.save() | 834 err = p.save() |
835 if err != nil { | 835 if err != nil { |
836 » » http.Error(w, err.String(), http.StatusInternalServerError) | 836 » » http.Error(w, err.Error(), http.StatusInternalServerError) |
837 return | 837 return |
838 } | 838 } |
839 http.Redirect(w, r, "/view/"+title, http.StatusFound) | 839 http.Redirect(w, r, "/view/"+title, http.StatusFound) |
840 } | 840 } |
841 </pre> | 841 </pre> |
842 | 842 |
843 <h2>Introducing Function Literals and Closures</h2> | 843 <h2>Introducing Function Literals and Closures</h2> |
844 | 844 |
845 <p> | 845 <p> |
846 Catching the error condition in each handler introduces a lot of repeated code. | 846 Catching the error condition in each handler introduces a lot of repeated code. |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
951 p = &Page{Title: title} | 951 p = &Page{Title: title} |
952 } | 952 } |
953 renderTemplate(w, "edit", p) | 953 renderTemplate(w, "edit", p) |
954 } | 954 } |
955 | 955 |
956 func saveHandler(w http.ResponseWriter, r *http.Request, title string) { | 956 func saveHandler(w http.ResponseWriter, r *http.Request, title string) { |
957 body := r.FormValue("body") | 957 body := r.FormValue("body") |
958 p := &Page{Title: title, Body: []byte(body)} | 958 p := &Page{Title: title, Body: []byte(body)} |
959 err := p.save() | 959 err := p.save() |
960 if err != nil { | 960 if err != nil { |
961 » » http.Error(w, err.String(), http.StatusInternalServerError) | 961 » » http.Error(w, err.Error(), http.StatusInternalServerError) |
962 return | 962 return |
963 } | 963 } |
964 http.Redirect(w, r, "/view/"+title, http.StatusFound) | 964 http.Redirect(w, r, "/view/"+title, http.StatusFound) |
965 } | 965 } |
966 </pre> | 966 </pre> |
967 | 967 |
968 <h2>Try it out!</h2> | 968 <h2>Try it out!</h2> |
969 | 969 |
970 <p> | 970 <p> |
971 <a href="final.go">Click here to view the final code listing.</a> | 971 <a href="final.go">Click here to view the final code listing.</a> |
(...skipping 26 matching lines...) Expand all Loading... |
998 <li>Add a handler to make the web root redirect to· | 998 <li>Add a handler to make the web root redirect to· |
999 <code>/view/FrontPage</code>.</li> | 999 <code>/view/FrontPage</code>.</li> |
1000 <li>Spruce up the page templates by making them valid HTML and adding some | 1000 <li>Spruce up the page templates by making them valid HTML and adding some |
1001 CSS rules.</li> | 1001 CSS rules.</li> |
1002 <li>Implement inter-page linking by converting instances of· | 1002 <li>Implement inter-page linking by converting instances of· |
1003 <code>[PageName]</code> to <br> | 1003 <code>[PageName]</code> to <br> |
1004 <code><a href="/view/PageName">PageName</a></code>. | 1004 <code><a href="/view/PageName">PageName</a></code>. |
1005 (hint: you could use <code>regexp.ReplaceAllFunc</code> to do this) | 1005 (hint: you could use <code>regexp.ReplaceAllFunc</code> to do this) |
1006 </li> | 1006 </li> |
1007 </ul> | 1007 </ul> |
LEFT | RIGHT |