Vous êtes sur la page 1sur 15

Writing Web Applications

Introduction Getting Started Data Structures Introducing the net/http package (an interlude) Using net/http to serve wiki pages Editing Pages The html/template package andling non!e"istent pages Saving Pages Error handling Template caching #alidation Introducing $unction %iterals and &losures Tr' it out( )ther tasks

Introduction
&overed in this tutorial*

&reating a data structure with load and save methods Using the net/http package to +uild we+ applications Using the html/template package to process T,% templates Using the regexp package to validate user input Using closures

-ssumed knowledge*

Programming e"perience Understanding o. +asic we+ technologies ( TTP/ T,%) Some U0I1/D)S command!line knowledge

Getting Started
-t present/ 'ou need to have a $ree2SD/ %inu"/ )S 1/ or 3indows machine to run Go4 3e will use $ to represent the command prompt4 Install Go (see the Installation Instructions)4 ,ake a new director' .or this tutorial inside 'our GOPATH and cd to it*
$ mkdir gowiki $ cd gowiki

&reate a .ile named wiki.go/ open it in 'our .avorite editor/ and add the .ollowing lines*
package main import ( "fmt" "io/ioutil"

3e import the fmt and ioutil packages .rom the Go standard li+rar'4 %ater/ as we implement additional .unctionalit'/ we will add more packages to this import declaration4

Data Structures
%et6s start +' de.ining the data structures4 - wiki consists o. a series o. interconnected pages/ each o. which has a title and a +od' (the page content)4 ere/ we de.ine Page as a struct with two .ields representing the title and +od'4
t!pe Page "truct # Title "tring $od! %&'!te (

The t'pe %&'!te means 7a '!te slice74 (See Slices* usage and internals .or more on slices4) The $od! element is a %&'!te rather than "tring +ecause that is the t'pe e"pected +' the io li+raries we will use/ as 'ou6ll see +elow4 The Page struct descri+es how page data will +e stored in memor'4 2ut what a+out persistent storage8 3e can address that +' creating a "a)e method on Page*
func (p *Page "a)e( error # filename +, p.Title - ".txt" return ioutil..rite/ile(filename0 p.$od!0 1211 (

This method6s signature reads* 7This is a method named "a)e that takes as its receiver p/ a pointer to Page 4 It takes no parameters/ and returns a value o. t'pe error47 This method will save the Page6s $od! to a te"t .ile4 $or simplicit'/ we will use the Title as the .ile name4 The "a)e method returns an error value +ecause that is the return t'pe o. .rite/ile (a standard li+rar' .unction that writes a +'te slice to a .ile)4 The "a)e method returns the error value/ to let the application handle it should an'thing go wrong while writing the .ile4 I. all goes well/ Page."a)e( will return nil (the 9ero!value .or pointers/ inter.aces/ and some other t'pes)4 The octal integer constant 1211/ passed as the third parameter to .rite/ile/ indicates that the .ile should +e created with read!write permissions .or the current user onl'4 (See the Uni" man page open(3 .or details4) 3e will want to load pages/ too*
func loadPage(title "tring *Page # filename +, title - ".txt" 'od!0 4 +, ioutil.5ead/ile(filename return 6Page#Title+ title0 $od!+ 'od!( (

The .unction loadPage constructs the .ile name .rom Title/ reads the .ile6s contents into a new Page/ and returns a pointer to that new page4 $unctions can return multiple values4 The standard li+rar' .unction io.5ead/ile returns %&'!te and error4 In loadPage/ error isn6t +eing handled 'et; the 7+lank identi.ier7 represented +' the underscore (4) s'm+ol is used to throw awa' the error return value (in essence/ assigning the value to nothing)4 2ut what happens i. 5ead/ile encounters an error8 $or e"ample/ the .ile might not e"ist4 3e should not ignore such errors4 %et6s modi.' the .unction to return *Page and error4
func loadPage(title "tring (*Page0 error # filename +, title - ".txt" 'od!0 err +, ioutil.5ead/ile(filename if err 7, nil # return nil0 err ( return 6Page#Title+ title0 $od!+ 'od!(0 nil (

&allers o. this .unction can now check the second parameter; i. it is nil then it has success.ull' loaded a Page4 I. not/ it will +e an error that can +e handled +' the caller (see the language speci.ication .or details)4 -t this point we have a simple data structure and the a+ilit' to save to and load .rom a .ile4 %et6s write a main .unction to test what we6ve written*
func main( # p8 +, 6Page#Title+ "Te"tPage"0 $od!+ %&'!te("Thi" i" a "ample Page." ( p8."a)e( p30 4 +, loadPage("Te"tPage" fmt.Println("tring(p3.$od! (

-.ter compiling and e"ecuting this code/ a .ile named Te"tPage.txt would +e created/ containing the contents o. p84 The .ile would then +e read into the struct p3/ and its $od! element printed to the screen4 <ou can compile and run the program like this*
$ go 'uild wiki.go $ ./wiki Thi" i" a "ample page.

(I. 'ou6re using 3indows 'ou must t'pe 7wiki7 without the 7./7 to run the program4) &lick here to view the code we6ve written so .ar4

Introducing the net/http package (an interlude)


ere6s a .ull working e"ample o. a simple we+ server*

package main import ( "fmt" "net/http" func handler(w http.5e"pon"e.riter0 r *http.5e9ue"t # fmt./printf(w0 "Hi there0 : lo)e ;"7"0 r.<5=.Path%8+& ( func main( # http.Handle/unc("/"0 handler http.=i"tenAnd>er)e("+?1?1"0 nil (

The main .unction +egins with a call to http.Handle/unc/ which tells the http package to handle all re>uests to the we+ root ("/") with handler4 It then calls http.=i"tenAnd>er)e/ speci.'ing that it should listen on port ?@?@ on an' inter.ace ("+?1?1")4 (Don6t worr' a+out its second parameter/ nil/ .or now4) This .unction will +lock until the program is terminated4 The .unction handler is o. the t'pe http.Handler/unc4 It takes an http.5e"pon"e.riter and an http.5e9ue"t as its arguments4 -n http.5e"pon"e.riter value assem+les the TTP server6s response; +' writing to it/ we send data to the TTP client4 -n http.5e9ue"t is a data structure that represents the client TTP re>uest4 The string r.<5=.Path is the path component o. the re>uest UA%4 The trailing %8+& means 7create a su+!slice o. Path .rom the 5st character to the end47 This drops the leading 7/7 .rom the path name4 I. 'ou run this program and access the UA%*
http+//localho"t+?1?1/monke!"

the program would present a page containing*


Hi there0 : lo)e monke!"7

Using net/http to serve wiki pages


To use the net/http package/ it must +e imported*
import ( "fmt" "net/http" "io/ioutil"

%et6s create a handler to view a wiki page* B

con"t lenPath , len("/)iew/" func )iewHandler(w http.5e"pon"e.riter0 r *http.5e9ue"t # title +, r.<5=.Path%lenPath+& p0 4 +, loadPage(title fmt./printf(w0 "@h8A;"@/h8A@di)A;"@/di)A"0 p.Title0 p.$od! (

$irst/ this .unction e"tracts the page title .rom r.<5=.Path/ the path component o. the re>uest UA%4 The glo+al constant lenPath is the length o. the leading "/)iew/" component o. the re>uest path4 The Path is re!sliced with %lenPath+& to drop the .irst C characters o. the string4 This is +ecause the path will invaria+l' +egin with "/)iew/"/ which is not part o. the page title4 The .unction then loads the page data/ .ormats the page with a string o. simple T,%/ and writes it to w/ the http.5e"pon"e.riter4 -gain/ note the use o. 4 to ignore the error return value .rom loadPage4 This is done here .or simplicit' and generall' considered +ad practice4 3e will attend to this later4 To use this handler/ we create a main .unction that initiali9es http using the )iewHandler to handle an' re>uests under the path /)iew/4
func main( # http.Handle/unc("/)iew/"0 )iewHandler http.=i"tenAnd>er)e("+?1?1"0 nil (

&lick here to view the code we6ve written so .ar4 %et6s create some page data (as te"t.txt)/ compile our code/ and tr' serving a wiki page4 )pen te"t.txt .ile in 'our editor/ and save the string 7 ello world7 (without >uotes) in it4
$ go 'uild wiki.go $ ./wiki

3ith this we+ server running/ a visit to http+//localho"t+?1?1/)iew/te"t should show a page titled 7test7 containing the words 7 ello world74

Editing Pages
- wiki is not a wiki without the a+ilit' to edit pages4 %et6s create two new handlers* one named editHandler to displa' an 6edit page6 .orm/ and the other named "a)eHandler to save the data entered via the .orm4 $irst/ we add them to main( *
func main( # http.Handle/unc("/)iew/"0 )iewHandler http.Handle/unc("/edit/"0 editHandler http.Handle/unc("/"a)e/"0 "a)eHandler http.=i"tenAnd>er)e("+?1?1"0 nil

The .unction editHandler loads the page (or/ i. it doesn6t e"ist/ create an empt' Page struct)/ and displa's an T,% .orm4
func editHandler(w http.5e"pon"e.riter0 r *http.5e9ue"t title +, r.<5=.Path%lenPath+& p0 err +, loadPage(title if err 7, nil # p , 6Page#Title+ title( ( fmt./printf(w0 "@h8ABditing ;"@/h8A""@form action,C"/"a)e/;"C" method,C"PO>TC"A""@textarea name,C"'od!C"A;"@/textareaA@'rA""@input t!pe,C""u'mitC" )alue,C">a)eC"A""@/formA"0 p.Title0 p.Title0 p.$od! ( #

This .unction will work .ine/ +ut all that hard!coded T,% is ugl'4 ). course/ there is a +etter wa'4

he html/template package
The html/template package is part o. the Go standard li+rar'4 3e can use html/template to keep the T,% in a separate .ile/ allowing us to change the la'out o. our edit page without modi.'ing the underl'ing Go code4 $irst/ we must add html/template to the list o. imports*
import ( "html/template" "http" "io/ioutil" "o""

%et6s create a template .ile containing the T,% .orm4 )pen a new .ile named edit.html/ and add the .ollowing lines*
@h8ABditing ##.Title((@/h8A @form action,"/"a)e/##.Title((" method,"PO>T"A @di)A@textarea name,"'od!" row","31" col","?1"A##printf ";"" .$od!((@/textareaA@/di)A @di)A@input t!pe,""u'mit" )alue,">a)e"A@/di)A @/formA

,odi.' editHandler to use the template/ instead o. the hard!coded T,%*


func editHandler(w http.5e"pon"e.riter0 r *http.5e9ue"t title +, r.<5=.Path%lenPath+& p0 err +, loadPage(title if err 7, nil # p , 6Page#Title+ title( #

( t0 4 +, template.Par"e/ile"("edit.html" t.Bxecute(w0 p

The .unction template.Par"e/ile" will read the contents o. edit.html and return a *template.Template4 The method t.Bxecute e"ecutes the template/ writing the generated T,% to the http.5e"pon"e.riter4 The .Title and .$od! dotted identi.iers re.er to p.Title and p.$od!4 Template directives are enclosed in dou+le curl' +races4 The printf ";"" .$od! instruction is a .unction call that outputs .$od! as a string instead o. a stream o. +'tes/ the same as a call to fmt.Printf4 The html/template package helps guarantee that onl' sa.e and correct! looking T,% is generated +' template actions4 $or instance/ it automaticall' escapes an' greater than sign (A)/ replacing it with 6gtD/ to make sure user data does not corrupt the .orm T,%4 0ow that we6ve removed the fmt./printf statement/ we can remove "fmt" .rom the import list4 3hile we6re working with templates/ let6s create a template .or our )iewHandler called )iew.html*
@h8A##.Title((@/h8A @pA%@a href,"/edit/##.Title(("Aedit@/aA&@/pA @di)A##printf ";"" .$od!((@/di)A

,odi.' )iewHandler accordingl'*


func )iewHandler(w http.5e"pon"e.riter0 r *http.5e9ue"t title +, r.<5=.Path%lenPath+& p0 4 +, loadPage(title t0 4 +, template.Par"e/ile"(")iew.html" t.Bxecute(w0 p ( #

0otice that we6ve used almost e"actl' the same templating code in +oth handlers4 %et6s remove this duplication +' moving the templating code to its own .unction*
func )iewHandler(w http.5e"pon"e.riter0 r *http.5e9ue"t title +, r.<5=.Path%lenPath+& p0 4 +, loadPage(title renderTemplate(w0 ")iew"0 p ( func editHandler(w http.5e"pon"e.riter0 r *http.5e9ue"t title +, r.<5=.Path%lenPath+& p0 err +, loadPage(title if err 7, nil # p , 6Page#Title+ title( ( #

renderTemplate(w0 "edit"0 p ( func renderTemplate(w http.5e"pon"e.riter0 tmpl "tring0 p *Page t0 4 +, template.Par"e/ile"(tmpl - ".html" t.Bxecute(w0 p (

The handlers are now shorter and simpler4

!andling non"e#istent pages


3hat i. 'ou visit /)iew/APageThatEoe"ntBxi"t8 The program will crash4 This is +ecause it ignores the error return value .rom loadPage4 Instead/ i. the re>uested Page doesn6t e"ist/ it should redirect the client to the edit Page so the content ma' +e created*
func )iewHandler(w http.5e"pon"e.riter0 r *http.5e9ue"t # title0 err +, getTitle(w0 r if err 7, nil # return ( p0 err +, loadPage(title if err 7, nil # http.5edirect(w0 r0 "/edit/"-title0 http.>tatu"/ound return ( renderTemplate(w0 ")iew"0 p (

The http.5edirect .unction adds an TTP status code o. http.>tatu"/ound (=@:) and a =ocation header to the TTP response4

Saving Pages
The .unction "a)eHandler will handle the .orm su+mission4
func "a)eHandler(w http.5e"pon"e.riter0 r *http.5e9ue"t # title +, r.<5=.Path%lenPath+& 'od! +, r./ormFalue("'od!" p +, 6Page#Title+ title0 $od!+ %&'!te('od! ( p."a)e( http.5edirect(w0 r0 "/)iew/"-title0 http.>tatu"/ound (

The page title (provided in the UA%) and the .orm6s onl' .ield/ $od!/ are stored in a new Page4 The "a)e( method is then called to write the data to a .ile/ and the client is redirected to the /)iew/ page4 The value returned +' /ormFalue is o. t'pe "tring4 3e must convert that value to %&'!te +e.ore it will .it into the Page struct4 3e use %&'!te('od! to per.orm the conversion4

Error handling

There are several places in our program where errors are +eing ignored4 This is +ad practice/ not least +ecause when an error does occur the program will crash4 - +etter solution is to handle the errors and return an error message to the user4 That wa' i. something does go wrong/ the server will continue to .unction and the user will +e noti.ied4 $irst/ let6s handle the errors in renderTemplate*
func renderTemplate(w http.5e"pon"e.riter0 tmpl "tring0 p *Page # t0 err +, template.Par"e/ile"(tmpl - ".html" if err 7, nil # http.Brror(w0 err.Brror( 0 http.>tatu":nternal>er)erBrror return ( err , t.Bxecute(w0 p if err 7, nil # http.Brror(w0 err.Brror( 0 http.>tatu":nternal>er)erBrror ( (

The http.Brror .unction sends a speci.ied TTP response code (in this case 7Internal Server Error7) and error message4 -lread' the decision to put this in a separate .unction is pa'ing o..4 0ow let6s .i" up "a)eHandler*
func "a)eHandler(w http.5e"pon"e.riter0 r *http.5e9ue"t # title0 err +, getTitle(w0 r if err 7, nil # return ( 'od! +, r./ormFalue("'od!" p +, 6Page#Title+ title0 $od!+ %&'!te('od! ( err , p."a)e( if err 7, nil # http.Brror(w0 err.Brror( 0 http.>tatu":nternal>er)erBrror return ( http.5edirect(w0 r0 "/)iew/"-title0 http.>tatu"/ound (

-n' errors that occur during p."a)e( will +e reported to the user4

e$plate caching
There is an ine..icienc' in this code* renderTemplate calls Par"e/ile" ever' time a page is rendered4 - +etter approach would +e to call Par"e/ile" once at program initiali9ation/ parsing all templates into a single *Template4 Then we can use the BxecuteTemplate method to render a speci.ic template4 $irst we create a glo+al varia+le named template"/ and initiali9e it with Par"e/ile"4
)ar template" , template.Gu"t(template.Par"e/ile"("edit.html"0 ")iew.html"

The .unction template.Gu"t is a convenience wrapper that panics when passed a non!nil error value/ and otherwise returns the *Template unaltered4 - panic is appropriate here; i. the templates can6t +e loaded the onl' sensi+le thing to do is e"it the program4 - for loop is used with a range statement to iterate over an arra' constant containing the names o. the templates we want parsed4 I. we were to add more templates to our program/ we would add their names to that arra'4 3e then modi.' the renderTemplate .unction to call the template".BxecuteTemplate method with the name o. the appropriate template*
func renderTemplate(w http.5e"pon"e.riter0 tmpl "tring0 p *Page # err +, template".BxecuteTemplate(w0 tmpl-".html"0 p if err 7, nil # http.Brror(w0 err.Brror( 0 http.>tatu":nternal>er)erBrror ( (

0ote that the template name is the template .ile name/ so we must append ".html" to the tmpl argument4

%alidation
-s 'ou ma' have o+served/ this program has a serious securit' .law* a user can suppl' an ar+itrar' path to +e read/written on the server4 To mitigate this/ we can write a .unction to validate the title with a regular e"pression4 $irst/ add "regexp" to the import list4 Then we can create a glo+al varia+le to store our validation rege"p*
)ar titleFalidator , regexp.Gu"tHompile("I%aJKAJL1JM&-$"

The .unction regexp.Gu"tHompile will parse and compile the regular e"pression/ and return a regexp.5egexp4 Gu"tHompile is distinct .rom Hompile in that it will panic i. the e"pression compilation .ails/ while Hompile returns an error as a second parameter4 0ow/ let6s write a .unction that e"tracts the title string .rom the re>uest UA%/ and tests it against our TitleFalidator e"pression*
func getTitle(w http.5e"pon"e.riter0 r *http.5e9ue"t error # title , r.<5=.Path%lenPath+& if 7titleFalidator.Gatch>tring(title # http.Not/ound(w0 r err , error".New(":n)alid Page Title" ( return ( (title "tring0 err

I. the title is valid/ it will +e returned along with a nil error value4 I. the title is invalid/ the .unction will write a 7B@B 0ot $ound7 error to the TTP connection/ and return an error to the handler4 5@

%et6s put a call to getTitle in each o. the handlers*


func )iewHandler(w http.5e"pon"e.riter0 r *http.5e9ue"t # title0 err +, getTitle(w0 r if err 7, nil # return ( p0 err +, loadPage(title if err 7, nil # http.5edirect(w0 r0 "/edit/"-title0 http.>tatu"/ound return ( renderTemplate(w0 ")iew"0 p ( func editHandler(w http.5e"pon"e.riter0 r *http.5e9ue"t # title0 err +, getTitle(w0 r if err 7, nil # return ( p0 err +, loadPage(title if err 7, nil # p , 6Page#Title+ title( ( renderTemplate(w0 "edit"0 p ( func "a)eHandler(w http.5e"pon"e.riter0 r *http.5e9ue"t # title0 err +, getTitle(w0 r if err 7, nil # return ( 'od! +, r./ormFalue("'od!" p +, 6Page#Title+ title0 $od!+ %&'!te('od! ( err , p."a)e( if err 7, nil # http.Brror(w0 err.Brror( 0 http.>tatu":nternal>er)erBrror return ( http.5edirect(w0 r0 "/)iew/"-title0 http.>tatu"/ound (

Introducing &unction 'iterals and (losures


&atching the error condition in each handler introduces a lot o. repeated code4 3hat i. we could wrap each o. the handlers in a .unction that does this validation and error checking8 Go6s .unction literals provide a power.ul means o. a+stracting .unctionalit' that can help us here4 $irst/ we re!write the .unction de.inition o. each o. the handlers to accept a title string*
func )iewHandler(w http.5e"pon"e.riter0 r *http.5e9ue"t0 title "tring func editHandler(w http.5e"pon"e.riter0 r *http.5e9ue"t0 title "tring func "a)eHandler(w http.5e"pon"e.riter0 r *http.5e9ue"t0 title "tring

0ow let6s de.ine a wrapper .unction that takes a function of the above type/ and returns a .unction o. t'pe http.Handler/unc (suita+le to +e passed to the .unction http.Handle/unc)*

55

func makeHandler(fn func (http.5e"pon"e.riter0 *http.5e9ue"t0 "tring http.Handler/unc # return func(w http.5e"pon"e.riter0 r *http.5e9ue"t # // Here we will extract the page title from the 5e9ue"t0 // and call the pro)ided handler OfnO ( (

The returned .unction is called a closure +ecause it encloses values de.ined outside o. it4 In this case/ the varia+le fn (the single argument to makeHandler) is enclosed +' the closure4 The varia+le fn will +e one o. our save/ edit/ or view handlers4 0ow we can take the code .rom getTitle and use it here (with some minor modi.ications)*
func makeHandler(fn func(http.5e"pon"e.riter0 *http.5e9ue"t0 "tring http.Handler/unc # return func(w http.5e"pon"e.riter0 r *http.5e9ue"t # title +, r.<5=.Path%lenPath+& if 7titleFalidator.Gatch>tring(title # http.Not/ound(w0 r return ( fn(w0 r0 title ( (

The closure returned +' makeHandler is a .unction that takes an http.5e"pon"e.riter and http.5e9ue"t (in other words/ an http.Handler/unc)4 The closure e"tracts the title .rom the re>uest path/ and validates it with the TitleFalidator rege"p4 I. the title is invalid/ an error will +e written to the 5e"pon"e.riter using the http.Not/ound .unction4 I. the title is valid/ the enclosed handler .unction fn will +e called with the 5e"pon"e.riter/ 5e9ue"t/ and title as arguments4 0ow we can wrap the handler .unctions with makeHandler in main/ +e.ore the' are registered with the http package*
func main( # http.Handle/unc("/)iew/"0 makeHandler()iewHandler http.Handle/unc("/edit/"0 makeHandler(editHandler http.Handle/unc("/"a)e/"0 makeHandler("a)eHandler http.=i"tenAnd>er)e("+?1?1"0 nil (

$inall' we remove the calls to getTitle .rom the handler .unctions/ making them much simpler*
func )iewHandler(w http.5e"pon"e.riter0 r *http.5e9ue"t0 title "tring p0 err +, loadPage(title if err 7, nil # http.5edirect(w0 r0 "/edit/"-title0 http.>tatu"/ound return ( renderTemplate(w0 ")iew"0 p ( func editHandler(w http.5e"pon"e.riter0 r *http.5e9ue"t0 title "tring p0 err +, loadPage(title #

5:

if err 7, nil # p , 6Page#Title+ title( ( renderTemplate(w0 "edit"0 p ( func "a)eHandler(w http.5e"pon"e.riter0 r *http.5e9ue"t0 title "tring 'od! +, r./ormFalue("'od!" p +, 6Page#Title+ title0 $od!+ %&'!te('od! ( err +, p."a)e( if err 7, nil # http.Brror(w0 err.Brror( 0 http.>tatu":nternal>er)erBrror return ( http.5edirect(w0 r0 "/)iew/"-title0 http.>tatu"/ound ( #

r) it out*
&lick here to view the .inal code listing4 Aecompile the code/ and run the app*
$ go 'uild wiki.go $ ./wiki

#isiting http*//localhost*?@?@/view/-0ewPage should present 'ou with the page edit .orm4 <ou should then +e a+le to enter some te"t/ click 6Save6/ and +e redirected to the newl' created page4

+ther tasks
ere are some simple tasks 'ou might want to tackle on 'our own*

Store templates in tmpl/ and page data in data/4 -dd a handler to make the we+ root redirect to /)iew//rontPage4 Spruce up the page templates +' making them valid T,% and adding some &SS rules4 Implement inter!page linking +' converting instances o. %PageName& to @a href,"/)iew/PageName"APageName@/aA4 (hint* 'ou could use regexp.5eplaceAll/unc to do this)

5=

Source ,ile doc-articles-wiki-,inal.go


8 // Hop!right 3181 The Go Author". All right" re"er)ed. 3 // <"e of thi" "ource code i" go)erned '! a $>EJ"t!le P // licen"e that can 'e found in the =:HBN>B file. Q R package main 2 S import ( ? "html/template" M "io/ioutil" 81 "net/http" 88 "regexp" 83 8P 8Q t!pe Page "truct # 8R Title "tring 82 $od! %&'!te 8S ( 8? 8M func (p *Page "a)e( error # 31 filename +, p.Title - ".txt" 38 return ioutil..rite/ile(filename0 p.$od!0 1211 33 ( 3P 3Q func loadPage(title "tring (*Page0 error # 3R filename +, title - ".txt" 32 'od!0 err +, ioutil.5ead/ile(filename 3S if err 7, nil # 3? return nil0 err 3M ( P1 return 6Page#Title+ title0 $od!+ 'od!(0 nil P8 ( P3 PP func )iewHandler(w http.5e"pon"e.riter0 r *http.5e9ue"t0 title "tring # PQ p0 err +, loadPage(title PR if err 7, nil # P2 http.5edirect(w0 r0 "/edit/"-title0 http.>tatu"/ound PS return P? ( PM renderTemplate(w0 ")iew"0 p Q1 ( Q8 Q3 func editHandler(w http.5e"pon"e.riter0 r *http.5e9ue"t0 title "tring # QP p0 err +, loadPage(title QQ if err 7, nil # QR p , 6Page#Title+ title( Q2 ( QS renderTemplate(w0 "edit"0 p Q? ( QM R1 func "a)eHandler(w http.5e"pon"e.riter0 r *http.5e9ue"t0 title "tring # R8 'od! +, r./ormFalue("'od!" R3 p +, 6Page#Title+ title0 $od!+ %&'!te('od! (

5B

RP err +, p."a)e( RQ if err 7, nil # RR http.Brror(w0 err.Brror( 0 http.>tatu":nternal>er)erBrror R2 return RS ( R? http.5edirect(w0 r0 "/)iew/"-title0 http.>tatu"/ound RM ( 21 28 )ar template" , template.Gu"t(template.Par"e/ile"("edit.html"0 ")iew.html" 23 2P func renderTemplate(w http.5e"pon"e.riter0 tmpl "tring0 p *Page 2Q err +, template".BxecuteTemplate(w0 tmpl-".html"0 p 2R if err 7, nil # 22 http.Brror(w0 err.Brror( 0 http.>tatu":nternal>er)erBrror 2S ( 2? ( 2M S1 con"t lenPath , len("/)iew/" S8 S3 )ar titleFalidator , regexp.Gu"tHompile("I%aJKAJL1JM&-$" SP SQ func makeHandler(fn func(http.5e"pon"e.riter0 *http.5e9ue"t0 "tring http.Handler/unc # SR return func(w http.5e"pon"e.riter0 r *http.5e9ue"t # S2 title +, r.<5=.Path%lenPath+& SS if 7titleFalidator.Gatch>tring(title # S? http.Not/ound(w0 r SM return ?1 ( ?8 fn(w0 r0 title ?3 ( ?P ( ?Q ?R func main( # ?2 http.Handle/unc("/)iew/"0 makeHandler()iewHandler ?S http.Handle/unc("/edit/"0 makeHandler(editHandler ?? http.Handle/unc("/"a)e/"0 makeHandler("a)eHandler ?M http.=i"tenAnd>er)e("+?1?1"0 nil M1 (

5D

Vous aimerez peut-être aussi