Aplicación web básica de MVC en GO
Este proyecto demuestra cómo estructurar y construir un sitio web utilizando el idioma GO sin un marco. Hay un artículo de blog que puedes leer en http://www.josephspurrier.com/go-web-app-example/. Hay una aplicación completa que construí con una versión anterior del proyecto en https://github.com/verifiedninja/webapp. Hay una versión API de este proyecto en https://github.com/josephspurrier/gowebapi.
Para descargar, ejecute el siguiente comando:
go get github.com/josephspurrier/gowebapp
Si está en GO 1.5, debe establecer GovendorExperiment en 1. Si está en GO 1.4 o antes, el código no funcionará porque usa la carpeta de proveedores.
El archivo gowebapp.db se creará una vez que inicie la aplicación.
Construir y ejecutar desde el directorio raíz. Abra su navegador web a: http: // localhost. Deberías ver la página de bienvenida.
Navegue a la página de inicio de sesión y luego a la página de registro. Cree un nuevo usuario y debería poder iniciar sesión. Eso es todo.
Comienza MongoDB.
Abra config/config.json y edite la sección de la base de datos para que la información de conexión coincida con su instancia de MongoDB. Además, cambie el tipo de perno a MongoDB.
Construir y ejecutar desde el directorio raíz. Abra su navegador web a: http: // localhost. Deberías ver la página de bienvenida.
Navegue a la página de inicio de sesión y luego a la página de registro. Cree un nuevo usuario y debería poder iniciar sesión. Eso es todo.
Inicie MySQL e importe config/mysql.sql para crear la base de datos y las tablas.
Abra config/config.json y edite la sección de la base de datos para que la información de conexión coincida con su instancia mysql. Además, cambie el tipo de Bolt a MySQL.
Construir y ejecutar desde el directorio raíz. Abra su navegador web a: http: // localhost. Deberías ver la página de bienvenida.
Navegue a la página de inicio de sesión y luego a la página de registro. Cree un nuevo usuario y debería poder iniciar sesión. Eso es todo.
La aplicación web tiene una página de inicio pública, página de inicio autenticada, página de inicio de sesión, página de registro, aproximadamente la página y un simple bloc de notas para demostrar las operaciones CRUD.
El punto de entrada para la aplicación web es gowebapp.go. El archivo carga la configuración de la aplicación, inicia la sesión, se conecta a la base de datos, configura las plantillas, carga las rutas, adjunta el middleware e inicia el servidor web.
La parte delantera se construye con Bootstrap con algunos pequeños cambios en las fuentes y el espacio. Los mensajes flash se personalizan para que aparezcan en la parte inferior derecha de la pantalla.
Todos los mensajes de error y advertencia deben mostrarse al usuario o en la consola. Los mensajes informativos se muestran al usuario a través de mensajes flash que desaparecen después de 4 segundos. Los mensajes flash están controlados por JavaScript en la carpeta estática.
Recientemente, la estructura de la carpeta cambió. Después de mirar todas las horquillas y reutilizar mi proyecto en diferentes lugares, decidí mover el código GO a la carpeta de aplicaciones dentro de la carpeta del proveedor para que la ruta GitHub no esté llena de muchas importaciones. No quería usar rutas relativas para que la carpeta del proveedor pareciera la mejor opción.
El proyecto está organizado en las siguientes carpetas:
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
Hay algunos paquetes 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
Las plantillas se organizan en carpetas debajo de la carpeta de plantilla :
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
Hay algunas funciones de plantilla disponibles para facilitar el trabajo con las plantillas y los archivos 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/2006También hay algunas variables que puede usar en plantillas:
<!-- 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}} " >También es fácil agregar código específico de plantilla antes del cierre y las etiquetas:
<!-- 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}}Puede activar una notificación 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..." ) ; Los archivos del controlador comparten el mismo nombre del paquete. Esto reduce la cantidad de paquetes cuando está asignando las rutas. También te obliga a usar una buena convención de nombres para cada uno de los funciones para que sepa dónde se encuentran cada uno de los funciones y a qué tipo de solicitud HTTP se asignan.
Acceder a una sesión de gorila:
// Get the current session
sess := session . Instance ( r )
...
// Close the session after you are finished making changes
sess . Save ( r , w )Activar 1 de 4 tipos diferentes de mensajes flash en la siguiente página de la página (no se necesita otro código):
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 los campos de formulario no están vacíos:
// 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
}Representar una plantilla:
// 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 )Devuelva los mensajes flash durante una solicitud 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 )Manejar la consulta de la base de datos:
// 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
}Enviar un correo electrónico:
// 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 un formulario si el Google Recaptcha está habilitado en la configuración:
// Validate with Google reCAPTCHA
if ! recaptcha . Verified ( r ) {
sess . AddFlash (view. Flash { "reCAPTCHA invalid!" , view . FlashError })
sess . Save ( r , w )
RegisterGET ( w , r )
return
}Es una buena idea abstraer la capa de base de datos, por lo que si necesita hacer cambios, no tiene que mirar a través de la lógica de negocios para encontrar las consultas. Todas las consultas se almacenan en la carpeta de modelos.
Este proyecto admite BoltDB, MongoDB y MySQL. Todas las consultas se almacenan en los mismos archivos para que pueda cambiar fácilmente la base de datos sin modificar nada más que el archivo de configuración.
Los archivos user.go y nota.go están en la raíz del directorio de modelos y son una cumplimiento de todas las consultas para cada tipo de base de datos. Hay algunos hacks en los modelos para que las estructuras funcionen con todas las bases de datos compatibles.
Conéctese a la base de datos (solo una vez necesario en su aplicación):
// Connect to database
database . Connect ( config . Database )Leer de la base de datos:
result := User {}
err := database . DB . Get ( & result , "SELECT id, password, status_id, first_name FROM user WHERE email = ? LIMIT 1" , email )
return result , errEscribe en la base de datos:
_ , err := database . DB . Exec ( "INSERT INTO user (first_name, last_name, email, password) VALUES (?,?,?,?)" , firstName , lastName , email , password )
return err Hay algunas piezas de middleware incluidas. El paquete llamado CSRFBanana protege contra ataques de falsificación de solicitudes de sitios cruzados y evita que se envíen dobles envíos. El paquete httprouterwrapper proporciona funciones auxiliares para hacer que los funciones sean compatibles con httprouter. El paquete LogRequest registrará todas las solicitudes hechas en el sitio web a la consola. El paquete PProfHandler habilita PPROF para que funcione con httprouter. En la ruta. GO, todas las rutas individuales usan Alice para hacer que el encadenamiento sea muy fácil.
Para que la aplicación web sea un poco más flexible, puede hacer cambios en diferentes componentes en un solo lugar a través del archivo config.json. Si desea agregar cualquiera de sus propias configuraciones, puede agregarlos a config.json y actualizar las estructuras en gowebapp.go y los archivos individuales para que pueda hacer referencia a su código. Esto es 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 habilitar HTTPS, establezca UseHTTPS en True, cree una carpeta llamada TLS en la raíz y luego coloque el certificado y los archivos de clave en esa carpeta.
Inicio público:
Acerca de:
Registro:
Acceso:
Inicio autenticado:
Vista de notas:
Agregar nota:
Editar nota:
Todos los comentarios son bienvenidos. Avíseme si tiene alguna sugerencia, pregunta o crítica. Si algo no es idiomático, hágamelo saber para que podamos mejorarlo.