BuildingWebAppswithGo
TableofContents
Introduction
0
GoMakesThingsSimple
1
Thenet/httppackage
2
CreatingaBasicWebApp
3
Deployment
4
URLRouting
5
Middleware
6
Rendering
7
JSON
7.1
HTMLTemplates
7.2
UsingTherenderpackage
7.3
Testing
8
UnitTesting
8.1
EndtoEndTesting
8.2
Controllers
9
Databases
10
TipsandTricks
11
MovingForward
12
2
BuildingWebAppswithGo
Introduction
WelcometoBuildingWebAppswithGo!Ifyouarereadingthisthenyouhavejuststarted
yourjourneyfromnoobtopro.Noseriously,webprogramminginGoissofunandeasythat
youwon'tevennoticehowmuchinformationyouarelearningalongtheway!
Keepinmindthattherearestillportionsofthisbookthatareincompleteandneedsome
love.ThebeautyofopensourcepublishingisthatIcangiveyouanincompletebookandit
isstillofvaluetoyou.
Beforewegetintoallthenittygrittydetails,let'sstartwithsomegroundrules:
Prerequisites
Tokeepthistutorialsmallandfocused,I'massumingthatyouarepreparedinthefollowing
ways:
1. YouhaveinstalledtheGoProgrammingLanguage.
2. Youhavesetupa GOPATHbyfollowingtheHowtoWriteGoCodetutorial.
3. YouaresomewhatfamiliarwiththebasicsofGo.(TheGoTourisaprettygoodplaceto
start)
4. Youhaveinstalledalltherequiredpackages
5. YouhaveinstalledtheHerokuToolbelt
6. YouhaveaHerokuaccount
RequiredPackages
Forthemostpartwewillbeusingthebuiltinpackagesfromthestandardlibrarytobuildout
ourwebapps.CertainlessonssuchasDatabases,MiddlewareandURLRoutingwillrequire
athirdpartypackage.Hereisalistofallthegopackagesyouwillneedtoinstallbefore
starting:
Introduction
3
BuildingWebAppswithGo
Name
ImportPath
Description
httprouter
github.com/julienschmidt/httprouter
AhighperformanceHTTPrequest
routerthatscaleswell
Negroni
github.com/codegangsta/negroni
IdiomaticHTTPMiddleware
Black
Friday
github.com/russross/blackfriday
amarkdownprocessor
Render
gopkg.in/unrolled/render.v1
EasyrenderingforJSON,XML,and
HTML
SQLite3
github.com/mattn/go-sqlite3
sqlite3driverforgo
Youcaninstall(orupdate)thesepackagesbyrunningthefollowingcommandinyour
console
goget-u<import_path>
Forinstance,ifyouwishtoinstallNegroni,thefollowingcommandwouldbe:
goget-ugithub.com/codegangsta/negroni
Introduction
4
BuildingWebAppswithGo
GoMakesThingsSimple
Ifyouhavebuiltawebapplicationbefore,yousurelyknowthattherearequitealotof
conceptstokeepinyourhead.HTTP,HTML,CSS,JSON,databases,sessions,cookies,
forms,middleware,routingandcontrollersarejustafewamongthemanythingsyourweb
appmayneedtointeractwith.
Whileeachoneofthesethingscanbeimportantinthebuildingofyourwebapplications,not
everyoneofthemisimportantforanygivenapp.Forinstance,awebAPImayjustuse
JSONasitsserializationformat,thusmakingconceptslikeHTMLnotrelevantforthat
particularwebapp.
TheGoWay
TheGocommunityunderstandsthisdilemma.Ratherthanrelyonlarge,heavyweight
frameworksthattrytocoverallthebases,Goprogrammerspullinthebarenecessitiesto
getthejobdone.Thisminimalistapproachtowebprogrammingmaybeoff-puttingatfirst,
buttheresultofthiseffortisamuchsimplerprogramintheend.
Gomakesthingssimple,it'saseasyasthat.Ifwetrainourselvestoalignwiththe"Go
way"ofprogrammingfortheweb,wewillendupwithmoresimple,flexible,and
maintainablewebapplications.
PowerinSimplicity
Aswegothroughtheexercisesinthisbook,Ithinkyouwillbesurprisedbyhowsimple
someoftheseprogramscanbewhilststillaffordingabunchoffunctionality.
WhensittingdowntocraftyourownwebapplicationsinGo,thinkhardaboutthe
componentsandconceptsthatyourappwillbefocusedon,andusejustthosepieces.This
bookwillbecoveringawidearrayofwebtopics,butdonotfeelobligatedtousethemall.In
thewordsofourfriendLonestar,"Takeonlywhatyouneedtosurvive".
GoMakesThingsSimple
5
BuildingWebAppswithGo
GoMakesThingsSimple
6
BuildingWebAppswithGo
Thenet/httpPackage
YouhaveprobablyheardthatGoisfantasticforbuildingwebapplicationsofallshapesand
sizes.Thisispartlyduetothefantasticworkthathasbeenputintomakingthestandard
libraryclean,consistent,andeasytouse.
PerhapsoneofthemostimportantpackagesforanybuddingGowebdeveloperisthe
net/httppackage.ThispackageallowsyoutobuildHTTPserversinGowithitspowerful
compositionalconstructs.Beforewestartcoding,let'sdoanextremelyquickoverviewof
HTTP.
HTTPBasics
Whenwetalkaboutbuildingwebapplications,weusuallymeanthatwearebuildingHTTP
servers.HTTPisaprotocolthatwasoriginallydesignedtotransportHTMLdocumentsfrom
aservertoaclientwebbrowser.Today,HTTPisusedtotransportawholelotmorethan
HTML.
Theimportantthingtonoticeinthisdiagramisthetwopointsofinteractionbetweenthe
ServerandtheBrowser.TheBrowsermakesanHTTPrequestwithsomeinformation,the
ServerthenprocessesthatrequestandreturnsaResponse.
Thenet/httppackage
7
BuildingWebAppswithGo
Thispatternofrequest-responseisoneofthekeyfocalpointsinbuildingwebapplicationsin
Go.Infact,the net/httppackage'smostimportantpieceisthe http.HandlerInterface.
Thehttp.HandlerInterface
AsyoubecomemorefamiliarwithGo,youwillnoticehowmuchofanimpactinterfaces
makeinthedesignofyourprograms.The net/httpinterfaceencapsulatestherequestresponsepatterninonemethod:
typeHandlerinterface{
ServeHTTP(ResponseWriter,*Request)
}
Implementorsofthisinterfaceareexpectedtoinspectandprocessdatacomingfromthe
http.Requestobjectandwriteoutaresponsetothe http.ResponseWriterobject.
The http.ResponseWriterinterfacelookslikethis:
typeResponseWriterinterface{
Header()Header
Write([]byte)(int,error)
WriteHeader(int)
}
ComposingWebServices
Becausemuchofthe net/httppackageisbuiltoffofwelldefinedinterfacetypes,wecan
(andareexpectedto)buildourwebapplicationswithcompositioninmind.Each
http.Handlerimplementationcanbethoughtofasitsownwebserver.
Manypatternscanbefoundinthatsimplebutpowerfulassumption.Throughoutthisbook
wewillcoversomeofthesepatternsandhowwecanusethemtosolverealworld
problems.
Exercise:1LineFileServer
Let'ssolvearealworldproblemin1lineofcode.
Thenet/httppackage
8
BuildingWebAppswithGo
Mostofthetimepeoplejustneedtoservestaticfiles.MaybeyouhaveastaticHTML
landingpageandjustwanttoserveupsomeHTML,images,andCSSandcallitaday.
Sure,youcouldpullinApacheorPython's SimpleHTTPServer,butApacheistoomuchfor
thislittlesiteand SimpleHTTPServeris,well,tooslow.
Wewillbeginbycreatinganewprojectinour GOPATH.
cdGOPATH/src
mkdirfileserver&&cdfileserver
Createamain.gowithourtypicalgoboilerplate.
packagemain
import"net/http"
funcmain(){
}
Allweneedtoimportisthe net/httppackageforthistowork.Rememberthatthisisall
partofthestandardlibraryinGo.
Let'swriteourfileservercode:
http.ListenAndServe(":8080",http.FileServer(http.Dir(".")))
The http.ListenAndServefunctionisusedtostarttheserver,itwillbindtotheaddresswe
gaveit( :8080)andwhenitreceivesanHTTPrequest,itwillhanditofftothe
http.Handlerthatwesupplyasthesecondargument.Inourcaseitisthebuilt-in
http.FileServer.
The http.FileServerfunctionbuildsan http.Handlerthatwillserveanentiredirectoryof
filesandfigureoutwhichfiletoservebasedontherequestpath.WetoldtheFileServerto
servethecurrentworkingdirectorywith http.Dir(".").
Theentireprogramlookslikethis:
packagemain
import"net/http"
funcmain(){
http.ListenAndServe(":8080",http.FileServer(http.Dir(".")))
}
Thenet/httppackage
9
BuildingWebAppswithGo
Let'sbuildandrunourfileserverprogram:
gobuild
./fileserver
Ifwevisit localhost:8080/main.goweshouldseethecontentsofourmain.gofileinourweb
browser.Wecanrunthisprogramfromanydirectoryandservethetreeasastaticfile
server.Allin1lineofGocode.
Thenet/httppackage
10
BuildingWebAppswithGo
CreatingaBasicWebApp
NowthatwearedonegoingoverthebasicsofHTTP,let'screateasimplebutusefulweb
applicationinGo.
Pullingfromourfileserverprogramthatweimplementedlastchapter,wewillimplementa
Markdowngeneratorusingthe github.com/russross/blackfridaypackage.
HTMLForm
Forstarters,wewillneedabasicHTMLformforthemarkdowninput:
<html>
<head>
<linkhref="/css/bootstrap.min.css"rel="stylesheet">
</head>
<body>
<divclass="container">
<divclass="page-title">
MarkdownGenerator
GenerateyourmarkdownwithGo
<hr/>
</div>
<formaction="/markdown"method="POST">
<divclass="form-group">
<textareaclass="form-control"name="body"cols="30"rows="10"></textarea>
</div>
<divclass="form-group">
<inputtype="submit"class="btnbtn-primarypull-right"/>
</div>
</form>
</div>
<scriptsrc="/js/bootstrap.min.js"></script>
</body>
</html>
PutthisHTMLintoafilenamed index.htmlinthe"public"folderofourapplicationandthe
bootstrap.min.cssfrominthe"public/css"folder.Noticethatthe
formmakesanHTTPPOSTtothe"/markdown"endpointofourapplication.Wedon't
actuallyhandlethatrouterightnow,solet'saddit.
CreatingaBasicWebApp
11
BuildingWebAppswithGo
The"/markdown"route
Theprogramtohandlethe'/markdown'routeandservethepublic index.htmlfilelookslike
this:
packagemain
import(
"net/http"
"github.com/russross/blackfriday"
)
funcmain(){
http.HandleFunc("/markdown",GenerateMarkdown)
http.Handle("/",http.FileServer(http.Dir("public")))
http.ListenAndServe(":8080",nil)
}
funcGenerateMarkdown(rwhttp.ResponseWriter,r*http.Request){
markdown:=blackfriday.MarkdownCommon([]byte(r.FormValue("body")))
rw.Write(markdown)
}
Let'sbreakitdownintosmallerpiecestogetabetterideaofwhatisgoingon.
http.HandleFunc("/markdown",GenerateMarkdown)
http.Handle("/",http.FileServer(http.Dir("public")))
Weareusingthe http.HandleFuncand http.Handlemethodstodefinesomesimple
routingforourapplication.Itisimportanttonotethatcalling http.Handleonthe"/"pattern
willactasacatch-allroute,sowedefinethatroutelast. http.FileServerreturnsan
http.Handlersoweuse http.Handletomapapatternstringtoahandler.Thealternative
method, http.HandleFunc,usesan http.HandlerFuncinsteadofan http.Handler.This
maybemoreconvenient,tothinkofhandlingroutesviaafunctioninsteadofanobject.
funcGenerateMarkdown(rwhttp.ResponseWriter,r*http.Request){
markdown:=blackfriday.MarkdownCommon([]byte(r.FormValue("body")))
rw.Write(markdown)
}
OurGenerateMarkdownfunctionimplementsthestandard http.HandlerFuncinterfaceand
rendersHTMLfromaformfieldcontainingmarkdown-formattedtext.Inthiscase,the
contentisretrievedwith r.FormValue("body").Itisverycommontogetinputfromthe
CreatingaBasicWebApp
12
BuildingWebAppswithGo
http.Requestobjectthatthe http.HandlerFuncreceivesasanargument.Someother
examplesofinputarethe r.Header, r.Body,and r.URLmembers.
Wefinalizetherequestbywritingitouttoour http.ResponseWriter.Noticethatwedidn't
explicitlysendaresponsecode.Ifwewriteouttotheresponsewithoutacode,the
net/httppackagewillassumethattheresponseisa 200OK.Thismeansthatif
somethingdidhappentogowrong,weshouldsettheresponsecodeviathe
rw.WriteHeader()method.
http.ListenAndServe(":8080",nil)
Thelastbitofthisprogramstartstheserver,wepass nilasourhandler,whichassumes
thattheHTTPrequestswillbehandledbythe net/httppackagesdefault http.ServeMux,
whichisconfiguredusing http.Handleand http.HandleFunc,respectively.
AndthatisallyouneedtobeabletogeneratemarkdownasaserviceinGo.Itisa
surprisinglysmallamountofcodefortheamountofheavyliftingitdoes.Inthenextchapter
wewilllearnhowtodeploythisapplicationtothewebusingHeroku.
CreatingaBasicWebApp
13
BuildingWebAppswithGo
Deployment
Herokumakesdeployingapplicationseasy.Itisaperfectplatformforsmalltomediumsize
webapplicationsthatarewillingtosacrificealittlebitofflexibilityininfrastructuretogaina
fairlypain-freeenvironmentfordeployingandmaintainingwebapplications.
IamchoosingtodeployourwebapplicationtoHerokuforthesakeofthistutorialbecausein
myexperienceithasbeenthefastestwaytogetawebapplicationupandrunninginno
time.RememberthatthefocusofthistutorialishowtobuildwebapplicationsinGoandnot
gettingcaughtupinallofthedistractionofprovisioning,configuring,deploying,and
maintainingthemachinesthatourGocodewillberunon.
Gettingsetup
Ifyoudon'talreadyhaveaHerokuaccount,signupatid.heroku.com/signup.It'squick,easy
andfree.
ApplicationmanagementandconfigurationisdonethroughtheHerokutoolbelt,whichisa
freecommandlinetoolmaintainedbyHeroku.Wewillbeusingittocreateourapplication
onHeroku.Youcangetitfromtoolbelt.heroku.com.
ChangingtheCode
TomakesuretheapplicationfromourlastchapterwillworkonHeroku,wewillneedtomake
afewchanges.Herokugivesusa PORTenvironmentvariableandexpectsourweb
applicationtobindtoit.Let'sstartbyimportingthe"os"packagesowecangrabthat PORT
environmentvariable:
import(
"net/http"
"os"
"github.com/russross/blackfriday"
)
Next,weneedtograbthe PORTenvironmentvariable,checkifitisset,andifitisweshould
bindtothatinsteadofourhardcodedport(8080).
Deployment
14
BuildingWebAppswithGo
port:=os.Getenv("PORT")
ifport==""{
port="8080"
}
Lastly,wewanttobindtothatportinour http.ListenAndServecall:
http.ListenAndServe(":"+port,nil)
Thefinalcodeshouldlooklikethis:
packagemain
import(
"net/http"
"os"
"github.com/russross/blackfriday"
)
funcmain(){
port:=os.Getenv("PORT")
ifport==""{
port="8080"
}
http.HandleFunc("/markdown",GenerateMarkdown)
http.Handle("/",http.FileServer(http.Dir("public")))
http.ListenAndServe(":"+port,nil)
}
funcGenerateMarkdown(rwhttp.ResponseWriter,r*http.Request){
markdown:=blackfriday.MarkdownCommon([]byte(r.FormValue("body")))
rw.Write(markdown)
}
Configuration
WeneedacouplesmallconfigurationfilestotellHerokuhowitshouldrunourapplication.
Thefirstoneisthe Procfile,whichallowsustodefinewhichprocessesshouldberunfor
ourapplication.Bydefault,Gowillnametheexecutableafterthecontainingdirectoryofyour
mainpackage.Forinstance,ifmywebapplicationlivedin
GOPATH/github.com/codegangsta/bwag/deployment,my Procfilewilllooklikethis:
Deployment
15
BuildingWebAppswithGo
web:deployment
SpecificallytorunGoapplications,weneedtoalsospecifya .godirfiletotellHeroku
whichdirisinfactourpackagedirectory.
deployment
Deployment
Onceallthesethingsinplace,Herokumakesiteasytodeploy.
InitializetheprojectasaGitrepository:
gitinit
gitadd-A
gitcommit-m"InitialCommit"
CreateyourHerokuapplication(specifyingtheGobuildpack):
herokucreate-b />
PushittoHerokuandwatchyourapplicationbedeployed!
gitpushherokumaster
Viewyourapplicationinyourbrowser:
herokuopen
Deployment
16
BuildingWebAppswithGo
URLRouting
Forsomesimpleapplications,thedefault http.ServeMuxcantakeyouprettyfar.Ifyouneed
morepowerinhowyouparseURLendpointsandroutethemtotheproperhandler,youmay
needtopullinathirdpartyroutingframework.Forthistutorial,wewillusethepopular
github.com/julienschmidt/httprouterlibraryasourrouter.
github.com/julienschmidt/httprouterisagreatchoiceforarouterasitisaverysimple
implementationwithoneofthebestperformancebenchmarksoutofallthethirdpartyGo
routers.
Inthisexample,wewillcreatesomeroutingforaRESTfulresourcecalled"posts".Belowwe
definemechanismstoviewindex,show,create,update,destroy,andeditposts.
packagemain
import(
"fmt"
"net/http"
"github.com/julienschmidt/httprouter"
)
funcmain(){
r:=httprouter.New()
r.GET("/",HomeHandler)
//Postscollection
r.GET("/posts",PostsIndexHandler)
r.POST("/posts",PostsCreateHandler)
//Postssingular
r.GET("/posts/:id",PostShowHandler)
r.PUT("/posts/:id",PostUpdateHandler)
r.GET("/posts/:id/edit",PostEditHandler)
fmt.Println("Startingserveron:8080")
http.ListenAndServe(":8080",r)
}
funcHomeHandler(rwhttp.ResponseWriter,r*http.Request,phttprouter.Params){
fmt.Fprintln(rw,"Home")
}
funcPostsIndexHandler(rwhttp.ResponseWriter,r*http.Request,phttprouter.Params){
fmt.Fprintln(rw,"postsindex")
}
URLRouting
17
BuildingWebAppswithGo
funcPostsCreateHandler(rwhttp.ResponseWriter,r*http.Request,phttprouter.Params){
fmt.Fprintln(rw,"postscreate")
}
funcPostShowHandler(rwhttp.ResponseWriter,r*http.Request,phttprouter.Params){
id:=p.ByName("id")
fmt.Fprintln(rw,"showingpost",id)
}
funcPostUpdateHandler(rwhttp.ResponseWriter,r*http.Request,phttprouter.Params){
fmt.Fprintln(rw,"postupdate")
}
funcPostDeleteHandler(rwhttp.ResponseWriter,r*http.Request,phttprouter.Params){
fmt.Fprintln(rw,"postdelete")
}
funcPostEditHandler(rwhttp.ResponseWriter,r*http.Request,phttprouter.Params){
fmt.Fprintln(rw,"postedit")
}
Exercises
1. Explorethedocumentationfor github.com/julienschmidt/httprouter.
2. Findouthowwell github.com/julienschmidt/httprouterplaysnicelywithexisting
http.Handlerslike http.FileServer
3.
httprouterhasaverysimpleinterface.Explorewhatkindofabstractionscanbebuilt
ontopofthisfastroutertomakebuildingthingslikeRESTfulroutingeasier.
URLRouting
18
BuildingWebAppswithGo
Middleware
Ifyouhavesomecodethatneedstoberunforeveryrequest,regardlessoftheroutethatit
willeventuallyendupinvoking,youneedsomewaytostack http.Handlersontopofeach
otherandruntheminsequence.Thisproblemissolvedelegantlythroughmiddleware
packages.Negroniisapopularmiddlewarepackagethatmakesbuildingandstacking
middlewareveryeasywhilekeepingthecomposablenatureoftheGowebecosystemintact.
NegronicomeswithsomedefaultmiddlewaresuchasLogging,ErrorRecovery,andStatic
fileserving.SooutoftheboxNegroniwillprovideyouwithalotofvaluewithoutalotof
overhead.
TheexamplebelowshowshowtouseaNegronistackwiththebuiltinmiddlewareandhow
tocreateyourowncustommiddleware.
Middleware
19
BuildingWebAppswithGo
packagemain
import(
"log"
"net/http"
"github.com/codegangsta/negroni"
)
funcmain(){
//Middlewarestack
n:=negroni.New(
negroni.NewRecovery(),
negroni.HandlerFunc(MyMiddleware),
negroni.NewLogger(),
negroni.NewStatic(http.Dir("public")),
)
n.Run(":8080")
}
funcMyMiddleware(rwhttp.ResponseWriter,r*http.Request,nexthttp.HandlerFunc){
log.Println("Loggingonthewaythere...")
ifr.URL.Query().Get("password")=="secret123"{
next(rw,r)
}else{
http.Error(rw,"NotAuthorized",401)
}
log.Println("Loggingonthewayback...")
}
Exercises
1. ThinkofsomecoolmiddlewareideasandtrytoimplementthemusingNegroni.
2. ExplorehowNegronicanbecomposedwith github.com/gorilla/muxusingthe
http.Handlerinterface.
3. PlaywithcreatingNegronistacksforcertaingroupsofroutesinsteadoftheentire
application.
Middleware
20
BuildingWebAppswithGo
Rendering
Renderingistheprocessoftakingdatafromyourapplicationordatabaseandpresentingit
fortheclient.TheclientcanbeabrowserthatrendersHTML,oritcanbeanother
applicationthatconsumesJSONasitsserializationformat.Inthischapterwewilllearnhow
torenderbothoftheseformatsusingthemethodsthatGoprovidesforusinthestandard
library.
Rendering
21
BuildingWebAppswithGo
JSON
JSONisquicklybecomingtheubiquitousserializationformatforwebAPIs,soitmaybethe
mostrelevantwhenlearninghowtobuildwebappsusingGo.Fortunately,Gomakesit
simpletoworkwithJSON--itisextremelyeasytoturnexistingGostructsintoJSONusing
the encoding/jsonpackagefromthestandardlibrary.
packagemain
import(
"encoding/json"
"net/http"
)
typeBookstruct{
Titlestring`json:"title"`
Authorstring`json:"author"`
}
funcmain(){
http.HandleFunc("/",ShowBooks)
http.ListenAndServe(":8080",nil)
}
funcShowBooks(whttp.ResponseWriter,r*http.Request){
book:=Book{"BuildingWebAppswithGo","JeremySaenz"}
js,err:=json.Marshal(book)
iferr!=nil{
http.Error(w,err.Error(),http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type","application/json")
w.Write(js)
}
Exercises
1. ReadthroughtheJSONAPIdocsandfindouthowtorenameandignorefieldsfor
JSONserialization.
2. Insteadofusingthe json.Marshalmethod,tryusingthe json.EncoderAPI.
3. FigureourhowtoprettyprintJSONwiththe encoding/jsonpackage.
JSON
22
BuildingWebAppswithGo
HTMLTemplates
ServingHTMLisanimportantjobforsomewebapplications.Gohasoneofmyfavorite
templatinglanguagestodate.Notforitsfeatures,butforitssimplicityandoutofthebox
security.RenderingHTMLtemplatesisalmostaseasyasrenderingJSONusingthe
'html/template'packagefromthestandardlibrary.Hereiswhatthesourcecodeforrendering
HTMLtemplateslookslike:
packagemain
import(
"html/template"
"net/http"
"path"
)
typeBookstruct{
Titlestring
Authorstring
}
funcmain(){
http.HandleFunc("/",ShowBooks)
http.ListenAndServe(":8080",nil)
}
funcShowBooks(whttp.ResponseWriter,r*http.Request){
book:=Book{"BuildingWebAppswithGo","JeremySaenz"}
fp:=path.Join("templates","index.html")
tmpl,err:=template.ParseFiles(fp)
iferr!=nil{
http.Error(w,err.Error(),http.StatusInternalServerError)
return
}
iferr:=tmpl.Execute(w,book);err!=nil{
http.Error(w,err.Error(),http.StatusInternalServerError)
}
}
Thisisthefollowingtemplatewewillbeusing.Itshouldbeplacedina
templates/index.htmlfileinthedirectoryyourprogramisrunfrom:
HTMLTemplates
23
BuildingWebAppswithGo
<html>
{{.Title}}
by{{.Author}}
</html>
Exercises
1. Lookthroughthedocsfor text/templateand html/templatepackage.Playwiththe
templatinglanguageabittogetafeelforitsgoals,strengths,andweaknesses.
2. Intheexampleweparsethefilesoneveryrequest,whichcanbealotofperformance
overhead.Experimentwithparsingthefilesatthebeginningofyourprogramand
executingtheminyour http.Handler(hint:makeuseofthe Copy()methodon
html.Template).
3. Experimentwithparsingandusingmultipletemplates.
HTMLTemplates
24
BuildingWebAppswithGo
Usingtherenderpackage
IfyouwantrenderingJSONandHTMLtobeevensimpler,thereisthe
github.com/unrolled/renderpackage.Thispackagewasinspiredbythe martinicontrib/renderpackageandismygotowhenitcomestorenderingdataforpresentationin
mywebapplications.
packagemain
import(
"net/http"
"gopkg.in/unrolled/render.v1"
)
funcmain(){
r:=render.New(render.Options{})
mux:=http.NewServeMux()
mux.HandleFunc("/",func(whttp.ResponseWriter,req*http.Request){
w.Write([]byte("Welcome,visitsubpagesnow."))
})
mux.HandleFunc("/data",func(whttp.ResponseWriter,req*http.Request){
r.Data(w,http.StatusOK,[]byte("Somebinarydatahere."))
})
mux.HandleFunc("/json",func(whttp.ResponseWriter,req*http.Request){
r.JSON(w,http.StatusOK,map[string]string{"hello":"json"})
})
mux.HandleFunc("/html",func(whttp.ResponseWriter,req*http.Request){
//Assumesyouhaveatemplatein./templatescalled"example.tmpl"
//$mkdir-ptemplates&&echo"Hello{{.}}.
">templates/example.tmpl
r.HTML(w,http.StatusOK,"example",nil)
})
http.ListenAndServe(":8080",mux)
}
Exercises
1. Havefunplayingwithalloftheoptionsavailablewhencalling render.New()
2. Tryusingthe .yieldhelperfunction(withthecurlybraces)andalayoutwithHTML
UsingTherenderpackage
25