Aplicativo Basic MVC Web em Go
Este projeto demonstra como estruturar e construir um site usando o idioma Go sem uma estrutura. Há um artigo do blog que você pode ler em http://www.josephspurrier.com/go-web-app-example/. Há um aplicativo completo que criei com uma versão anterior do projeto em https://github.com/verifedninja/webapp. Há uma versão da API deste projeto em https://github.com/josephspurrier/gowebapi.
Para baixar, execute o seguinte comando:
go get github.com/josephspurrier/gowebapp
Se você estiver em Go 1.5, precisará definir o Govendorexperiment para 1. Se você estiver em Go 1,4 ou anterior, o código não funcionará porque ele usa a pasta do fornecedor.
O arquivo gowebapp.db será criado assim que você iniciar o aplicativo.
Construir e fugir do diretório raiz. Abra seu navegador da web para: http: // localhost. Você deve ver a página de boas -vindas.
Navegue até a página de login e, em seguida, até a página de registro. Crie um novo usuário e você poderá fazer login. É isso.
Inicie o MongoDB.
Abra Config/Config.json e edite a seção do banco de dados para que as informações de conexão correspondam à sua instância do MongoDB. Além disso, altere o tipo de parafuso para MongoDB.
Construir e fugir do diretório raiz. Abra seu navegador da web para: http: // localhost. Você deve ver a página de boas -vindas.
Navegue até a página de login e, em seguida, até a página de registro. Crie um novo usuário e você poderá fazer login. É isso.
Inicie o MySQL e importe Config/mysql.sql para criar o banco de dados e as tabelas.
Abra Config/Config.json e edite a seção do banco de dados para que as informações de conexão correspondam à sua instância MySQL. Além disso, altere o tipo de parafuso para MySQL.
Construir e fugir do diretório raiz. Abra seu navegador da web para: http: // localhost. Você deve ver a página de boas -vindas.
Navegue até a página de login e, em seguida, até a página de registro. Crie um novo usuário e você poderá fazer login. É isso.
O aplicativo da web possui uma página inicial pública, página inicial autenticada, página de login, página de registro, página sobre página e um simples bloco de notas para demonstrar as operações do CRUD.
O ponto de entrada para o aplicativo da web é Gowebapp.go. O arquivo carrega as configurações do aplicativo, inicia a sessão, se conecta ao banco de dados, configura os modelos, carrega as rotas, anexa o middleware e inicia o servidor da Web.
A extremidade frontal é construída usando Bootstrap com algumas pequenas alterações nas fontes e espaçamento. As mensagens flash são personalizadas para que elas apareçam no canto inferior direito da tela.
Todas as mensagens de erro e aviso devem ser exibidas para o usuário ou no console. As mensagens informativas são exibidas ao usuário por meio de mensagens flash que desaparecem após 4 segundos. As mensagens flash são controladas por JavaScript na pasta estática.
Recentemente, a estrutura da pasta mudou. Depois de olhar para todos os garfos e reutilizar meu projeto em lugares diferentes, decidi mover o código GO para a pasta do aplicativo dentro da pasta do fornecedor para que o caminho do GitHub não fique repleto de muitas importações. Eu não queria usar caminhos relativos, então a pasta do fornecedor parecia a melhor opção.
O projeto está organizado nas seguintes pastas:
config - application settings and database schema
static - location of statically served files like CSS and JS
template - HTML templates
vendor/app/controller - page logic organized by HTTP methods (GET, POST)
vendor/app/shared - packages for templates, MySQL, cryptography, sessions, and json
vendor/app/model - database queries
vendor/app/route - route information and middleware
Existem alguns pacotes externos:
github.com/gorilla/context - registry for global request variables
github.com/gorilla/sessions - cookie and filesystem sessions
github.com/go-sql-driver/mysql - MySQL driver
github.com/haisum/recaptcha - Google reCAPTCHA support
github.com/jmoiron/sqlx - MySQL general purpose extensions
github.com/josephspurrier/csrfbanana - CSRF protection for gorilla sessions
github.com/julienschmidt/httprouter - high performance HTTP request router
github.com/justinas/alice - middleware chaining
github.com/mattn/go-sqlite3 - SQLite driver
golang.org/x/crypto/bcrypt - password hashing algorithm
Os modelos são organizados em pastas sob a pasta do modelo :
about/about.tmpl - quick info about the app
index/anon.tmpl - public home page
index/auth.tmpl - home page once you login
login/login.tmpl - login page
notepad/create.tmpl - create note
notepad/read.tmpl - read a note
notepad/update.tmpl - update a note
partial/footer.tmpl - footer
partial/menu.tmpl - menu at the top of all the pages
register/register.tmpl - register page
base.tmpl - base template for all the pages
Existem algumas funções de modelo disponíveis para facilitar o trabalho com os modelos e arquivos estáticos:
<!-- CSS files with timestamps -->
{{CSS "static/css/normalize3.0.0.min.css"}}
parses to
< link rel =" stylesheet " type =" text/css " href =" /static/css/normalize3.0.0.min.css?1435528339 " />
<!-- JS files with timestamps -->
{{JS "static/js/jquery1.11.0.min.js"}}
parses to
< script type =" text/javascript " src =" /static/js/jquery1.11.0.min.js?1435528404 " > </ script >
<!-- Hyperlinks -->
{{LINK "register" "Create a new account."}}
parses to
< a href =" /register " > Create a new account. </ a >
<!-- Output an unescaped variable (not a safe idea, but I find it useful when troubleshooting) -->
{{.SomeVariable | NOESCAPE}}
<!-- Time format -->
{{.SomeTime | PRETTYTIME}}
parses to format
3:04 PM 01/02/2006Existem algumas variáveis que você pode usar em modelos também:
<!-- Use AuthLevel=auth to determine if a user is logged in (if session.Values["id"] != nil) -->
{{if eq .AuthLevel "auth"}}
You are logged in.
{{else}}
You are not logged in.
{{end}}
<!-- Use BaseURI to print the base URL of the web app -->
< li > < a href =" {{.BaseURI}}about " > About </ a > </ li >
<!-- Use token to output the CSRF token in a form -->
< input type =" hidden " name =" token " value =" {{.token}} " >Também é fácil adicionar código específico do modelo antes do fechamento e das tags:
<!-- Code is added before the closing </head> tag -->
{{define "head"}} < meta name =" robots " content =" noindex " > {{end}}
...
<!-- Code is added before the closing </body> tag -->
{{define "foot"}}{{JS "//www.google.com/recaptcha/api.js"}}{{end}}Você pode desencadear uma notificação flash usando JavaScript.
flashError ( "You must type in a username." ) ;
flashSuccess ( "Record created!" ) ;
flashNotice ( "There seems to be a piece missing." ) ;
flashWarning ( "Something does not seem right..." ) ; Todos os arquivos do controlador compartilham o mesmo nome do pacote. Isso reduz o número de pacotes quando você está mapeando as rotas. Ele também obriga a usar uma boa convenção de nomenclatura para cada um dos funções para saber para onde cada um dos funções está localizado e que tipo de solicitação HTTP para onde cada um é mapeado.
Acesse uma sessão de gorila:
// Get the current session
sess := session . Instance ( r )
...
// Close the session after you are finished making changes
sess . Save ( r , w )Trigger 1 de 4 tipos diferentes de mensagens flash na próxima página Carregar (nenhum outro código necessário):
sess . AddFlash (view. Flash { "Sorry, no brute force :-)" , view . FlashNotice })
sess . Save ( r , w ) // Ensure you save the session after making a change to itValidar campos de formulário não estão vazios:
// Ensure a user submitted all the required form fields
if validate , missingField := view . Validate ( r , [] string { "email" , "password" }); ! validate {
sess . AddFlash (view. Flash { "Field missing: " + missingField , view . FlashError })
sess . Save ( r , w )
LoginGET ( w , r )
return
}Renderizar um modelo:
// Create a new view
v := view . New ( r )
// Set the template name
v . Name = "login/login"
// Assign a variable that is accessible in the form
v . Vars [ "token" ] = csrfbanana . Token ( w , r , sess )
// Refill any form fields from a POST operation
view . Repopulate ([] string { "email" }, r . Form , v . Vars )
// Render the template
v . Render ( w )Retorne as mensagens flash durante uma solicitação de Ajax:
// Get session
sess := session . Instance ( r )
// Set the flash message
sess . AddFlash (view. Flash { "An error occurred on the server. Please try again later." , view . FlashError })
sess . Save ( r , w )
// Display the flash messages as JSON
v := view . New ( r )
v . SendFlashes ( w )Lidar com a consulta do banco de dados:
// Get database result
result , err := model . UserByEmail ( email )
if err == sql . ErrNoRows {
// User does not exist
} else if err != nil {
// Display error message
} else if passhash . MatchString ( result . Password , password ) {
// Password matches!
} else {
// Password does not match
}Envie um e -mail:
// Email a user
err := email . SendEmail ( email . ReadConfig (). From , "This is the subject" , "This is the body!" )
if err != nil {
log . Println ( err )
sess . AddFlash (view. Flash { "An error occurred on the server. Please try again later." , view . FlashError })
sess . Save ( r , w )
return
}Validar um formulário se o Google Recaptcha estiver ativado na configuração:
// Validate with Google reCAPTCHA
if ! recaptcha . Verified ( r ) {
sess . AddFlash (view. Flash { "reCAPTCHA invalid!" , view . FlashError })
sess . Save ( r , w )
RegisterGET ( w , r )
return
}É uma boa ideia abstrair a camada de banco de dados, por isso, se você precisar fazer alterações, não precisará examinar a lógica de negócios para encontrar as consultas. Todas as consultas são armazenadas na pasta Modelos.
Este projeto suporta Boltdb, MongoDB e MySQL. Todas as consultas são armazenadas nos mesmos arquivos para que você possa alterar facilmente o banco de dados sem modificar nada além do arquivo de configuração.
Os arquivos User.Go e Note.Go estão na raiz do diretório de modelos e são uma compliação de todas as consultas para cada tipo de banco de dados. Existem alguns hacks nos modelos para fazer com que as estruturas funcionem com todos os bancos de dados suportados.
Conecte -se ao banco de dados (apenas uma vez necessário em seu aplicativo):
// Connect to database
database . Connect ( config . Database )Leia no banco de dados:
result := User {}
err := database . DB . Get ( & result , "SELECT id, password, status_id, first_name FROM user WHERE email = ? LIMIT 1" , email )
return result , errEscreva no banco de dados:
_ , err := database . DB . Exec ( "INSERT INTO user (first_name, last_name, email, password) VALUES (?,?,?,?)" , firstName , lastName , email , password )
return err Existem algumas peças de middleware incluídas. O pacote chamado CSRFBANANA protege contra ataques de falsificação de solicitação entre sites e evita submetimentos duplos. O pacote HTTProuterWrapper fornece funções auxiliares para tornar o FUNCS compatível com o HTTPROUTER. O pacote LogRequest registrará todas as solicitações feitas no site no console. O pacote PProfHandler permite o PPROF para que funcione com o HTTPROUTER. Na rota.GO, todas as rotas individuais usam Alice para facilitar muito o encadeamento.
Para tornar o aplicativo da web um pouco mais flexível, você pode fazer alterações em diferentes componentes em um só lugar através do arquivo config.json. Se você deseja adicionar alguma de suas próprias configurações, poderá adicioná -las ao config.json e atualizar as estruturas no gowebapp.go e nos arquivos individuais para que você possa fazer referência a eles no seu código. Este é o config.json:
{
"Database" : {
"Type" : " Bolt " ,
"Bolt" : {
"Path" : " gowebapp.db "
},
"MongoDB" : {
"URL" : " 127.0.0.1 " ,
"Database" : " gowebapp "
},
"MySQL" : {
"Username" : " root " ,
"Password" : " " ,
"Name" : " gowebapp " ,
"Hostname" : " 127.0.0.1 " ,
"Port" : 3306 ,
"Parameter" : " ?parseTime=true "
}
},
"Email" : {
"Username" : " " ,
"Password" : " " ,
"Hostname" : " " ,
"Port" : 25 ,
"From" : " "
},
"Recaptcha" : {
"Enabled" : false ,
"Secret" : " " ,
"SiteKey" : " "
},
"Server" : {
"Hostname" : " " ,
"UseHTTP" : true ,
"UseHTTPS" : false ,
"HTTPPort" : 80 ,
"HTTPSPort" : 443 ,
"CertFile" : " tls/server.crt " ,
"KeyFile" : " tls/server.key "
},
"Session" : {
"SecretKey" : " @r4B?EThaSEh_drudR7P_hub=s#s2Pah " ,
"Name" : " gosess " ,
"Options" : {
"Path" : " / " ,
"Domain" : " " ,
"MaxAge" : 28800 ,
"Secure" : false ,
"HttpOnly" : true
}
},
"Template" : {
"Root" : " base " ,
"Children" : [
" partial/menu " ,
" partial/footer "
]
},
"View" : {
"BaseURI" : " / " ,
"Extension" : " tmpl " ,
"Folder" : " template " ,
"Name" : " blank " ,
"Caching" : true
}
}Para ativar o HTTPS, defina o usehttps como true, crie uma pasta chamada TLS na raiz e coloque o certificado e os arquivos de chave nessa pasta.
Casa pública:
Sobre:
Registro:
Conecte-se:
Casa autenticada:
Ver Notas:
Adicionar nota:
Nota de edição:
Todo feedback é bem -vindo. Deixe -me saber se você tiver alguma sugestão, perguntas ou críticas. Se algo não for idiomático, informe -me para que possamos melhorar.