1. Introduction2. GoEnvironmentConfiguration
i. Installationii. $GOPATHandworkspaceiii. Gocommandsiv. Godevelopmenttoolsv. Summary
3. Gobasicknowledgei. Hello,Goii. Gofoundationiii. Controlstatementsandfunctionsiv. structv. Object-orientedvi. interfacevii. Concurrencyviii. Summary
4. Webfoundationi. Webworkingprinciplesii. Buildasimplewebserveriii. HowGoworkswithwebiv. Getintohttppackagev. Summary
5. HTTPFormi. Processforminputsii. Validationofinputsiii. Crosssitescriptingiv. Duplicatesubmissionsv. Fileuploadvi. Summary
6. Databasei. database/sqlinterfaceii. HowtouseMySQLiii. HowtouseSQLiteiv. HowtousePostgreSQLv. HowtousebeedbORMvi. NOSQLvii. Summary
7. Datastorageandsessioni. Sessionandcookiesii. HowtousesessioninGoiii. Sessionstorageiv. Preventhijackofsessionv. Summary
8. Textfilesi. XMLii. JSONiii. Regexpiv. Templatesv. Filesvi. Stringsvii. Summary
9. Webservices
TableofContents
i. Socketsii. WebSocketiii. RESTiv. RPCv. Summary
10. Securityandencryptioni. CSRFattacksii. Filterinputsiii. XSSattacksiv. SQLinjectionv. Passwordstoragevi. Encryptanddecryptdatavii. Summary
11. Internationalizationandlocalizationi. Timezoneii. Localizedresourcesiii. Internationalsitesiv. Summary
12. Errorhandling,debuggingandtestingi. Errorhandlingii. DebuggingbyusingGDBiii. Writetestcasesiv. Summary
13. Deploymentandmaintenancei. Logsii. Errorsandcrashesiii. Deploymentiv. Backupandrecoveryv. Summary
14. Buildawebframeworki. Projectprogramii. Customizedroutersiii. Designcontrollersiv. Logsandconfigurationsv. Add,deleteandupdateblogsvi. Summary
15. Developwebframeworki. Staticfilesii. Sessioniii. Formiv. Uservalidationv. Multi-languagesupportvi. pprofvii. Summary
16. References17. preface
BecauseI'minterestedinwebapplicationdevelopment,Iusedmyfreetimetowritethisbookasanopensourceversion.Itdoesn'tmeanthatIhaveaverygoodabilitytobuildwebapplications;IwouldliketosharewhatI'vedonewithGoinbuildingwebapplications.
ForthoseofyouwhoareworkingwithPHP/Python/Ruby,youwilllearnhowtobuildawebapplicationwithGo.ForthoseofyouwhoareworkingwithC/C++,youwillknowhowthewebworks.
Ibelievethepurposeofstudyingissharingwithothers.ThehappiestthinginmylifeissharingeverythingI'veknownwithmorepeople.
AliPay:
alipay
EnglishDonate:donate
QQ386056972
BBShttp://golanghome.com/
AprilCitizen(reviewcode)HongRuiqi(reviewcode)BianJiang(writetheconfigurationsaboutVimandEmacsforGodevelopment)OlingCat(reviewcode)WenleiWu(providesomepictures)Polaris(reviewwholebook)RainTrail(reviewchapter2and3)
ThisbookislicensedundertheCCBY-SA3.0License,thecodeislicensedunderaBSD3-ClauseLicense,unlessotherwisespecified.
BuildWebApplicationwithGolang
Purpose
Donate
Community
Acknowledgments
License
WelcometotheworldofGo,let'sstartexploring!
Goisafast-compiled,garbage-collected,concurrentsystemsprogramminglanguage.Ithasthefollowingadvantages:
Compilesalargeprojectwithinafewseconds.Providesasoftwaredevelopmentmodelthatiseasytoreasonabout,avoidingmostoftheproblemsassociatedwithC-styleheaderfiles.Isastaticlanguagethatdoesnothavelevelsinitstypesystem,sousersdonotneedtospendmuchtimedealingwithrelationsbetweentypes.Itismorelikealightweightobject-orientedlanguage.Performsgarbagecollection.Itprovidesbasicsupportforconcurrencyandcommunication.Designedformulti-corecomputers.
Goisacompiledlanguage.Itcombinesthedevelopmentefficiencyofinterpretedordynamiclanguageswiththesecurityofstaticlanguages.Itisgoingtobethelanguageofchoiceformodern,multi-corecomputerswithnetworking.Forthesepurposes,therearesomeproblemsthatneedtoinherentlyberesolvedatthelevelofthelanguageofchoice,suchasarichlyexpressivelightweighttypesystem,anativeconcurrencymodel,andstrictlyregulatedgarbagecollection.Forquitesometime,nopackagesortoolshaveemergedthathaveaimedtosolvealloftheseproblemsinapragmaticfashion;thuswasbornthemotivationfortheGolanguage.
Inthischapter,IwillshowyouhowtoinstallandconfigureyourownGodevelopmentenvironment.
DirectoryNextsection:Installation
1GoEnvironmentConfiguration
Links
TherearemanywaystoconfiguretheGodevelopmentenvironmentonyourcomputer,andyoucanchoosewhicheveroneyoulike.Thethreemostcommonwaysareasfollows.
Officialinstallationpackages.TheGoteamprovidesconvenientinstallationpackagesinWindows,Linux,Macandotheroperatingsystems.Thisisprobablytheeasiestwaytogetstarted.
Installityourselffromsourcecode.PopularwithdeveloperswhoarefamiliarwithUnix-likesystems.
Usingthird-partytools.Therearemanythird-partytoolsandpackagemanagersforinstallingGo,likeapt-getinUbuntuandhomebrewforMac.
IncaseyouwanttoinstallmorethanoneversionofGoonacomputer,youshouldtakealookatatoolcalledGVM.ItisthebesttoolI'veseensofarforaccomplishingthistask,otherwiseyou'dhavetodealwithityourself.
BecausesomepartsofGoarewritteninPlan9CandAT&Tassembler,youhavetoinstallaCcompilerbeforetakingthenextstep.
OnaMac,ifyouhaveinstalledXcode,youalreadyhavethecompiler.
OnUnix-likesystems,youneedtoinstallgccorasimilarcompiler.Forexample,usingthepackagemanagerapt-get(includedwithUbuntu),onecaninstalltherequiredcompilersasfollows:
sudoapt-getinstallgcclibc6-dev
OnWindows,youneedtoinstallMinGWinordertoinstallgcc.Don'tforgettoconfigureyourenvironmentvariablesaftertheinstallationhascompleted.(Everythingthatlookslikethismeansit'scommentedbyatranslator:Ifyouareusing64-bitWindows,youshouldinstallthe64-bitversionofMinGW)
TheGoteamusesMercurialtomanagetheirsourcecode,soyouneedtoinstallthistoolinordertodownloadtheGosourcecode.
Atthispoint,executethefollowingcommandstoclonetheGosourcecodeandcompileit.(Itwillclonethesourcecodetoyourcurrentdirectory.Switchyourworkpathbeforeyoucontinue.Thismaytakesometime.)
hgclone-ureleasehttps://code.google.com/p/gocdgo/src./all.bash
Asuccessfulinstallationwillendwiththemessage"ALLTESTSPASSED."
OnWindows,youcanachievethesamebyrunningall.bat.
IfyouareusingWindows,theinstallationpackagewillsetyourenvironmentvariablesautomatically.InUnix-likesystems,youneedtosetthesevariablesmanuallyasfollows.(IfyourGoversionisgreaterthan1.0,youdon'thavetoset$GOBIN,anditwillautomaticallyberelatedtoyour$GOROOT/bin,whichwewilltalkaboutinthenextsection)
1.1Installation
ThreewaystoinstallGo
Installfromsourcecode
exportGOROOT=$HOME/goexportGOBIN=$GOROOT/binexportPATH=$PATH:$GOROOT/bin
Ifyouseethefollowinginformationonyourscreen,you'reallset.
Figure1.1Informationafterinstallingfromsourcecode
OnceyouseetheusageinformationofGo,itmeansyouhavesuccessfullyinstalledGoonyourcomputer.Ifitsays"nosuchcommand",checkthatyour$PATHenvironmentvariablecontainstheinstallationpathofGo.
Gohasone-clickinstallationpackagesforeverysupportedoperatingsystem.ThesepackageswillinstallGoin/usr/local/go(c:\GoinWindows)bydefault.Ofcoursethiscanbemodified,butyoualsoneedtochangealltheenvironmentvariablesmanuallyasI'veshownabove.
Ournextstepdependsonyouroperatingsystemtype,sowehavetocheckitbeforewedownloadthestandardinstallationpackages.
IfyouareusingWindows,pressWin+Randthenrunthecommandtool.Typethesysteminfocommandanditwillshowyousomeusefulsysteminformation.Findthelinethatsays"systemtype"-ifyousee"x64-basedPC"thatmeansyouroperatingsystemis64-bit,32-bitotherwise.
Istronglyrecommenddownloadingthe64-bitpackageifyouareaMacuser,asGonolongersupportspure32-bitprocessorsonMacOSX.
Linuxuserscantypeuname-aintheterminaltoseesysteminformation.A64-bitoperatingsystemwillshowthefollowing:
x86_64x86_64x86_64GNU/Linux//somemachinessuchasUbuntu10.04willshowasfollowingx86_64GNU/Linux
32-bitoperatingsystemsinsteadshow:
i686i686i386GNU/Linux
Gotothedownloadpage,choosego1.0.3.darwin-386.pkgfor32-bitsystemsandgo1.0.3.darwin-amd64.pkgfor64-bitsystems.Goingallthewaytotheendbyclicking"next",~/go/binwillbeaddedtoyoursystem's$PATHafteryoufinishtheinstallation.Nowopentheterminalandtypego.Youshouldseethesameoutputshowninigure1.1.
Gotothedownloadpage,choosego1.0.3.linux-386.tar.gzfor32-bitsystemsandgo1.0.3.linux-amd64.tar.gzfor64-bitsystems.SupposeyouwanttoinstallGointhe$GO_INSTALL_DIRpath.Uncompressthetar.gztoyourchosenpathusingthecommandtarzxvfgo1.0.3.linux-amd64.tar.gz-C$GO_INSTALL_DIR.Thensetyour$PATHwiththefollowing:exportPATH=$PATH:$GO_INSTALL_DIR/go/bin.Nowjustopentheterminalandtypego.Youshouldnowseethesameoutputdisplayedinfigure1.1.
Usingthestandardinstallationpackages
Howtocheckifyouroperatingsystemis32-bitor64-bit?
Mac
Linux
Gotothedownloadpage,choosego1.0.3.windows-386.msifor32-bitsystemsandgo1.0.3.windows-amd64.msifor64-bitsystems.Goingallthewaytotheendbyclicking"next",c:/go/binwillbeaddedtopath.Nowjustopenacommandlinewindowandtypego.Youshouldnowseethesameoutputdisplayedinfigure1.1.
GVMisaGomulti-versioncontroltooldevelopedbyathird-party,likervmforruby.It'squiteeasytouse.Installgvmbytypingthefollowingcommandsinyourterminal:
bash10{fmt.Println("xisgreaterthan10")}else{fmt.Println("xislessthan10")}
ThemostusefulthingconcerningifinGoisthatitcanhaveoneinitializationstatementbeforetheconditionalstatement.Thescopeofthevariablesdefinedinthisinitializationstatementareonlyavailableinsidetheblockofthedefiningif.
//initializex,thencheckifxgreaterthanifx:=computedValue();x>10{fmt.Println("xisgreaterthan10")}else{fmt.Println("xislessthan10")}
//thefollowingcodewillnotcompilefmt.Println(x)
Useif-elseformultipleconditions.
ifinteger==3{fmt.Println("Theintegerisequalto3")}elseifintegerp2.age{returnp1,p1.age-p2.age}returnp2,p2.age-p1.age}
funcmain(){vartomperson
//initializationtom.name,tom.age="Tom",18
//initializetwovaluesbyformat"field:value"bob:=person{age:25,name:"Bob"}
//initializetwovalueswithorderpaul:=person{"Paul",43}
tb_Older,tb_diff:=Older(tom,bob)tp_Older,tp_diff:=Older(tom,paul)bp_Older,bp_diff:=Older(bob,paul)
fmt.Printf("Of%sand%s,%sisolderby%dyears\n",tom.name,bob.name,tb_Older.name,tb_diff)
fmt.Printf("Of%sand%s,%sisolderby%dyears\n",tom.name,paul.name,tp_Older.name,tp_diff)
fmt.Printf("Of%sand%s,%sisolderby%dyears\n",bob.name,paul.name,bp_Older.name,bp_diff)}
I'vejustintroducedtoyouhowtodefineastructwithfieldnamesandtype.Infact,Gosupportsfieldswithoutnames,butwithtypes.Wecalltheseembeddedfields.
Whentheembeddedfieldisastruct,allthefieldsinthatstructwillimplicitlybethefieldsinthestructinwhichithasbeenembdedded.
Let'sseeoneexample.
packagemainimport"fmt"
typeHumanstruct{namestringageintweightint}
typeStudentstruct{Human//embeddedfield,itmeansStudentstructincludesallfieldsthatHumanhas.specialitystring}
funcmain(){//initializeastudentmark:=Student{Human{"Mark",25,120},"ComputerScience"}
//accessfieldsfmt.Println("Hisnameis",mark.name)fmt.Println("Hisageis",mark.age)fmt.Println("Hisweightis",mark.weight)fmt.Println("Hisspecialityis",mark.speciality)//modifynotesmark.speciality="AI"fmt.Println("Markchangedhisspeciality")fmt.Println("Hisspecialityis",mark.speciality)//modifyage
embeddedfieldsinstruct
fmt.Println("Markbecomeold")mark.age=46fmt.Println("Hisageis",mark.age)//modifyweightfmt.Println("Markisnotanathletanymore")mark.weight+=60fmt.Println("Hisweightis",mark.weight)}
Figure2.7InheritanceinStudentandHuman
WeseethatwecanaccesstheageandnamefieldsinStudentjustlikewecaninHuman.Thisishowembeddedfieldswork.It'sverycool,isn'tit?Holdon,there'ssomethingcooler!YoucanevenuseStudenttoaccessHumaninthisembeddedfield!
mark.Human=Human{"Marcus",55,220}mark.Human.age-=1
AllthetypesinGocanbeusedasembeddedfields.
packagemainimport"fmt"
typeSkills[]string
typeHumanstruct{namestringageintweightint}
typeStudentstruct{Human//structasembeddedfieldSkills//stringsliceasembeddedfieldint//built-intypeasembeddedfieldspecialitystring}
funcmain(){//initializeStudentJanejane:=Student{Human:Human{"Jane",35,100},speciality:"Biology"}//accessfieldsfmt.Println("Hernameis",jane.name)fmt.Println("Herageis",jane.age)fmt.Println("Herweightis",jane.weight)fmt.Println("Herspecialityis",jane.speciality)//modifyvalueofskillfieldjane.Skills=[]string{"anatomy"}fmt.Println("Herskillsare",jane.Skills)fmt.Println("Sheacquiredtwonewones")jane.Skills=append(jane.Skills,"physics","golang")fmt.Println("Herskillsnoware",jane.Skills)//modifyembeddedfieldjane.int=3fmt.Println("Herpreferrednumberis",jane.int)}
Intheaboveexample,wecanseethatalltypescanbeembeddedfieldsandwecanusefunctionstooperateonthem.
Thereisonemoreproblemhowever.IfHumanhasafieldcalledphoneandStudenthasafieldwithsamename,whatshouldwedo?
Gouseaverysimplewaytosolveit.Theouterfieldsgetupperaccesslevels,whichmeanswhenyouaccessstudent.phone,wewillgetthefieldcalledphoneinstudent,nottheoneintheHumanstruct.Thisfeaturecanbesimplyseenasfieldoverloading.
packagemainimport"fmt"
typeHumanstruct{namestringageintphonestring//Humanhasphonefield}
typeEmployeestruct{Human//embeddedfieldHumanspecialitystringphonestring//phoneinemployee}
funcmain(){Bob:=Employee{Human{"Bob",34,"777-444-XXXX"},"Designer","333-222"}fmt.Println("Bob'sworkphoneis:",Bob.phone)//accessphonefieldinHumanfmt.Println("Bob'spersonalphoneis:",Bob.Human.phone)}
DirectoryPrevioussection:ControlstatementsandfunctionsNextsection:Object-oriented
Links
Wetalkedaboutfunctionsandstructsinthelasttwosections,butdidyoueverconsiderusingfunctionsasfieldsofastruct?Inthissection,Iwillintroduceyoutoanotherformofmethodthathasareceiver,whichiscalledmethod.
Supposeyoudefinea"rectangle"structandyouwanttocalculateitsarea.We'dtypicallyusethefollowingcodetoachievethisgoal.
packagemainimport"fmt"
typeRectanglestruct{width,heightfloat64}
funcarea(rRectangle)float64{returnr.width*r.height}
funcmain(){r1:=Rectangle{12,2}r2:=Rectangle{9,4}fmt.Println("Areaofr1is:",area(r1))fmt.Println("Areaofr2is:",area(r2))}
Theaboveexamplecancalculatearectangle'sarea.Weusethefunctioncalledarea,butit'snotamethodoftherectanglestruct(likeclassmethodsinclassicobject-orientedlanguages).Thefunctionandstructaretwoindependentthingsasyoumaynotice.
It'snotaproblemsofar.However,ifyoualsohavetocalculatetheareaofacircle,square,pentagon,oranyotherkindofshape,youaregoingtoneedtoaddadditionalfunctionswithverysimilarnames.
Figure2.8Relationshipbetweenfunctionandstruct
Obviouslythat'snotcool.Also,theareashouldreallybethepropertyofacircleorrectangle.
Forthosereasons,wehavethemethodconcept.methodisaffiliatedwithtype.Ithasthesamesyntaxasfunctionsdoexceptforanadditionalparameterafterthefunckeywordcalledthereceiver,whichisthemainbodyofthatmethod.
Usingthesameexample,Rectangle.area()belongsdirectlytorectangle,insteadofasaperipheralfunction.Morespecifically,length,widthandarea()allbelongtorectangle.
AsRobPikesaid.
"Amethodisafunctionwithanimplicitfirstargument,calledareceiver."
Syntaxofmethod.
func(rReceiverType)funcName(parameters)(results)
Let'schangeourexampleusingmethodinstead.
Object-oriented
method
packagemainimport("fmt""math")
typeRectanglestruct{width,heightfloat64}
typeCirclestruct{radiusfloat64}
func(rRectangle)area()float64{returnr.width*r.height}
func(cCircle)area()float64{returnc.radius*c.radius*math.Pi}
funcmain(){r1:=Rectangle{12,2}r2:=Rectangle{9,4}c1:=Circle{10}c2:=Circle{25}
fmt.Println("Areaofr1is:",r1.area())fmt.Println("Areaofr2is:",r2.area())fmt.Println("Areaofc1is:",c1.area())fmt.Println("Areaofc2is:",c2.area())}
Notesforusingmethods.
Ifthenameofmethodsarethesamebuttheydon'tsharethesamereceivers,theyarenotthesame.Methodsareabletoaccessfieldswithinreceivers.Use.tocallamethodinthestruct,thesamewayfieldsarecalled.
Figure2.9Methodsaredifferentindifferentstructs
Intheexampleabove,thearea()methodsbelongtobothRectangleandCirclerespectively,sothereceiversareRectangleandCircle.
Onethingthat'sworthnotingisthatthemethodwithadottedlinemeansthereceiverispassedbyvalue,notbyreference.Thedifferencebetweenthemisthatamethodcanchangeitsreceiver'svalueswhenthereceiverispassedbyreference,anditgetsacopyofthereceiverwhenthereceiverispassedbyvalue.
Canthereceiveronlybeastruct?Ofcoursenot.Anytypecanbethereceiverofamethod.Youmaybeconfusedaboutcustomizedtypes.Structisaspecialkindofcustomizedtype-therearemorecustomizedtypes.
Usethefollowingformattodefineacustomizedtype.
typetypeNametypeLiteral
Examplesofcustomizedtypes:
typeagesint
typemoneyfloat32
typemonthsmap[string]int
m:=months{"January":31,"February":28,..."December":31,}
Ihopethatyouknowhowtousecustomizedtypesnow.SimilartotypedefinC,weuseagestosubstituteintintheaboveexample.
Let'sgetbacktotalkingaboutmethod.
Youcanuseasmanymethodsincustomtypesasyouwant.
packagemainimport"fmt"
const(WHITE=iotaBLACKBLUEREDYELLOW)
typeColorbyte
typeBoxstruct{width,height,depthfloat64colorColor}
typeBoxList[]Box//asliceofboxes
func(bBox)Volume()float64{returnb.width*b.height*b.depth}
func(b*Box)SetColor(cColor){b.color=c}
func(blBoxList)BiggestsColor()Color{v:=0.00k:=Color(WHITE)for_,b:=rangebl{ifb.Volume()>v{v=b.Volume()k=b.color}}returnk}
func(blBoxList)PaintItBlack(){fori,_:=rangebl{bl[i].SetColor(BLACK)}}
func(cColor)String()string{strings:=[]string{"WHITE","BLACK","BLUE","RED","YELLOW"}returnstrings[c]}
funcmain(){boxes:=BoxList{Box{4,4,4,RED},Box{10,10,1,YELLOW},Box{1,1,20,BLACK},Box{10,10,1,BLUE},Box{10,30,1,WHITE},Box{20,20,20,YELLOW},}
fmt.Printf("Wehave%dboxesinourset\n",len(boxes))fmt.Println("Thevolumeofthefirstoneis",boxes[0].Volume(),"cm")
fmt.Println("Thecolorofthelastoneis",boxes[len(boxes)-1].color.String())fmt.Println("Thebiggestoneis",boxes.BiggestsColor().String())
fmt.Println("Let'spaintthemallblack")boxes.PaintItBlack()fmt.Println("Thecolorofthesecondoneis",boxes[1].color.String())
fmt.Println("Obviously,now,thebiggestoneis",boxes.BiggestsColor().String())}
Wedefinesomeconstantsandcustomizedtypes.
UseColorasaliasofbyte.DefineastructBoxwhichhasfieldsheight,width,lengthandcolor.DefineastructBoxListwhichhasBoxasitsfield.
Thenwedefinedsomemethodsforourcustomizedtypes.
Volume()usesBoxasitsreceiverandreturnsvolumeofBox.SetColor(cColor)changesBox'scolor.BiggestsColor()returnsthecolorwhichhasthebiggestvolume.PaintItBlack()setscolorforallBoxinBoxListtoblack.String()useColorasitsreceiver,returnsthestringformatofcolorname.
Isitmuchclearerwhenweusewordstodescribeourrequirements?Weoftenwriteourrequirementsbeforewestartcoding.
Let'stakealookatSetColormethod.ItsreceiverisapointerofBox.Yes,youcanuse*Boxasareceiver.Whydoweuseapointerhere?BecausewewanttochangeBox'scolorinthismethod.Thus,ifwedon'tuseapointer,itwillonlychangethevalueinsideacopyofBox.
Ifweseethatareceiveristhefirstargumentofamethod,it'snothardtounderstandhowitworks.
Youmightbeaskingwhywearen'tusing(*b).Color=cinsteadofb.Color=cintheSetColor()method.EitheroneisOKherebecauseGoknowshowtointerprettheassignment.DoyouthinkGoismorefascinatingnow?
Youmayalsobeaskingwhetherweshoulduse(&bl[i]).SetColor(BLACK)inPaintItBlackbecausewepassapointertoSetColor.Again,eitheroneisOKbecauseGoknowshowtointerpretit!
Welearnedaboutinheritanceoffieldsinthelastsection.Similarly,wealsohavemethodinheritanceinGo.Ifananonymousfieldhasmethods,thenthestructthatcontainsthefieldwillhaveallthemethodsfromitaswell.
packagemainimport"fmt"
typeHumanstruct{namestringageintphonestring}
typeStudentstruct{Human//anonymousfieldschoolstring}
typeEmployeestruct{Humancompanystring}
Usepointerasreceiver
Inheritanceofmethod
//defineamethodinHumanfunc(h*Human)SayHi(){fmt.Printf("Hi,Iam%syoucancallmeon%s\n",h.name,h.phone)}
funcmain(){mark:=Student{Human{"Mark",25,"222-222-YYYY"},"MIT"}sam:=Employee{Human{"Sam",45,"111-888-XXXX"},"GolangInc"}
mark.SayHi()sam.SayHi()}
IfwewantEmployeetohaveitsownmethodSayHi,wecandefineamethodthathasthesamenameinEmployee,anditwillhideSayHiinHumanwhenwecallit.
packagemainimport"fmt"
typeHumanstruct{namestringageintphonestring}
typeStudentstruct{Humanschoolstring}
typeEmployeestruct{Humancompanystring}
func(h*Human)SayHi(){fmt.Printf("Hi,Iam%syoucancallmeon%s\n",h.name,h.phone)}
func(e*Employee)SayHi(){fmt.Printf("Hi,Iam%s,Iworkat%s.Callmeon%s\n",e.name,e.company,e.phone)//Yesyoucansplitinto2lineshere.}
funcmain(){mark:=Student{Human{"Mark",25,"222-222-YYYY"},"MIT"}sam:=Employee{Human{"Sam",45,"111-888-XXXX"},"GolangInc"}
mark.SayHi()sam.SayHi()}
YouareabletowriteanObject-orientedprogramnow,andmethodsuseruleofcapitallettertodecidewhetherpublicorprivateaswell.
DirectoryPrevioussection:structNextsection:interface
Methodoverload
Links
OneofthesubtlestdesignfeaturesinGoareinterfaces.Afterreadingthissection,youwilllikelybeimpressedbytheirimplementation.
Inshort,aninterfaceisasetofmethodsthatweusetodefineasetofactions.
Liketheexamplesinprevioussections,bothStudentandEmployeecanSayHi(),buttheydon'tdothesamething.
Let'sdosomemorework.We'lladdonemoremethodSing()tothem,alongwiththeBorrowMoney()methodtoStudentandtheSpendSalary()methodtoEmployee.
Now,StudenthasthreemethodscalledSayHi(),Sing()andBorrowMoney(),andEmployeehasSayHi(),Sing()andSpendSalary().
ThiscombinationofmethodsiscalledaninterfaceandisimplementedbybothStudentandEmployee.So,StudentandEmployeeimplementtheinterface:SayHi()andSing().Atthesametime,Employeedoesn'timplementtheinterface:SayHi(),Sing(),BorrowMoney(),andStudentdoesn'timplementtheinterface:SayHi(),Sing(),SpendSalary().ThisisbecauseEmployeedoesn'thavethemethodBorrowMoney()andStudentdoesn'thavethemethodSpendSalary().
Aninterfacedefinesasetofmethods,soifatypeimplementsallthemethodswesaythatitimplementstheinterface.
typeHumanstruct{namestringageintphonestring}
typeStudentstruct{Humanschoolstringloanfloat32}
typeEmployeestruct{Humancompanystringmoneyfloat32}
func(h*Human)SayHi(){fmt.Printf("Hi,Iam%syoucancallmeon%s\n",h.name,h.phone)}
func(h*Human)Sing(lyricsstring){fmt.Println("Lala,lalala,lalalalala...",lyrics)}
func(h*Human)Guzzle(beerSteinstring){fmt.Println("GuzzleGuzzleGuzzle...",beerStein)}
//EmployeeoverloadsSayhifunc(e*Employee)SayHi(){fmt.Printf("Hi,Iam%s,Iworkat%s.Callmeon%s\n",e.name,e.company,e.phone)//Yesyoucansplitinto2lineshere.}
func(s*Student)BorrowMoney(amountfloat32){
2.6Interface
Interface
Whatisaninterface
TypeofInterface
s.loan+=amount//(againandagainand...)}
func(e*Employee)SpendSalary(amountfloat32){e.money-=amount//Morevodkaplease!!!Getmethroughtheday!}
//defineinterfacetypeMeninterface{SayHi()Sing(lyricsstring)Guzzle(beerSteinstring)}
typeYoungChapinterface{SayHi()Sing(songstring)BorrowMoney(amountfloat32)}
typeElderlyGentinterface{SayHi()Sing(songstring)SpendSalary(amountfloat32)}
Weknowthataninterfacecanbeimplementedbyanytype,andonetypecanimplementmanyinterfacessimultaneously.
Notethatanytypeimplementstheemptyinterfaceinterface{}becauseitdoesn'thaveanymethodsandalltypeshavezeromethodsbydefault.
Sowhatkindofvaluescanbeputintheinterface?Ifwedefineavariableasatypeinterface,anytypethatimplementstheinterfacecanassignedtothisvariable.
Liketheaboveexample,ifwedefineavariable"m"asinterfaceMen,thenanyoneofStudent,HumanorEmployeecanbeassignedto"m".SowecouldhaveasliceofMen,andanytypethatimplementsinterfaceMencanassigntothisslice.Beawarehoweverthatthesliceofinterfacedoesn'thavethesamebehaviorasasliceofothertypes.
packagemain
import"fmt"
typeHumanstruct{namestringageintphonestring}
typeStudentstruct{Humanschoolstringloanfloat32}
typeEmployeestruct{Humancompanystringmoneyfloat32}
func(hHuman)SayHi(){fmt.Printf("Hi,Iam%syoucancallmeon%s\n",h.name,h.phone)}
func(hHuman)Sing(lyricsstring){fmt.Println("Lalalala...",lyrics)}
func(eEmployee)SayHi(){fmt.Printf("Hi,Iam%s,Iworkat%s.Callmeon%s\n",e.name,e.company,e.phone)//Yesyoucansplitinto2lineshere.
Valueofinterface
}//InterfaceMenimplementedbyHuman,StudentandEmployeetypeMeninterface{SayHi()Sing(lyricsstring)}
funcmain(){mike:=Student{Human{"Mike",25,"222-222-XXX"},"MIT",0.00}paul:=Student{Human{"Paul",26,"111-222-XXX"},"Harvard",100}sam:=Employee{Human{"Sam",36,"444-222-XXX"},"GolangInc.",1000}Tom:=Employee{Human{"Sam",36,"444-222-XXX"},"ThingsLtd.",5000}
//defineinterfaceivariMen
//icanstoreStudenti=mikefmt.Println("ThisisMike,aStudent:")i.SayHi()i.Sing("Novemberrain")
//icanstoreEmployeei=Tomfmt.Println("ThisisTom,anEmployee:")i.SayHi()i.Sing("Borntobewild")
//sliceofMenfmt.Println("Let'suseasliceofMenandseewhathappens")x:=make([]Men,3)//thesethreeelementsaredifferenttypesbuttheyallimplementedinterfaceMenx[0],x[1],x[2]=paul,sam,mike
for_,value:=rangex{value.SayHi()}}
Aninterfaceisasetofabstractmethods,andcanbeimplementedbynon-interfacetypes.Itcannotthereforeimplementitself.
Anemptyinterfaceisaninterfacethatdoesn'tcontainanymethods,soalltypesimplementanemptyinterface.Thisfactisveryusefulwhenwewanttostorealltypesatsomepoint,andissimilartovoid*inC.
//defineaasemptyinterfacevarainterface{}variint=5s:="Helloworld"//acanstorevalueofanytypea=ia=s
Ifafunctionusesanemptyinterfaceasitsargumenttype,itcanacceptanytype;ifafunctionusesemptyasitsreturnvaluetype,itcanreturnanytype.
Anyvariablecanbeusedinaninterface.Sohowcanweusethisfeaturetopassanytypeofvariabletoafunction?
Forexampleweusefmt.Printlnalot,buthaveyouevernoticedthatitcanacceptanytypeofargument?Lookingattheopensourcecodeoffmt,weseethefollowingdefinition.
typeStringerinterface{String()string}
Emptyinterface
Methodargumentsofaninterface
ThismeansanytypethatimplementsinterfaceStringercanbepassedtofmt.Printlnasanargument.Let'sproveit.
packagemain
import("fmt""strconv")
typeHumanstruct{namestringageintphonestring}
//Humanimplementedfmt.Stringerfunc(hHuman)String()string{return"Name:"+h.name+",Age:"+strconv.Itoa(h.age)+"years,Contact:"+h.phone}
funcmain(){Bob:=Human{"Bob",39,"000-7777-XXX"}fmt.Println("ThisHumanis:",Bob)}
LookingbacktotheexampleofBox,youwillfindthatColorimplementsinterfaceStringeraswell,soweareabletocustomizetheprintformat.Ifwedon'timplementthisinterface,fmt.Printlnprintsthetypewithitsdefaultformat.
fmt.Println("Thebiggestoneis",boxes.BiggestsColor().String())fmt.Println("Thebiggestoneis",boxes.BiggestsColor())
Attention:Ifthetypeimplementedtheinterfaceerror,fmtwillcallerror(),soyoudon'thavetoimplementStringeratthispoint.
Ifavariableisthetypethatimplementsaninterface,weknowthatanyothertypethatimplementsthesameinterfacecanbeassignedtothisvariable.Thequestionishowcanweknowthespecifictypestoredintheinterface.TherearetwowayswhichIwillshowyou.
AssertionofComma-okpattern
Gohasthesyntaxvalue,ok:=element.(T).Thischeckstoseeifthevariableisthetypethatweexpect,where"value"isthevalueofthevariable,"ok"isavariableofbooleantype,"element"istheinterfacevariableandtheTisthetypeofassertion.
Iftheelementisthetypethatweexpect,okwillbetrue,falseotherwise.
Let'suseanexampletoseemoreclearly.
packagemain
import("fmt""strconv")
typeElementinterface{}typeList[]Element
typePersonstruct{namestringageint}
Typeofvariableinaninterface
func(pPerson)String()string{return"(name:"+p.name+"-age:"+strconv.Itoa(p.age)+"years)"}
funcmain(){list:=make(List,3)list[0]=1//anintlist[1]="Hello"//astringlist[2]=Person{"Dennis",70}
forindex,element:=rangelist{ifvalue,ok:=element.(int);ok{fmt.Printf("list[%d]isanintanditsvalueis%d\n",index,value)}elseifvalue,ok:=element.(string);ok{fmt.Printf("list[%d]isastringanditsvalueis%s\n",index,value)}elseifvalue,ok:=element.(Person);ok{fmt.Printf("list[%d]isaPersonanditsvalueis%s\n",index,value)}else{fmt.Printf("list[%d]isofadifferenttype\n",index)}}}
It'squiteeasytousethispattern,butifwehavemanytypestotest,we'dbetteruseswitch.
switchtest
Let'suseswitchtorewritetheaboveexample.
packagemain
import("fmt""strconv")
typeElementinterface{}typeList[]Element
typePersonstruct{namestringageint}
func(pPerson)String()string{return"(name:"+p.name+"-age:"+strconv.Itoa(p.age)+"years)"}
funcmain(){list:=make(List,3)list[0]=1//anintlist[1]="Hello"//astringlist[2]=Person{"Dennis",70}
forindex,element:=rangelist{switchvalue:=element.(type){caseint:fmt.Printf("list[%d]isanintanditsvalueis%d\n",index,value)casestring:fmt.Printf("list[%d]isastringanditsvalueis%s\n",index,value)casePerson:fmt.Printf("list[%d]isaPersonanditsvalueis%s\n",index,value)default:fmt.Println("list[%d]isofadifferenttype",index)}}}
Onethingyoushouldrememberisthatelement.(type)cannotbeusedoutsideoftheswitchbody,whichmeansinthatcaseyouhavetousethecomma-okpattern.
Embeddedinterfaces
ThemostbeautifulthingisthatGohasalotofbuilt-inlogicsyntax,suchasanonymousfieldsinstruct.Notsuprisingly,wecanuseinterfacesasanonymousfieldsaswell,butwecallthemEmbeddedinterfaces.Here,wefollowthesamerulesasanonymousfields.Morespecifically,ifaninterfacehasanotherinterfaceembeddedwithinit,itwillhaveasifithasallthemethodsthattheembeddedinterfacehas.
Wecanseethatthesourcefileincontainer/heaphasthefollowingdefinition:
typeInterfaceinterface{sort.Interface//embeddedsort.InterfacePush(xinterface{})//aPushmethodtopushelementsintotheheapPop()interface{}//aPopelementsthatpopselementsfromtheheap}
Weseethatsort.Interfaceisanembeddedinterface,sotheaboveInterfacehasthethreemethodscontainedwithinthesort.Interfaceimplicitly.
typeInterfaceinterface{//Lenisthenumberofelementsinthecollection.Len()int//Lessreturnswhethertheelementwithindexishouldsort//beforetheelementwithindexj.Less(i,jint)bool//Swapswapstheelementswithindexesiandj.Swap(i,jint)}
Anotherexampleistheio.ReadWriterinpackageio.
//io.ReadWritertypeReadWriterinterface{ReaderWriter}
ReflectioninGoisusedfordetermininginformationatruntime.Weusethereflectpackage,andthisofficialarticleexplainshowreflectworksinGo.
Therearethreestepsinvolvedwhenusingreflect.First,weneedtoconvertaninterfacetoreflecttypes(reflect.Typeorreflect.Value,thisdependsonthesituation).
t:=reflect.TypeOf(i)//getmeta-dataintypei,andusettogetallelementsv:=reflect.ValueOf(i)//getactualvalueintypei,andusevtochangeitsvalue
Afterthat,wecanconvertthereflectedtypestogetthevaluesthatweneed.
varxfloat64=3.4v:=reflect.ValueOf(x)fmt.Println("type:",v.Type())fmt.Println("kindisfloat64:",v.Kind()==reflect.Float64)fmt.Println("value:",v.Float())
Finally,ifwewanttochangethevaluesofthereflectedtypes,weneedtomakeitmodifiable.Asdiscussedearlier,thereisadifferencebetweenpassbyvalueandpassbyreference.Thefollowingcodewillnotcompile.
varxfloat64=3.4
Reflection
v:=reflect.ValueOf(x)v.SetFloat(7.1)
Instead,wemustusethefollowingcodetochangethevaluesfromreflecttypes.
varxfloat64=3.4p:=reflect.ValueOf(&x)v:=p.Elem()v.SetFloat(7.1)
Wehavejustdiscussedthebasicsofreflection,howeveryoumustpracticemoreinordertounderstandmore.
DirectoryPrevioussection:Object-orientedNextsection:Concurrency
Links
ItissaidthatGoistheClanguageofthe21stcentury.Ithinktherearetworeasons:first,Goisasimplelanguage;second,concurrencyisahottopicintoday'sworld,andGosupportsthisfeatureatthelanguagelevel.
goroutinesandconcurrencyarebuiltintothecoredesignofGo.They'resimilartothreadsbutworkdifferently.Morethanadozengoroutinesmaybeonlyhave5or6underlyingthreads.Goalsogivesyoufullsupporttosharingmemoryinyourgoroutines.Onegoroutineusuallyuses4~5KBofstackmemory.Therefore,it'snothardtorunthousandsofgoroutinesonasinglecomputer.Agoroutineismorelightweight,moreefficientandmoreconvenientthansystemthreads.
goroutinesrunonthethreadmanageratruntimeinGo.Weusethegokeywordtocreateanewgoroutine,whichisafunctionattheunderlyinglevel(main()isagoroutine).
gohello(a,b,c)
Let'sseeanexample.
packagemain
import("fmt""runtime")
funcsay(sstring){fori:=0;i0!buffernon-blockuntilnelementsinthechannel
Youcantrythefollowingcodeonyourcomputerandchangesomevalues.
packagemain
import"fmt"
funcmain(){c:=make(chanint,2)//change2to1willhaveruntimeerror,but3isfinec
import"fmt"
funcfibonacci(c,quitchanint){x,y:=1,1for{select{casec
Exitsthecurrentgoroutine,butdeferedfunctionswillbeexecutedasusual.
runtime.Gosched()
Letstheschedulerexecuteothergoroutinesandcomesbackatsomepoint.
runtime.NumCPU()int
ReturnsthenumberofCPUcores
runtime.NumGoroutine()int
Returnsthenumberofgoroutines
runtime.GOMAXPROCS(nint)int
SetshowmanyCPUcoresyouwanttouse
DirectoryPrevioussection:interfaceNextsection:Summary
Links
Inthischapter,wemainlyintroducedthe25Gokeywords.Let'sreviewwhattheyareandwhattheydo.
breakdefaultfuncinterfaceselectcasedefergomapstructchanelsegotopackageswitchconstfallthroughifrangetypecontinueforimportreturnvar
varandconstareusedtodefinevariablesandconstants.packageandimportareforpackageuse.funcisusedtodefinefunctionsandmethods.returnisusedtoreturnvaluesinfunctionsormethods.deferisusedtodefinedeferfunctions.goisusedtostartanewgoroutine.selectisusedtoswitchovermultiplechannelsforcommunication.interfaceisusedtodefineinterfaces.structisusedtodefinespecialcustomizedtypes.break,case,continue,for,fallthrough,else,if,switch,gotoanddefaultwereintroducedinsection2.3.chanisthetypeofchannelforcommunicationamonggoroutines.typeisusedtodefinecustomizedtypes.mapisusedtodefinemapwhichissimilartohashtablesinotherlanguages.rangeisusedforreadingdatafromslice,mapandchannel.
Ifyouunderstandhowtousethese25keywords,you'velearnedalotofGoalready.
DirectoryPrevioussection:ConcurrencyNextchapter:Webfoundation
2.8Summary
Links
ThereasonyouarereadingthisbookisthatyouwanttolearntobuildwebapplicationsinGo.AsI'vesaidbefore,Goprovidesmanypowerfulpackageslikehttp.Thesepackagescanhelpyoualotwhentryingtobuildwebapplications.I'llteachyoueverythingyouneedtoknowinthefollowingchapters,andwe'lltalkaboutsomeconceptsofthewebandhowtorunwebapplicationsinGointhischapter.
DirectoryPreviouschapter:Chapter2SummaryNextsection:Webworkingprinciples
3Webfoundation
Links
Everytimeyouopenyourbrowsers,typesomeURLsandpressenter,youwillseebeautifulwebpagesappearonyourscreen.Butdoyouknowwhatishappeningbehindthesesimpleactions?
Normally,yourbrowserisaclient.AfteryoutypeaURL,ittakesthehostpartoftheURLandsendsittoaDNSserverinordertogettheIPaddressofthehost.ThenitconnectstotheIPaddressandaskstosetupaTCPconnection.ThebrowsersendsHTTPrequeststhroughtheconnection.TheserverhandlesthemandreplieswithHTTPresponsescontainingthecontentthatmakeupthewebpage.Finally,thebrowserrendersthebodyofthewebpageanddisconnectsfromtheserver.
Figure3.1Processesofusersvisitawebsite
Awebserver,alsoknownasanHTTPserver,usestheHTTPprotocoltocommunicatewithclients.Allwebbrowserscanbeconsideredclients.
Wecandividetheweb'sworkingprinciplesintothefollowingsteps:
ClientusesTCP/IPprotocoltoconnecttoserver.ClientsendsHTTPrequestpackagestoserver.ServerreturnsHTTPresponsepackagestoclient.Iftherequestedresourcesincludedynamicscripts,servercallsscriptenginefirst.Clientdisconnectsfromserver,startsrenderingHTML.
ThisisasimpleworkflowofHTTPaffairs-noticethattheserverclosesitsconnectionsafteritsendsdatatotheclients,thenwaitsforthenextrequest.
WealwaysuseURLstoaccesswebpages,butdoyouknowhowURLswork?
ThefullnameofaURLisUniformResourceLocator.It'sfordescribingresourcesontheinternetanditsbasicformisasfollows.
scheme://host[:port#]/path/.../[?query-string][#anchor]schemeassignunderlyingprotocol(suchasHTTP,HTTPS,FTP)hostIPordomainnameofHTTPserverport#defaultportis80,anditcanbeomittedinthiscase.Ifyouwanttouseotherports,youmustspecifywhichport.Forexample,http://www.cnblogs.com:8080/pathresourcespathquery-stringdataaresenttoserveranchoranchor
DNSisanabbreviationofDomainNameSystem.It'sthenamingsystemforcomputernetworkservices,anditconvertsdomainnamestoactualIPaddresses,justlikeatranslator.
Figure3.2DNSworkingprinciples
Tounderstandmoreaboutitsworkingprinciple,let'sseethedetailedDNSresolutionprocessasfollows.
1. Aftertypingthedomainnamewww.qq.cominthebrowser,theoperatingsystemwillcheckifthereareanymappingrelationshipsinthehosts'filesforthisdomainname.Ifso,thenthedomainnameresolutioniscomplete.
Webworkingprinciples
URLandDNSresolution
2. Ifnomappingrelationshipsexistinthehosts'files,theoperatingsystemwillcheckifanycacheexistsintheDNS.Ifso,thenthedomainnameresolutioniscomplete.
3. IfnomappingrelationshipsexistinboththehostandDNScache,theoperatingsystemfindsthefirstDNSresolutionserverinyourTCP/IPsettings,whichislikelyyourlocalDNSserver.WhenthelocalDNSserverreceivesthequery,ifthedomainnamethatyouwanttoqueryiscontainedwithinthelocalconfigurationofitsregionalresources,itreturnstheresultstotheclient.ThisDNSresolutionisauthoritative.
4. IfthelocalDNSserverdoesn'tcontainthedomainnamebutamappingrelationshipexistsinthecache,thelocalDNSservergivesbackthisresulttotheclient.ThisDNSresolutionisnotauthoritative.
5. IfthelocalDNSservercannotresolvethisdomainnameeitherbyconfigurationofregionalresourcesorcache,itwillproceedtothenextstep,whichdependsonthelocalDNSserver'ssettings.-IfthelocalDNSserverdoesn'tenableforwarding,itroutestherequesttotherootDNSserver,thenreturnstheIPaddressofatoplevelDNSserverwhichmayknowthedomainname,.cominthiscase.IfthefirsttoplevelDNSserverdoesn'trecognizethedomainname,itagainreroutestherequesttothenexttoplevelDNSserveruntilitreachesonethatrecognizesthedomainname.ThenthetoplevelDNSserverasksthisnextlevelDNSserverfortheIPaddresscorrespondingtowww.qq.com.-IfthelocalDNSserverhasforwardingenabled,itsendstherequesttoanupperlevelDNSserver.IftheupperlevelDNSserveralsodoesn'trecognizethedomainname,thentherequestkeepsgettingreroutedtohigherlevelsuntilitfinallyreachesaDNSserverwhichrecognizesthedomainname.
WhetherornotthelocalDNSserverenablesforwarding,theIPaddressofthedomainnamealwaysreturnstothelocalDNSserver,andthelocalDNSserversendsitbacktotheclient.
Figure3.3DNSresolutionworkflow
Recursivequeryprocesssimplymeansthattheenquirerschangeintheprocess.EnquirersdonotchangeinIterativequeryprocesses.
NowweknowclientsgetIPaddressesintheend,sothebrowsersarecommunicatingwithserversthroughIPaddresses.
TheHTTPprotocolisacorepartofwebservices.It'simportanttoknowwhattheHTTPprotocolisbeforeyouunderstandhowthewebworks.
HTTPistheprotocolthatisusedtofacilitatecommunicationbetweenbrowsersandwebservers.ItisbasedontheTCPprotocolandusuallyusesport80onthesideofthewebserver.Itisaprotocolthatutilizestherequest-responsemodel-clientssendrequestsandserversrespond.AccordingtotheHTTPprotocol,clientsalwayssetupnewconnectionsandsendHTTPrequeststoservers.Serversarenotabletoconnecttoclientsproactively,orestablishcallbackconnections.Theconnectionbetweenaclientandaservercanbeclosedbyeitherside.Forexample,youcancancelyourdownloadrequestandHTTPconnectionandyourbrowserwilldisconnectfromtheserverbeforeyoufinishdownloading.
TheHTTPprotocolisstateless,whichmeanstheserverhasnoideaabouttherelationshipbetweenthetwoconnectionseventhoughtheyarebothfromsameclient.Tosolvethisproblem,webapplicationsusecookiestomaintainthestateofconnections.
BecausetheHTTPprotocolisbasedontheTCPprotocol,allTCPattackswillaffectHTTPcommunicationsinyourserver.ExamplesofsuchattacksareSYNflooding,DoSandDDoSattacks.
Requestpackagesallhavethreeparts:requestline,requestheader,andbody.Thereisoneblanklinebetweenheaderandbody.
GET/domains/example/HTTP/1.1//requestline:requestmethod,URL,protocolanditsversionHostwww.iana.org//domainnameUser-AgentMozilla/5.0(WindowsNT6.1)AppleWebKit/537.4(KHTML,likeGecko)Chrome/22.0.1229.94Safari/537.4//browserinformation
HTTPprotocol
HTTPrequestpackage(browserinformation)
Accepttext/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8//minethatclientscanacceptAccept-Encodinggzip,deflate,sdch//streamcompressionAccept-CharsetUTF-8,*;q=0.5//charactersetinclientside//blankline//body,requestresourcearguments(forexample,argumentsinPOST)
Weusefiddlertogetthefollowingrequestinformation.
Figure3.4InformationofaGETrequestcaughtbyfiddler
Figure3.5InformationofaPOSTrequestcaughtbyfiddler
WecanseethatGETdoesnothavearequestbody,unlikePOST,whichdoes.
TherearemanymethodsyoucanusetocommunicatewithserversinHTTP;GET,POST,PUTandDELETEarethe4basicmethodsthatwetypicallyuse.AURLrepresentsaresourceonanetwork,sothese4methodsdefinethequery,change,addanddeleteoperationsthatcanactontheseresources.GETandPOSTareverycommonlyusedinHTTP.GETcanappendqueryparameterstotheURL,using?toseparatetheURLandparametersand&betweenthearguments,likeEditPosts.aspx?name=test1&id=123456.POSTputsdataintherequestbodybecausetheURLimplementsalengthlimitationviathebrowser.Thus,POSTcansubmitmuchmoredatathanGET.Also,whenwesubmitusernamesandpasswords,wedon'twantthiskindofinformationtoappearintheURL,soweusePOSTtokeeptheminvisible.
Let'sseewhatinformationiscontainedintheresponsepackages.
HTTP/1.1200OK//statuslineServer:nginx/1.0.8//webserversoftwareanditsversionintheservermachineDate:Date:Tue,30Oct201204:14:25GMT//respondedtimeContent-Type:text/html//respondeddatatypeTransfer-Encoding:chunked//itmeansdataweresentinfragmentsConnection:keep-alive//keepconnectionContent-Length:90//lengthofbody//blankline
Thetermstatelessdoesn'tmeanthattheserverhasnoabilitytokeepaconnection.Itsimplymeansthattheserverdoesn'trecognizeanyrelationshipsbetweenanytworequests.
InHTTP/1.1,Keep-aliveisusedbydefault.Ifclientshaveadditionalrequests,theywillusethesameconnectionforthem.
NoticethatKeep-alivecannotmaintianoneconnectionforever;theapplicationrunningintheserverdeterminesthelimitwithwhichtokeeptheconnectionalivefor,andinmostcasesyoucanconfigurethislimit.
Figure3.7Allpackagesforopeningonewebpage
Wecanseetheentirecommunicationprocessbetweenclientandserverfromtheabovepicture.Youmaynoticethattherearemanyresourcefilesinthelist;thesearecalledstaticfiles,andGohasspecializedprocessingmethodsforthesefiles.
Thisisthemostimportantfunctionofbrowsers:torequestforaURLandretrievedatafromwebservers,thenrendertheHTML.IfitfindssomefilesintheDOMsuchasCSSorJSfiles,browserswillrequesttheseresourcesfromtheserveragainuntilalltheresourcesfinishrenderingonyourscreen.
ReducingHTTPrequesttimesisonewayofimprovingtheloadingspeedofwebpages.ByreducingthenumberofCSSandJSfilesthatneedtobeloaded,bothrequestlatenciesandpressureonyourwebserverscanbereducedatthesametime.
DirectoryPrevioussection:WebfoundationNextsection:Buildasimplewebserver
Requestinstance
Links
We'vediscussedthatwebapplicationsarebasedontheHTTPprotocol,andGoprovidesfullHTTPsupportinthenet/httppackage.It'sveryeasytosetawebserverupusingthispackage.
packagemain
import("fmt""net/http""strings""log")
funcsayhelloName(whttp.ResponseWriter,r*http.Request){r.ParseForm()//parsearguments,youhavetocallthisbyyourselffmt.Println(r.Form)//printforminformationinserversidefmt.Println("path",r.URL.Path)fmt.Println("scheme",r.URL.Scheme)fmt.Println(r.Form["url_long"])fork,v:=ranger.Form{fmt.Println("key:",k)fmt.Println("val:",strings.Join(v,""))}fmt.Fprintf(w,"Helloastaxie!")//senddatatoclientside}
funcmain(){http.HandleFunc("/",sayhelloName)//setroutererr:=http.ListenAndServe(":9090",nil)//setlistenportiferr!=nil{log.Fatal("ListenAndServe:",err)}}
Afterweexecutetheabovecode,theserverbeginslisteningtoport9090inlocalhost.
Openyourbrowserandvisithttp://localhost:9090.YoucanseethatHelloastaxieisonyourscreen.
Let'stryanotheraddresswithadditionalarguments:http://localhost:9090/?url_long=111&url_long=222
Nowlet'sseewhathappensonboththeclientandserversides.
Youshouldseethefollowinginformationontheserverside:
Figure3.8Serverprintedinformation
Asyoucansee,weonlyneedtocalltwofunctionsinordertobuildasimplewebserver.
IfyouareworkingwithPHP,you'reprobablyaskingwhetherornotweneedsomethinglikeNginxorApache.Theanswerisweno,sinceGolistenstotheTCPportbyitself,andthefunctionsayhelloNameisthelogicfunctionjustlikeacontrollerinPHP.
IfyouareworkingwithPythonyoushouldknowtornado,andtheaboveexampleisverysimilartothat.
IfyouareworkingwithRuby,youmaynoticeitislikescript/serverinROR.
Weusedtwosimplefunctionstosetupasimplewebserverinthissection,andthissimpleserveralreadyhasthecapacity
3.2Buildasimplewebserver
Usehttppackagesetupawebserver
forhighconcurrencyoperations.Wewilltalkabouthowtoutilizethisinthenexttwosections.
DirectoryPrevioussection:WebworkingprinciplesNextsection:HowGoworkswithweb
Links
Welearnedtousethenet/httppackagetobuildasimplewebserverintheprevioussection,andallthoseworkingprinciplesarethesameasthosewewilltalkaboutinthefirstsectionofthischapter.
Request:requestdatafromusers,includingPOST,GET,CookieandURL.
Response:responsedatafromservertoclients.
Conn:connectionsbetweenclientsandservers.
Handler:Requesthandlinglogicandresponsegeneration.
ThefollowingpictureshowstheworkflowofaGowebserver.
Figure3.9httpworkflow
1. Createalisteningsocket,listentoaportandwaitforclients.2. Acceptrequestsfromclients.3. Handlerequests,readHTTPheader.IftherequestusesPOSTmethod,readdatainthemessagebodyandpassthem
tohandlers.Finally,socketreturnsresponsedatatoclients.
Onceweknowtheanswerstothethreefollowingquestions,it'seasytoknowhowthewebworksinGo.
Howdowelistentoaport?Howdoweacceptclientrequests?Howdoweallocatehandlers?
IntheprevioussectionwesawthatGousesListenAndServetohandlethesesteps:initializeaserverobject,callnet.Listen("tcp",addr)tosetupaTCPlistenerandlistentoaspecificaddressandport.
Let'stakealookatthehttppackage'ssourcecode.
//Buildversiongo1.1.2.func(srv*Server)Serve(lnet.Listener)error{deferl.Close()vartempDelaytime.Duration//howlongtosleeponacceptfailurefor{rw,e:=l.Accept()ife!=nil{ifne,ok:=e.(net.Error);ok&&ne.Temporary(){iftempDelay==0{tempDelay=5*time.Millisecond}else{tempDelay*=2}ifmax:=1*time.Second;tempDelay>max{tempDelay=max}log.Printf("http:Accepterror:%v;retryingin%v",e,tempDelay)time.Sleep(tempDelay)continue}returne
3.3HowGoworkswithweb
Conceptsinwebprinciples
httppackageoperatingmechanism
}tempDelay=0c,err:=srv.newConn(rw)iferr!=nil{continue}goc.serve()}}
Howdoweacceptclientrequestsafterwebeginlisteningtoaport?Inthesourcecode,wecanseethatsrv.Serve(net.Listener)iscalledtohandleclientrequests.Inthebodyofthefunctionthereisafor{}.Itacceptsarequest,createsanewconnectionthenstartsanewgoroutine,passingtherequestdatatothegoc.serve()goroutine.ThisishowGosupportshighconcurrency,andeverygoroutineisindependent.
Howdoweusespecificfunctionstohandlerequests?connparsesrequestc.ReadRequest()atfirst,thengetsthecorrespondinghandler:handler:=c.server.HandlerwhichisthesecondargumentwepassedwhenwecalledListenAndServe.Becausewepassednil,Gousesitsdefaulthandlerhandler=DefaultServeMux.SowhatisDefaultServeMuxdoinghere?Well,itstheroutervariablewhichcancallhandlerfunctionsforspecificURLs.Didwesetthis?Yes,wedid.Wedidthisinthefirstlinewhereweusedhttp.HandleFunc("/",sayhelloName).We'reusingthisfunctiontoregistertherouterruleforthe"/"path.WhentheURLis/,theroutercallsthefunctionsayhelloName.DefaultServeMuxcallsServerHTTPtogethandlerfunctionsfordifferentpaths,callingsayhelloNameinthisspecificcase.Finally,theserverwritesdataandrespondstoclients.
Detailedworkflow:
Figure3.10WorkflowofhandlinganHTTPrequest
IthinkyoushouldknowhowGorunswebserversnow.
DirectoryPrevioussection:BuildasimplewebserverNextsection:Getintohttppackage
Links
Inprevioussections,welearnedabouttheworkflowofthewebandtalkedalittlebitaboutGo'shttppackage.Inthissection,wearegoingtolearnabouttwocorefunctionsinthehttppackage:ConnandServeMux.
UnlikenormalHTTPservers,GousesgoroutinesforeveryjobinitiatedbyConninordertoachievehighconcurrencyandperformance,soeveryjobisindependent.
Gousesthefollowingcodetowaitfornewconnectionsfromclients.
c,err:=srv.newConn(rw)iferr!=nil{continue}goc.serve()
Asyoucansee,itcreatesanewgoroutineforeveryconnection,andpassesthehandlerthatisabletoreaddatafromtherequesttothegoroutine.
WeusedGo'sdefaultrouterinprevioussectionswhendiscussingconn.server,withtherouterpassingrequestdatatoaback-endhandler.
Thestructofthedefaultrouter:
typeServeMuxstruct{musync.RWMutex//becauseofconcurrency,wehavetouseamutexheremmap[string]muxEntry//routerrules,everystringmappingtoahandler}
ThestructofmuxEntry:
typemuxEntrystruct{explicitbool//exactmatchornothHandler}
TheinterfaceofHandler:
typeHandlerinterface{ServeHTTP(ResponseWriter,*Request)//routingimplementer}
Handlerisaninterface,butifthefunctionsayhelloNamedidn'timplementthisinterface,thenhowdidweadditashandler?TheanswerliesinanothertypecalledHandlerFuncinthehttppackage.WecalledHandlerFunctodefineoursayhelloNamemethod,sosayhelloNameimplementedHandleratthesametime.It'slikewe'recallingHandlerFunc(f),andthefunctionfisforceconvertedtotypeHandlerFunc.
typeHandlerFuncfunc(ResponseWriter,*Request)
3.4Getintohttppackage
goroutineinConn
CustomizedServeMux
//ServeHTTPcallsf(w,r).func(fHandlerFunc)ServeHTTP(wResponseWriter,r*Request){f(w,r)}
Howdoestheroutercallhandlersafterwesettherouterrules?
Theroutercallsmux.handler.ServeHTTP(w,r)whenitreceivesrequests.Inotherwords,itcallstheServeHTTPinterfaceofthehandlerswhichhaveimplementedit.
Now,let'sseehowmux.handlerworks.
func(mux*ServeMux)handler(r*Request)Handler{mux.mu.RLock()defermux.mu.RUnlock()
//Host-specificpatterntakesprecedenceovergenericonesh:=mux.match(r.Host+r.URL.Path)ifh==nil{h=mux.match(r.URL.Path)}ifh==nil{h=NotFoundHandler()}returnh}
Therouterusestherequest'sURLasakeytofindthecorrespondinghandlersavedinthemap,thencallshandler.ServeHTTPtoexecutefunctionstohandlethedata.
Youshouldunderstandthedefaultrouter'sworkflowbynow,andGoactuallysupportscustomizedrouters.ThesecondargumentofListenAndServeisforconfiguringcustomizedrouters.It'saninterfaceofHandler.Therefore,anyrouterthatimplementstheHandlerinterfacecanbeused.
Thefollowingexampleshowshowtoimplementasimplerouter.
packagemain
import("fmt""net/http")
typeMyMuxstruct{}
func(p*MyMux)ServeHTTP(whttp.ResponseWriter,r*http.Request){ifr.URL.Path=="/"{sayhelloName(w,r)return}http.NotFound(w,r)return}
funcsayhelloName(whttp.ResponseWriter,r*http.Request){fmt.Fprintf(w,"Hellomyroute!")}
funcmain(){mux:=&MyMux{}http.ListenAndServe(":9090",mux)}
Gocodeexecutionflow
Let'stakealookatthewholeexecutionflow.
Callhttp.HandleFunc1. CallHandleFuncofDefaultServeMux2. CallHandleofDefaultServeMux3. Addrouterrulestomap[string]muxEntryofDefaultServeMux
Callhttp.ListenAndServe(":9090",nil)1. InstantiateServer2. CallListenAndServemethodofServer3. Callnet.Listen("tcp",addr)tolistentoport4. Startaloopandacceptrequestsintheloopbody5. InstantiateaConnandstartagoroutineforeveryrequest:goc.serve()6. Readrequestdata:w,err:=c.readRequest()7. Checkwhetherhandlerisemptyornot,ifit'semptythenuseDefaultServeMux8. CallServeHTTPofhandler9. ExecutecodeinDefaultServeMuxinthiscase
10. ChoosehandlerbyURLandexecutecodeinthathandlerfunction:mux.handler.ServeHTTP(w,r)11. Howtochoosehandler:A.CheckrouterrulesforthisURLB.CallServeHTTPinthathandlerifthereisoneC.Call
ServeHTTPofNotFoundHandlerotherwise
DirectoryPrevioussection:HowGoworkswithwebNextsection:Summary
Links
Inthischapter,weintroducedHTTP,DNSresolutionflowandhowtobuildasimplewebserver.ThenwetalkedabouthowGoimplementswebserversforusbylookingatthesourcecodeofthenet/httppackage.
Ihopethatyounowknowmuchmoreaboutwebdevelopment,andyoushouldseethatit'squiteeasyandflexibletobuildawebapplicationinGo.
DirectoryPrevioussection:GetintohttppackageNextchapter:Userform
3.5Summary
Links
Auserformissomethingthatisverycommonlyusedwhendeveloppingwebapplications.Itprovidestheabilitytocommunicatebetweenclientsandservers.Youmustbeveryfamiliarwithformsifyouareawebdeveloper;ifyouareaC/C++programmer,youmaywanttoask:whatisauserform?
Aformisanareathatcontainsformelements.Userscaninputinformationintoformelementsliketextboxes,dropdownlists,radiobuttons,checkboxes,etc.Weusetheformtagtodefineforms.
...inputelements...
Goalreadyhasmanyconvenientfunctionstodealwithuserforms.YoucaneasilygetformdatainHTTPrequests,andtheyareeasytointegrateintoyourownwebapplications.Insection4.1,wearegoingtotalkabouthowtohandleformdatainGo.Also,sinceyoucannottrustanydatacomingfromtheclientside,youmustfirstverifythedatabeforeusingit.We'llgothroughsomeexamplesabouthowtoverifyformdatainsection4.2.
WesaythatHTTPisstateless.Howcanweidentifythatcertainformsarefromthesameuser?Andhowdowemakesurethatoneformcanonlybesubmittedonce?We'lllookatsomedetailsconcerningcookies(acookieisinformationthatcanbesavedontheclientsideandaddedtotherequestheaderwhentherequestissenttotheserver)inbothsections4.3and4.4.
Anotherbiguse-caseofformsisuploadingfiles.Insection4.5,youwilllearnhowtodothisaswellascontrollingthefileuploadsizebeforeitbeginsuploading,inGo.
DirectoryPreviouschapter:Chapter3SummaryNextsection:Processforminputs
4Userform
Links
Beforewebegin,let'stakealookatasimpleexampleofatypicaluserform,savedaslogin.gtplinyourprojectfolder.
Username:Password:
Thisformwillsubmitto/loginontheserver.Aftertheuserclickstheloginbutton,thedatawillbesenttotheloginhandlerregisteredbytheserverrouter.ThenweneedtoknowwhetheritusesthePOSTmethodorGET.
Thisiseasytofindoutusingthehttppackage.Let'sseehowtohandletheformdataontheloginpage.
packagemain
import("fmt""html/template""log""net/http""strings")
funcsayhelloName(whttp.ResponseWriter,r*http.Request){r.ParseForm()//Parseurlparameterspassed,thenparsetheresponsepacketforthePOSTbody(requestbody)//attention:IfyoudonotcallParseFormmethod,thefollowingdatacannotbeobtainedformfmt.Println(r.Form)//printinformationonserverside.fmt.Println("path",r.URL.Path)fmt.Println("scheme",r.URL.Scheme)fmt.Println(r.Form["url_long"])fork,v:=ranger.Form{fmt.Println("key:",k)fmt.Println("val:",strings.Join(v,""))}fmt.Fprintf(w,"Helloastaxie!")//writedatatoresponse}
funclogin(whttp.ResponseWriter,r*http.Request){fmt.Println("method:",r.Method)//getrequestmethodifr.Method=="GET"{t,_:=template.ParseFiles("login.gtpl")t.Execute(w,nil)}else{r.ParseForm()//logicpartofloginfmt.Println("username:",r.Form["username"])fmt.Println("password:",r.Form["password"])}}
funcmain(){http.HandleFunc("/",sayhelloName)//settingrouterrulehttp.HandleFunc("/login",login)err:=http.ListenAndServe(":9090",nil)//settinglisteningportiferr!=nil{log.Fatal("ListenAndServe:",err)}}
Hereweuser.Methodtogettherequestmethod,anditreturnsanhttpverb-"GET","POST","PUT",etc.
4.1Processforminputs
Intheloginfunction,weuser.Methodtocheckwhetherit'saloginpageorloginprocessinglogic.Inotherwords,wechecktoseewhethertheuserissimplyopeningthepage,ortryingtologin.ServeshowsthepageonlywhentherequestcomesinviatheGETmethod,anditexecutestheloginlogicwhentherequestusesthePOSTmethod.
Youshouldseethefollowinginterfaceafteropeninghttp://127.0.0.1:9090/logininyourbrowser.
Figure4.1Userlogininterface
Theserverwillnotprintanythinguntilafterwetypeinausernameandpassword,becausethehandlerdoesn'tparsetheformuntilwecallr.ParseForm().Let'saddr.ParseForm()beforefmt.Println("username:",r.Form["username"]),compileourprogramandtestitagain.Youwillfindthattheinformationisprintedontheserversidenow.
r.Formcontainsalloftherequestarguments,forinstancethequery-stringintheURLandthedatainPOSTandPUT.Ifthedatahasconflicts,forexampleparametersthathavethesamename,theserverwillsavethedataintoaslicewithmultiplevalues.TheGodocumentationstatesthatGowillsavethedatafromGETandPOSTrequestsindifferentplaces.
Trychangingthevalueoftheactionintheformhttp://127.0.0.1:9090/logintohttp://127.0.0.1:9090/login?username=astaxieinthelogin.gtplfile,testitagain,andyouwillseethatthesliceisprintedontheserverside.
Figure4.2Serverprintsrequestdata
Thetypeofrequest.Formisurl.Value.Itsavesdatawiththeformatkey=value.
v:=url.Values{}v.Set("name","Ava")v.Add("friend","Jess")v.Add("friend","Sarah")v.Add("friend","Zoe")//v.Encode()=="name=Ava&friend=Jess&friend=Sarah&friend=Zoe"fmt.Println(v.Get("name"))fmt.Println(v.Get("friend"))fmt.Println(v["friend"])
TipsRequestshavetheabilitytoaccessformdatausingtheFormValue()method.Forexample,youcanchanger.Form["username"]tor.FormValue("username"),andGocallsr.ParseFormautomatically.Noticethatitreturnsthefirstvalueifthereareargumentswiththesamename,anditreturnsanemptystringifthereisnosuchargument.
DirectoryPrevioussection:UserformNextsection:Verificationofinputs
Links
Oneofthemostimportantprinciplesinwebdevelopmentisthatyoucannottrustanythingfromclientsideuserforms.Youhavetoverifyallincomingdatabeforeuseit.Manywebsitesareaffectedbythisproblem,whichissimpleyetcrucial.
Therearetwowaysofverifyformdatathatarecommonlyused.OneisJavaScriptverificationinthefront-end,andtheotherisserververificationintheback-end.Inthissection,wearegoingtotalkaboutserversideverificationinwebdevelopment.
Sometimeswerequirethatusersinputsomefieldsbuttheydon't,forexampleintheprevioussectionwhenwerequiredausername.Youcanusethelenfunctiontogetthelengthofafieldinordertoensurethatusershaveenteredthisinformation.
iflen(r.Form["username"][0])==0{//codeforemptyfield}
r.Formtreatsdifferentformelementtypesdifferentlywhentheyareblank.Foremptytextboxes,textareasandfileuploads,itreturnsanemptystring;forradiobuttonsandcheckboxes,itdoesn'tevencreatethecorrespondingitems.Instead,youwillgeterrorsifyoutrytoaccessit.Therefore,it'ssafertouser.Form.Get()togetfiledvaluessinceitwillalwaysreturnemptyifthevaluedoesnotexist.Ontheotherhand,r.Form.Get()canonlygetonefieldvalueatatime,soyouneedtouser.Formtogetthemapofvalues.
Sometimesyouonlyneednumbersforthefieldvalue.Forexample,let'ssaythatyourequiretheageofauserinintegerformonly,i.e50or10,insteadof"oldenough"or"youngman".Ifwerequireapositivenumber,wecanconvertthevaluetotheinttypefirst,thenprocessit.
getint,err:=strconv.Atoi(r.Form.Get("age"))iferr!=nil{//erroroccurswhenconverttonumber,itmaynotanumber}
//checkrangeofnumberifgetint>100{//toobig}
Anotherwaytodothisisusingregularexpressions.
ifm,_:=regexp.MatchString("^[0-9]+$",r.Form.Get("age"));!m{returnfalse}
Forhighperformancepurposes,regularexpressionsarenotefficient,howeversimpleregularexpressionsareusuallyfastenough.Ifyouarefamiliarwithregularexpressions,it'saveryconvenientwaytoverifydata.NoticethatGousesRE2,soallUTF-8charactersaresupported.
4.2Verificationofinputs
Requiredfields
Numbers
Chinese
SometimesweneeduserstoinputtheirChinesenamesandwehavetoverifythattheyalluseChineseratherthanrandomcharacters.ForChineseverification,regularexpressionsaretheonlyway.
ifm,_:=regexp.MatchString("^[\\x{4e00}-\\x{9fa5}]+$",r.Form.Get("realname"));!m{returnfalse}
SometimesweneeduserstoinputonlyEnglishletters.Forexample,werequiresomeone'sEnglishname,likeastaxieinsteadofasta.Wecaneasilyuseregularexpressionstoperformourverification.
ifm,_:=regexp.MatchString("^[a-zA-Z]+$",r.Form.Get("engname"));!m{returnfalse}
IfyouwanttoknowwhetherusershaveenteredvalidE-mailaddresses,youcanusethefollowingregularexpression:
ifm,_:=regexp.MatchString(`^([\w\.\_]{2,10})@(\w{1,}).([a-z]{2,4})$`,r.Form.Get("email"));!m{fmt.Println("no")}else{fmt.Println("yes")}
Let'ssaywerequireanitemfromourdropdownlist,butinsteadwegetavaluefabricatedbyhackers.Howdowepreventthisfromhappening?
Supposewehavethefollowing:
applepearbanana
Wecanusethefollowingstrategytosanitizeourinput:
slice:=[]string{"apple","pear","banana"}
for_,v:=rangeslice{ifv==r.Form.Get("fruit"){returntrue}}returnfalse
AllthefunctionsI'veshownaboveareinmyopensourceprojectforoperatingonslicesandmaps:https://github.com/astaxie/beeku
Englishletters
E-mailaddress
Dropdownlist
Ifwewanttoknowwhethertheuserismaleorfemale,wemayusearadiobutton,returning1formaleand2forfemale.However,somelittlekidwhojustreadhisfirstbookonHTTP,decidestosendtoyoua3.Willyourprogramhavehaveexception?Asyoucansee,weneedtousethesamemethodaswedidforourdropdownlisttomakesurethatonlyexpectedvaluesarereturnedbyourradiobutton.
MaleFemale
Andweusefollowingcodetoverifytheinput:
slice:=[]int{1,2}
for_,v:=rangeslice{ifv==r.Form.Get("gender"){returntrue}}returnfalse
Supposetherearesomecheckboxesforuserinterests,andthatyoudon'twantextraneousvalueshereeither.
FootballBasketballTennis
Inthiscase,thesanitizationisalittlebitdifferentthanverifyingthebuttonandcheckboxinputssinceherewegetaslicefromthecheckboxes.
slice:=[]string{"football","basketball","tennis"}a:=Slice_diff(r.Form["interest"],slice)ifa==nil{returntrue}
returnfalse
Supposeyouwantuserstoinputvaliddatesortimes.Gohasthetimepackageforconvertingyear,monthanddaytotheircorrespondingtimes.Afterthat,it'seasytocheckit.
t:=time.Date(2009,time.November,10,23,0,0,0,time.UTC)fmt.Printf("Golaunchedat%s\n",t.Local())
Afteryouhavethetime,youcanusethetimepackageformoreoperations,dependingonyourneeds.
Inthissection,we'vediscussedsomecommonmethodsforverifyingformdataserverside.IhopethatyounowunderstandmoreaboutdataverificationinGo,especiallyhowtouseregularexpressionstoyouradvantage.
Radiobuttons
Checkboxes
Dateandtime
DirectoryPrevioussection:ProcessforminputsNextsection:Crosssitescripting
Links
Today'swebsiteshavemuchmoredynamiccontentinordertoimproveuserexperience,whichmeansthatwemustprovidedynamicinformationdependingoneveryindividual'sbehavior.Unfortunately,thereisathingcalled"Crosssitescripting"(knownas"XSS")alwaysattackingdynamicwebsites,fromwhichstaticwebsitesarecompletelyfineatthistime.
AttackersofteninjectmaliciousscriptslikeJavaScript,VBScript,ActiveXorFlashintothosewebsitesthathaveloopholes.Oncetheyhavesuccessfullyinjectedtheirscripts,userinformationcanbestolenandyourwebsitecanbefloodedwithspam.Theattackerscanalsochangeusersettingstowhatevertheywant.
Ifyouwanttopreventthiskindofattack,youshouldcombinethetwofollowingapproaches:
Verificationofalldatafromusers,whichwetalkedaboutintheprevioussection.Carefullyhandledatathatwillbesenttoclientsinordertopreventanyinjectedscriptsfromrunningonbrowsers.
SohowcanwedothesetwothingsinGo?Fortunately,thehtml/templatepackagehassomeusefulfunctionstoescapedataasfollows:
funcHTMLEscape(wio.Writer,b[]byte)escapesbtow.funcHTMLEscapeString(sstring)stringreturnsastringafterescapingfroms.funcHTMLEscaper(args...interface{})stringreturnsastringafterescapingfrommultiplearguments.
Let'schangetheexampleinsection4.1:
fmt.Println("username:",template.HTMLEscapeString(r.Form.Get("username")))//printatserversidefmt.Println("password:",template.HTMLEscapeString(r.Form.Get("password")))template.HTMLEscape(w,[]byte(r.Form.Get("username")))//respondedtoclients
Ifsomeonetriestoinputtheusernameasalert(),wewillseethefollowingcontentinthebrowser:
Figure4.3JavaScriptafterescaped
Functionsinthehtml/templatepackagehelpyoutoescapeallHTMLtags.Whatifyoujustwanttoprintalert()tobrowsers?Youshouldusetext/templateinstead.
import"text/template"...t,err:=template.New("foo").Parse(`{{define"T"}}Hello,{{.}}!{{end}}`)err=t.ExecuteTemplate(out,"T","alert('youhavebeenpwned')")
Output:
Hello,alert('youhavebeenpwned')!
Oryoucanusethetemplate.HTMLtype:Variablecontentwillnotbeescapedifitstypeistemplate.HTML.
import"html/template"...t,err:=template.New("foo").Parse(`{{define"T"}}Hello,{{.}}!{{end}}`)err=t.ExecuteTemplate(out,"T",template.HTML("alert('youhavebeenpwned')"))
Output:
4.3Crosssitescripting
Hello,alert('youhavebeenpwned')!
Onemoreexampleofescaping:
import"html/template"...t,err:=template.New("foo").Parse(`{{define"T"}}Hello,{{.}}!{{end}}`)err=t.ExecuteTemplate(out,"T","alert('youhavebeenpwned')")
Output:
Hello,!
DirectoryPrevioussection:VerificationofinputsNextsection:Duplicatesubmissions
Links
Idon'tknowifyou'veeverseensomeblogsorBBS'thathavemorethanonepoststhatareexactlythesame,butIcantellyouthatit'sbecauseuserssubmittedduplicatepostforms.Theremanythingsthatcancauseduplicatesubmissions;sometimesusersjustdoubleclickthesubmitbutton,ortheywanttomodifysomecontentafterpostingandpressthebackbutton.Othertimesit'stheintentionalactionsofmalicioususers.It'seasytoseehowduplicatesubmissionscanleadtomanyproblems.Thus,wehavetouseeffectivemeanstopreventit.
Thesolutionistoaddahiddenfieldwithauniquetokentoyourform,andtoalwayscheckthistokenbeforeprocessingtheincomingdata.Also,ifyouareusingAjaxtosubmitaform,useJavaScripttodisablethesubmitbuttononcetheformhasbeensubmitted.
Let'simprovetheexamplefromsection4.2:
FootballBasketballTennisUsername:Password:
WeuseanMD5hash(timestamp)togeneratethetoken,andaddedittobothahiddenfieldontheclientsideformandasessioncookieontheserverside(Chapter6).Wecanthenusethistokentocheckwhetherornotthisformwassubmitted.
funclogin(whttp.ResponseWriter,r*http.Request){fmt.Println("method:",r.Method)//getrequestmethodifr.Method=="GET"{crutime:=time.Now().Unix()h:=md5.New()io.WriteString(h,strconv.FormatInt(crutime,10))token:=fmt.Sprintf("%x",h.Sum(nil))
t,_:=template.ParseFiles("login.gtpl")t.Execute(w,token)}else{//loginrequestr.ParseForm()token:=r.Form.Get("token")iftoken!=""{//checktokenvalidity}else{//giveerrorifnotoken}fmt.Println("usernamelength:",len(r.Form["username"][0]))fmt.Println("username:",template.HTMLEscapeString(r.Form.Get("username")))//printinserversidefmt.Println("password:",template.HTMLEscapeString(r.Form.Get("password")))template.HTMLEscape(w,[]byte(r.Form.Get("username")))//respondtoclient}}
Figure4.4Thecontentinbrowserafteraddingatoken
Youcanrefreshthispageandyouwillseeadifferenttokeneverytime.Thisensuresthateveryformisunique.
Fornow,youcanpreventmanyduplicatesubmissionattacksbyaddingtokenstoyourforms,butitcannotpreventalldeceptiveattacksofthistype.Thereismuchmoreworkthatneedstobedone.
4.4Duplicatesubmissions
Links
DirectoryPrevioussection:CrosssitescriptingNextsection:Fileupload
SupposeyouhaveawebsitelikeInstagramandyouwantuserstouploadtheirbeautifulphotos.Howwouldyouimplementthatfunctionality?
Youhavetoaddpropertyenctypetotheformthatyouwanttouseforuploadingphotos.Therearethreepossiblevaluesforthisproperty:
application/x-www-form-urlencodedTranscodeallcharactersbeforeuploading(default).multipart/form-dataNotranscoding.Youmustusethisvaluewhenyourformhasfileuploadcontrols.text/plainConvertspacesto"+",butnotranscodingforspecialcharacters.
Therefore,theHTMLcontentofafileuploadformshouldlooklikethis:
Uploadfile
Weneedtoaddafunctionontheserversidetohandlethisform.
http.HandleFunc("/upload",upload)
//uploadlogicfuncupload(whttp.ResponseWriter,r*http.Request){fmt.Println("method:",r.Method)ifr.Method=="GET"{crutime:=time.Now().Unix()h:=md5.New()io.WriteString(h,strconv.FormatInt(crutime,10))token:=fmt.Sprintf("%x",h.Sum(nil))
t,_:=template.ParseFiles("upload.gtpl")t.Execute(w,token)}else{r.ParseMultipartForm(32
useio.Copytosavetoyourfilesystem.
Youdon'tneedtocallr.ParseFormwhenyouaccessothernon-filefieldsintheformbecauseGowillcallitwhenit'snecessary.Also,callingParseMultipartFormonceisenough-multiplecallsmakenodifference.
Weusethreestepsforuploadingfilesasfollows:
1. Addenctype="multipart/form-data"toyourform.2. Callr.ParseMultipartFormontheserversidetosavethefileeithertomemoryortoatemporaryfile.3. Callr.FormFiletogetthefilehandleandsavetothefilesystem.
Thefilehandleristhemultipart.FileHeader.Itusesthefollowingstruct:
typeFileHeaderstruct{FilenamestringHeadertextproto.MIMEHeader//containsfilteredorunexportedfields}
Figure4.5Printinformationonserverafterreceivingfile.
Ishowedanexampleofusingaformtoauploadafile.WecanimpersonateaclientformtouploadfilesinGoaswell.
packagemain
import("bytes""fmt""io""io/ioutil""mime/multipart""net/http""os")
funcpostFile(filenamestring,targetUrlstring)error{bodyBuf:=&bytes.Buffer{}bodyWriter:=multipart.NewWriter(bodyBuf)
//thisstepisveryimportantfileWriter,err:=bodyWriter.CreateFormFile("uploadfile",filename)iferr!=nil{fmt.Println("errorwritingtobuffer")returnerr}
//openfilehandlefh,err:=os.Open(filename)iferr!=nil{fmt.Println("erroropeningfile")returnerr}
//iocopy_,err=io.Copy(fileWriter,fh)iferr!=nil{returnerr}
contentType:=bodyWriter.FormDataContentType()bodyWriter.Close()
resp,err:=http.Post(targetUrl,contentType,bodyBuf)iferr!=nil{returnerr
Clientsuploadfiles
}deferresp.Body.Close()resp_body,err:=ioutil.ReadAll(resp.Body)iferr!=nil{returnerr}fmt.Println(resp.Status)fmt.Println(string(resp_body))returnnil}
//sampleusagefuncmain(){target_url:="http://localhost:9090/upload"filename:="./astaxie.pdf"postFile(filename,target_url)}
Theaboveexampleshowsyouhowtouseaclienttouploadfiles.Itusesmultipart.WritetowritefilesintocacheandsendsthemtotheserverthroughthePOSTmethod.
Ifyouhaveotherfieldsthatneedtowriteintodata,likeusername,callmultipart.WriteFieldasneeded.
DirectoryPrevioussection:DuplicatesubmissionsNextsection:Summary
Links
Inthischapter,wemainlylearnedhowtoprocessformdatainGothroughseveralexampleslikelogginginusersanduploadingfiles.Wealsoemphasizedthatverifyinguserdataisextremelyimportantforwebsitesecurity,andweusedonesectiontotalkabouthowtofilterdatawithregularexpressions.
Ihopethatyounowknowmoreaboutthecommunicationprocessbetweenclientandserver.
DirectoryPrevioussection:FileuploadNextchapter:Database
4.6Summary
Links
Forwebdevelopers,thedatabaseisatthecoreofwebdevelopment.Youcansavealmostanythingintoadatabaseandqueryorupdatedatainsideit,likeuserinformation,productsornewsarticles.
Godoesn'tprovideanydatabasedrivers,butitdoeshaveadriverinterfacedefinedinthedatabase/sqlpackage.Peoplecandevelopdatabasedriversbasedonthatinterface.Insection5.1,wearegoingtotalkaboutdatabasedriverinterfacedesigninGo;insections5.2to5.4,IwillintroducesomeSQLdatabasedriverstoyou;insection5.5,i'llpresenttheORMthati'vedevelopedwhichisbasedonthedatabase/sqlinterfacestandard.It'scompatiblewithmostdriversthathaveimplementedthedatabase/sqlinterface,anditmakesiteasytoaccessdatabasesidiomaticallyinGo.
NoSQLhasbeenahottopicinrecentyears.MorewebsitesaredecidingtouseNoSQLdatabasesastheirmaindatabaseinsteadofjustforthepurposeofcaching.IwillintroduceyoutotwoNoSQLdatabases,whichareMongoDBandRedis,insection5.6.
DirectoryPreviousChapter:Chapter4SummaryNextsection:database/sqlinterface
5Database
Links
Godoesn'tprovideanyofficialdatabasedrivers,unlikeotherlanguageslikePHPwhichdo.However,itdoeshavesomedatabasedriverinterfacestandardsfordeveloperstodevelopdatabasedriverswith.Theadvantageisthatifyourcodeisdevelopedaccordingtotheseinterfacestandards,youwillnotneedtochangeanycodeifyourdatabasechanges.Let'sseewhatthesedatabaseinterfacestandardsare.
Thisfunctionisinthedatabase/sqlpackageforregisteringdatabasedriverswhenyouusethird-partydatabasedrivers.AlloftheseshouldcalltheRegister(namestring,driverdriver.Driver)functionininit()inordertoregisterthemselves.
Let'stakealookatthecorrespondingmymysqlandsqlite3drivercode:
//https://github.com/mattn/go-sqlite3driverfuncinit(){sql.Register("sqlite3",&SQLiteDriver{})}
//https://github.com/mikespook/mymysqldriver//Driverautomaticallyregisteredindatabase/sqlvard=Driver{proto:"tcp",raddr:"127.0.0.1:3306"}funcinit(){Register("SETNAMESutf8")sql.Register("mymysql",&d)}
Weseethatallthird-partydatabasedrivershaveimplementedthisfunctiontoregisterthemselves,andGousesamaptosaveuserdriversinsideofdatabse/sql.
vardrivers=make(map[string]driver.Driver)
drivers[name]=driver
Therefore,thisregisterfunctioncanregisterdriversasmanyasyouwantwithdifferentnames.
Wealwaysseethefollowingcodewhenweusethird-partydrivers:
import("database/sql"_"github.com/mattn/go-sqlite3")
Heretheunderscore(alsoknownasa'blank')_canbequiteconfusingformanybeginners,butthisisagreatfeatureinGo.Wealreadyknowthatthisidentifierisfordiscardingvaluesfromfunctionreturns,andalsothatyoumustuseallpackagesthatyou'veimportedinyourcodeinGo.Sowhentheblankisusedwithimport,itmeansthatyouneedtoexecutetheinit()functionofthatpackagewithoutdirectlyusingit,whichexactlyfitstheuse-caseforregisteringdatabasedrivers.
DriverisaninterfacecontaininganOpen(namestring)methodthatreturnsaConninterface.
typeDriverinterface{
5.1database/sqlinterface
sql.Register
driver.Driver
Open(namestring)(Conn,error)}
Thisisaone-timeConn,whichmeansitcanonlybeusedonceinonegoroutine.Thefollowingcodewillcauseerrorstooccur:
...gogoroutineA(Conn)//querygogoroutineB(Conn)//insert...
BecauseGohasnoideawhichgoroutinedoeswhatoperation,thequeryoperationmaygettheresultoftheinsertoperation,andvice-versa.
Allthird-partydriversshouldhavethisfunctiontoparsethenameofConnandreturnthecorrectresults.
Thisisadatabaseconnectioninterfacewithsomemethods,andasi'vesaidabove,thesameConncanonlybeusedinonegoroutine.
typeConninterface{Prepare(querystring)(Stmt,error)Close()errorBegin()(Tx,error)}
PreparereturnsthepreparestatusofcorrespondingSQLcommandsforqueryinganddeleting,etc.Closeclosesthecurrentconnectionandcleansresources.Mostthird-partydriversimplementsomekindofconnectionpool,soyoudon'tneedtocacheconnectionsunlessyouwanttohaveunexpectederrors.BeginreturnsaTxthatrepresentsatransactionhandle.Youcanuseitforquerying,updating,rollingbacktransactions,etc.
ThisisareadystatusthatcorrespondswithConn,soitcanonlybeusedinonegoroutinelikeConn.
typeStmtinterface{Close()errorNumInput()intExec(args[]Value)(Result,error)Query(args[]Value)(Rows,error)}
Closeclosesthecurrentconnectionbutstillreturnsrowdataifitisexecutingaqueryoperation.NumInputreturnsthenumberofobligatearguments.Databasedriversshouldchecktheircaller'sargumentswhentheresultisgreaterthan0,anditreturns-1whendatabasedriversdon'tknowanyobligateargument.Execexecutestheupdate/insertSQLcommandspreparedinPrepare,returnsResult.QueryexecutestheselectSQLcommandpreparedinPrepare,returnsrowdata.
Generally,transactionhandlesonlyhavesubmitorrollbackmethods,anddatabasedriversonlyneedtoimplementthesetwomethods.
driver.Conn
driver.Stmt
driver.Tx
typeTxinterface{Commit()errorRollback()error}
Thisisanoptionalinterface.
typeExecerinterface{Exec(querystring,args[]Value)(Result,error)}
Ifthedriverdoesn'timplementthisinterface,whenyoucallDB.Exec,itwillautomaticallycallPrepare,thenreturnStmt.AfterthatitexecutestheExecmethodofStmt,thenclosesStmt.
Thisistheinterfaceforresultsofupdate/insertoperations.
typeResultinterface{LastInsertId()(int64,error)RowsAffected()(int64,error)}
LastInsertIdreturnsauto-incrementIdnumberafteradatabaseinsertoperation.RowsAffectedreturnsrowsthatwereaffectedbyqueryoperations.
Thisistheinterfacefortheresultofaqueryoperation.
typeRowsinterface{Columns()[]stringClose()errorNext(dest[]Value)error}
Columnsreturnsfieldinformationofdatabasetables.Theslicehasaone-to-onecorrespondencewithSQLqueryfieldsonly,anddoesnotreturnallfieldsofthatdatabasetable.CloseclosesRowsiterator.Nextreturnsnextdataandassignstodest,convertingallstringsintobytearrays,andgetsio.EOFerrorifnomoredataisavailable.
Thisisanaliasofint64,butitimplementstheResultinterface.
typeRowsAffectedint64
func(RowsAffected)LastInsertId()(int64,error)
func(vRowsAffected)RowsAffected()(int64,error)
driver.Execer
driver.Result
driver.Rows
driver.RowsAffected
Thisisanemptyinterfacethatcancontainsanykindofdata.
typeValueinterface{}
TheValuemustbesomethingthatdriverscanoperateonornil,soitshouldbeoneoffollowingtypes:
int64float64bool[]bytestring[*]ExceptRows.Nextwhichcannotreturnstringtime.Time
Thisdefinesaninterfaceforconvertingnormalvaluestodriver.Value.
typeValueConverterinterface{ConvertValue(vinterface{})(Value,error)}
Thisinterfaceiscommonlyusedindatabasedriversandhasmanyusefulfeatures:
Convertsdriver.Valuetoacorrespondingdatabasefieldtype,forexampleconvertsint64touint16.Convertsdatabasequeryresultstodriver.Value.Convertsdriver.Valuetoauserdefinedvalueinthescanfunction.
Thisdefinesaninterfaceforreturningdriver.Value.
typeValuerinterface{Value()(Value,error)}
Manytypesimplementthisinterfaceforconversionbetweendriver.Valueanditself.
Atthispoint,youshouldknowabitaboutdeveloppingdatabasedriversinGo.Onceyoucanimplementinterfacesforoperationslikeadd,delete,update,etc.,thereareonlyafewproblemsleftrelatedtocommunicatingwithspecificdatabases.
databse/sqldefinesevenmorehigh-levelmethodsontopofdatabase/sql/driverformoreconvenientdatabaseoperations,anditsuggeststhatyouimplementaconnectionpool.
typeDBstruct{driverdriver.Driver
driver.Value
driver.ValueConverter
driver.Valuer
database/sql
dsnstringmusync.Mutex//protectsfreeConnandclosedfreeConn[]driver.Connclosedbool}
Asyoucansee,theOpenfunctionreturnsaDBthathasafreeConn,andthisisasimpleconnectionpool.Itsimplementationisverysimpleandugly.Itusesdeferdb.putConn(ci,err)intheDb.preparefunctiontoputaconnectionintotheconnectionpool.EverytimeyoucalltheConnfunction,itchecksthelengthoffreeCoon.Ifit'sgreaterthan0,thatmeansthereisareusableconnectionanditdirectlyreturnstoyou.Otherwiseitcreatesanewconnectionandreturns.
DirectoryPrevioussection:DatabaseNextsection:MySQL
Links
TheLAMPstackhasbeenverypopularontheinternetinrecentyears,andtheMinLAMPstandforMySQL.MySQLisfamousbecauseit'sopensourceandeasytouse.Assuch,itbecamethedefactodatabaseintheback-endsofmanywebsites.
ThereareacoupleofdriversthatsupportMySQLinGo.Someofthemimplementthedatabase/sqlinterface,andothersusetheirowninterfacestandards.
https://github.com/go-sql-driver/mysqlsupportsdatabase/sql,writteninpureGo.https://github.com/ziutek/mymysqlsupportsdatabase/sqlanduserdefinedinterfaces,writteninpureGo.https://github.com/Philio/GoMySQLonlysupportsuserdefinedinterfaces,writteninpureGo.
I'llusethefirstdriverinthefollowingexamples(Iusethisoneinmypersonalprojectstoo),andIalsorecommendthatyouuseitforthefollowingreasons:
It'sanewdatabasedriverandsupportsmorefeatures.Fullysupportsdatabse/sqlinterfacestandards.Supportskeepalive,longconnectionswiththread-safety.
Inthefollowingsections,I'llusethesamedatabasetablestructurefordifferentdatabases,thencreateSQLasfollows:
CREATETABLE`userinfo`(`uid`INT(10)NOTNULLAUTO_INCREMENT,`username`VARCHAR(64)NULLDEFAULTNULL,`departname`VARCHAR(64)NULLDEFAULTNULL,`created`DATENULLDEFAULTNULL,PRIMARYKEY(`uid`));
Thefollowingexampleshowshowtooperateonadatabasebasedonthedatabase/sqlinterfacestandards.
packagemain
import(_"github.com/go-sql-driver/mysql""database/sql""fmt")
funcmain(){db,err:=sql.Open("mysql","astaxie:astaxie@/test?charset=utf8")checkErr(err)
//insertstmt,err:=db.Prepare("INSERTuserinfoSETusername=?,departname=?,created=?")checkErr(err)
res,err:=stmt.Exec("astaxie","","2012-12-09")checkErr(err)
id,err:=res.LastInsertId()checkErr(err)
fmt.Println(id)//updatestmt,err=db.Prepare("updateuserinfosetusername=?whereuid=?")
5.2MySQL
MySQLdrivers
Samples
checkErr(err)
res,err=stmt.Exec("astaxieupdate",id)checkErr(err)
affect,err:=res.RowsAffected()checkErr(err)
fmt.Println(affect)
//queryrows,err:=db.Query("SELECT*FROMuserinfo")checkErr(err)
forrows.Next(){varuidintvarusernamestringvardepartmentstringvarcreatedstringerr=rows.Scan(&uid,&username,&department,&created)checkErr(err)fmt.Println(uid)fmt.Println(username)fmt.Println(department)fmt.Println(created)}
//deletestmt,err=db.Prepare("deletefromuserinfowhereuid=?")checkErr(err)
res,err=stmt.Exec(id)checkErr(err)
affect,err=res.RowsAffected()checkErr(err)
fmt.Println(affect)
db.Close()
}
funccheckErr(errerror){iferr!=nil{panic(err)}}
Letmeexplainafewoftheimportantfunctionshere:
sql.Open()opensaregistereddatabasedriver.TheGo-MySQL-Driverregisteredthemysqldriverhere.ThesecondargumentistheDSN(DataSourceName)thatdefinesinformationpertainingtothedatabaseconnection.Itsupportsfollowingformats:
user@unix(/path/to/socket)/dbname?charset=utf8user:password@tcp(localhost:5555)/dbname?charset=utf8user:password@/dbnameuser:password@tcp([de:ad:be:ef::ca:fe]:80)/dbname
db.Prepare()returnsaSQLoperationthatisgoingtobeexecuted.ItalsoreturnstheexecutionstatusafterexecutingSQL.
db.Query()executesSQLandreturnsaRowsresult.stmt.Exec()executesSQLthathasbeenpreparedandstoredinStmt.
Notethatweusetheformat=?topassarguments.ThisisnecessaryforpreventingSQLinjectionattacks.
Links
DirectoryPrevioussection:database/sqlinterfaceNextsection:SQLite
SQLiteisanopensource,embeddedrelationaldatabase.Ithasaself-contained,zero-configurationandtransaction-supporteddatabaseengine.Itscharacteristicsarehighlyportable,easytouse,compact,efficientandreliable.Inmostofcases,youonlyneedabinaryfileofSQLitetocreate,connectandoperateadatabase.Ifyouarelookingforanembeddeddatabasesolution,SQLiteisworthconsidering.YoucansaySQLiteistheopensourceversionofAccess.
TherearemanydatabasedriversforSQLiteinGo,butmanyofthemdonotsupportthedatabase/sqlinterfacestandards.
https://github.com/mattn/go-sqlite3supportsdatabase/sql,basedoncgo.https://github.com/feyeleanor/gosqlite3doesn'tsupportdatabase/sql,basedoncgo.https://github.com/phf/go-sqlite3doesn'tsupportdatabase/sql,basedoncgo.
Thefirstdriveristheonlyonethatsupportsthedatabase/sqlinterfacestandardinitsSQLitedriver,soIusethisinmyprojects-itwillmakeiteasytomigratemycodeinthefutureifIneedto.
WecreatethefollowingSQL:
CREATETABLE`userinfo`(`uid`INTEGERPRIMARYKEYAUTOINCREMENT,`username`VARCHAR(64)NULL,`departname`VARCHAR(64)NULL,`created`DATENULL);
Anexample:
packagemain
import("database/sql""fmt"_"github.com/mattn/go-sqlite3")
funcmain(){db,err:=sql.Open("sqlite3","./foo.db")checkErr(err)
//insertstmt,err:=db.Prepare("INSERTINTOuserinfo(username,departname,created)values(?,?,?)")checkErr(err)
res,err:=stmt.Exec("astaxie","","2012-12-09")checkErr(err)
id,err:=res.LastInsertId()checkErr(err)
fmt.Println(id)//updatestmt,err=db.Prepare("updateuserinfosetusername=?whereuid=?")checkErr(err)
res,err=stmt.Exec("astaxieupdate",id)checkErr(err)
affect,err:=res.RowsAffected()checkErr(err)
5.3SQLite
SQLitedrivers
Samples
fmt.Println(affect)
//queryrows,err:=db.Query("SELECT*FROMuserinfo")checkErr(err)
forrows.Next(){varuidintvarusernamestringvardepartmentstringvarcreatedstringerr=rows.Scan(&uid,&username,&department,&created)checkErr(err)fmt.Println(uid)fmt.Println(username)fmt.Println(department)fmt.Println(created)}
//deletestmt,err=db.Prepare("deletefromuserinfowhereuid=?")checkErr(err)
res,err=stmt.Exec(id)checkErr(err)
affect,err=res.RowsAffected()checkErr(err)
fmt.Println(affect)
db.Close()
}
funccheckErr(errerror){iferr!=nil{panic(err)}}
Youmayhavenoticedthatthecodeisalmostthesameasintheprevioussection,andthatweonlychangedthenameoftheregistereddriverandcalledsql.OpentoconnecttoSQLiteinadifferentway.
Asafinalnoteonthissecton,thereisausefulSQLitemanagementtoolavailable:http://sqliteadmin.orbmu2k.de/
DirectoryPrevioussection:MySQLNextsection:PostgreSQL
Links
PostgreSQLisanobject-relationaldatabasemanagementsystemavailableformanyplatformsincludingLinux,FreeBSD,Solaris,MicrosoftWindowsandMacOSX.ItisreleasedunderanMIT-stylelicense,andisthusfreeandopensourcesoftware.It'slargerthanMySQLbecauseit'sdesignedforenterpriseusagelikeOracle.Postgresqlisgoodchoiceforenterprisetypeprojects.
TherearemanydatabasedriversavailableforPostgreSQL.Herearethreeexamplesofthem:
https://github.com/bmizerany/pqsupportsdatabase/sql,writteninpureGo.https://github.com/jbarham/gopgsqldriversupportsdatabase/sql,writteninpureGo.https://github.com/lxn/go-pgsqlsupportsdatabase/sql,writteninpureGo.
I'llusethefirstoneinmyfollowingexamples.
WecreatethefollowingSQL:
CREATETABLEuserinfo(uidserialNOTNULL,usernamecharactervarying(100)NOTNULL,departnamecharactervarying(500)NOTNULL,Createddate,CONSTRAINTuserinfo_pkeyPRIMARYKEY(uid))WITH(OIDS=FALSE);
Anexample:
packagemain
import("database/sql""fmt"_"github.com/lib/pq""time")
const(DB_USER="postgres"DB_PASSWORD="postgres"DB_NAME="test")
funcmain(){dbinfo:=fmt.Sprintf("user=%spassword=%sdbname=%ssslmode=disable",DB_USER,DB_PASSWORD,DB_NAME)db,err:=sql.Open("postgres",dbinfo)checkErr(err)deferdb.Close()
fmt.Println("#Insertingvalues")
varlastInsertIdinterr=db.QueryRow("INSERTINTOuserinfo(username,departname,created)VALUES($1,$2,$3)returninguid;","astaxie","","2012-12-09").Scan(&lastInsertId)checkErr(err)fmt.Println("lastinsertedid=",lastInsertId)
fmt.Println("#Updating")
5.4PostgreSQL
PostgreSQLdrivers
Samples
stmt,err:=db.Prepare("updateuserinfosetusername=$1whereuid=$2")checkErr(err)
res,err:=stmt.Exec("astaxieupdate",lastInsertId)checkErr(err)
affect,err:=res.RowsAffected()checkErr(err)
fmt.Println(affect,"rowschanged")
fmt.Println("#Querying")rows,err:=db.Query("SELECT*FROMuserinfo")checkErr(err)
forrows.Next(){varuidintvarusernamestringvardepartmentstringvarcreatedtime.Timeerr=rows.Scan(&uid,&username,&department,&created)checkErr(err)fmt.Println("uid|username|department|created")fmt.Printf("%3v|%8v|%6v|%6v\n",uid,username,department,created)}
fmt.Println("#Deleting")stmt,err=db.Prepare("deletefromuserinfowhereuid=$1")checkErr(err)
res,err=stmt.Exec(lastInsertId)checkErr(err)
affect,err=res.RowsAffected()checkErr(err)
fmt.Println(affect,"rowschanged")}
funccheckErr(errerror){iferr!=nil{panic(err)}}
NotethatPostgreSQLusesthe$1,$2formatinsteadofthe?thatMySQLuses,andithasadifferentDSNformatinsql.Open.AnotherthingisthatthePostgresdriverdoesnotsupportsql.Result.LastInsertId().Soinsteadofthis,
stmt,err:=db.Prepare("INSERTINTOuserinfo(username,departname,created)VALUES($1,$2,$3);")res,err:=stmt.Exec("astaxie","","2012-12-09")fmt.Println(res.LastInsertId())
usedb.QueryRow()and.Scan()togetthevalueforthelastinsertedid.
err=db.QueryRow("INSERTINTOTABLE_NAMEvalues($1)returninguid;",VALUE1").Scan(&lastInsertId)fmt.Println(lastInsertId)
DirectoryPrevioussection:SQLiteNextsection:DevelopORMbasedonbeedb
Links
(Projectbeedbisnolongermaintained,butthecodesstillthere)
beedbisanORM(object-relationalmapper)developedinGo,byme.ItusesidiomaticGotooperateondatabases,implementingstructtodatabasemappingandactsasalightweightGoORMframework.ThepurposeofdevelopingthisORMisnotonlytohelppeoplelearnhowtowriteanORM,butalsotofindagoodbalancebetweenfunctionalityandperformancewhenitcomestodatapersistence.
beedbisanopensourceprojectthatsupportsbasicORMfunctionality,butdoesn'tsupportassociationqueries.
Becausebeedbsupportsdatabase/sqlinterfacestandards,anydriverthatimplementsthisinterfacecanbeusedwithbeedb.I'vetestedthefollowingdrivers:
Mysql:github.com/ziutek/mymysql/godrv
Mysql:code.google.com/p/go-mysql-driver
PostgreSQL:github.com/bmizerany/pq
SQLite:github.com/mattn/go-sqlite3
MSADODB:github.com/mattn/go-adodb
ODBC:bitbucket.org/miquella/mgodbc
Youcanusegogettoinstallbeedblocally.
gogetgithub.com/astaxie/beedb
First,youhavetoimportallthenecessarypackages:
import("database/sql""github.com/astaxie/beedb"_"github.com/ziutek/mymysql/godrv")
Thenyouneedtoopenadatabaseconnectionandcreateabeedbobject(MySQLinthisexample):
db,err:=sql.Open("mymysql","test/xiemengjun/123456")iferr!=nil{panic(err)}orm:=beedb.New(db)
beedb.New()actuallyhastwoarguments.Thefirstisthethedatabaseobject,andthesecondisforindicatingwhichdatabaseengineyou'reusing.Ifyou'reusingMySQL/SQLite,youcanjustskipthesecondargument.
5.5Develo