Application Web de base MVC en Go
Ce projet montre comment structurer et créer un site Web en utilisant la langue go sans cadre. Il y a un article de blog que vous pouvez lire sur http://www.josephspurrier.com/go-web-app-example/. Il y a une application complète que j'ai construite avec une version antérieure du projet à https://github.com/verifiedninja/webapp. Il existe une version API de ce projet sur https://github.com/josephspurrier/gowebapi.
Pour télécharger, exécutez la commande suivante:
go get github.com/josephspurrier/gowebapp
Si vous êtes sur Go 1.5, vous devez définir GoVendorexperiment sur 1. Si vous êtes en Go 1.4 ou plus tôt, le code ne fonctionnera pas car il utilise le dossier du fournisseur.
Le fichier gowebapp.db sera créé une fois que vous aurez démarré l'application.
Construire et exécuter à partir du répertoire racine. Ouvrez votre navigateur Web à: http: // localhost. Vous devriez voir la page de bienvenue.
Accédez à la page de connexion, puis à la page Registre. Créez un nouvel utilisateur et vous devriez être en mesure de vous connecter. C'est ça.
Commencez MongoDB.
Ouvrez config / config.json et modifiez la section de la base de données afin que les informations de connexion correspondent à votre instance MongoDB. Aussi, changez le type du boulon à mongodb.
Construire et exécuter à partir du répertoire racine. Ouvrez votre navigateur Web à: http: // localhost. Vous devriez voir la page de bienvenue.
Accédez à la page de connexion, puis à la page Registre. Créez un nouvel utilisateur et vous devriez être en mesure de vous connecter. C'est ça.
Démarrez MySQL et importez config / mysql.sql pour créer la base de données et les tables.
Ouvrez config / config.json et modifiez la section de la base de données afin que les informations de connexion correspondent à votre instance MySQL. Aussi, changez le type du boulon à MySQL.
Construire et exécuter à partir du répertoire racine. Ouvrez votre navigateur Web à: http: // localhost. Vous devriez voir la page de bienvenue.
Accédez à la page de connexion, puis à la page Registre. Créez un nouvel utilisateur et vous devriez être en mesure de vous connecter. C'est ça.
L'application Web a une page d'accueil publique, une page d'accueil authentifiée, une page de connexion, une page de registre, une page à propos et un simple bloc-notes pour démontrer les opérations CRUD.
Le point d'entrée de l'application Web est gowebapp.go. Le fichier charge les paramètres de l'application, démarre la session, se connecte à la base de données, configure les modèles, charge les itinéraires, attache le middleware et démarre le serveur Web.
L'avant est construit à l'aide de bootstrap avec quelques petites modifications des polices et de l'espacement. Les messages flash sont personnalisés afin qu'ils apparaissent en bas à droite de l'écran.
Tous les messages d'erreur et d'avertissement doivent être affichés soit à l'utilisateur ou dans la console. Les messages d'information sont affichés à l'utilisateur via des messages flash qui disparaissent après 4 secondes. Les messages flash sont contrôlés par JavaScript dans le dossier statique.
Récemment, la structure du dossier a changé. Après avoir regardé toutes les fourches et réutilisé mon projet à différents endroits, j'ai décidé de déplacer le code Go vers le dossier de l'application à l'intérieur du dossier du fournisseur afin que le chemin GitHub ne soit pas jonché tout au long des nombreuses importations. Je ne voulais pas utiliser de chemins relatifs, donc le dossier du fournisseur semblait être la meilleure option.
Le projet est organisé dans les dossiers suivants:
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
Il y a quelques forfaits externes:
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
Les modèles sont organisés en dossiers sous le dossier de modèle :
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
Il existe quelques funcs de modèle qui sont disponibles pour faciliter le travail avec les modèles et les fichiers statiques:
<!-- 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/2006Il existe également quelques variables que vous pouvez utiliser dans les modèles:
<!-- 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}} " >Il est également facile d'ajouter du code spécifique au modèle avant la fermeture et les balises:
<!-- 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}}Vous pouvez déclencher une notification Flash à l'aide de 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..." ) ; Les fichiers du contrôleur partagent tous le même nom de package. Cela réduit le nombre de packages lorsque vous mappez les itinéraires. Cela vous oblige également à utiliser une bonne convention de dénomination pour chacune des funcs afin que vous sachiez où se trouvent chacune des funcs et quel type de demande HTTP dans laquelle ils sont cartographiés.
Accédez à une session de gorille:
// Get the current session
sess := session . Instance ( r )
...
// Close the session after you are finished making changes
sess . Save ( r , w )Déclencher 1 des 4 différents types de messages flash sur la charge de la page suivante (aucun autre code nécessaire):
sess . AddFlash (view. Flash { "Sorry, no brute force :-)" , view . FlashNotice })
sess . Save ( r , w ) // Ensure you save the session after making a change to itValider les champs de formulaire ne sont pas vides:
// 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
}Rendez un modèle:
// 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 )Renvoie les messages Flash lors d'une demande 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 )Gérer la requête de la base de données:
// 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
}Envoyer un 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
}Valider un formulaire si le Google Recaptcha est activé dans la configuration:
// Validate with Google reCAPTCHA
if ! recaptcha . Verified ( r ) {
sess . AddFlash (view. Flash { "reCAPTCHA invalid!" , view . FlashError })
sess . Save ( r , w )
RegisterGET ( w , r )
return
}C'est une bonne idée de résumé la couche de base de données, donc si vous avez besoin d'apporter des modifications, vous n'avez pas à examiner la logique métier pour trouver les requêtes. Toutes les requêtes sont stockées dans le dossier des modèles.
Ce projet prend en charge Boltdb, MongoDB et MySQL. Toutes les requêtes sont stockées dans les mêmes fichiers afin que vous puissiez facilement modifier la base de données sans rien modifier que le fichier de configuration.
Les fichiers user.go et note.go sont à la racine du répertoire du modèle et constituent une compréhension de toutes les requêtes pour chaque type de base de données. Il y a quelques hacks dans les modèles pour faire fonctionner les structures avec toutes les bases de données prises en charge.
Connectez-vous à la base de données (une seule fois nécessaire dans votre application):
// Connect to database
database . Connect ( config . Database )Lire dans la base de données:
result := User {}
err := database . DB . Get ( & result , "SELECT id, password, status_id, first_name FROM user WHERE email = ? LIMIT 1" , email )
return result , errÉcrivez dans la base de données:
_ , err := database . DB . Exec ( "INSERT INTO user (first_name, last_name, email, password) VALUES (?,?,?,?)" , firstName , lastName , email , password )
return err Il y a quelques middleware inclus. Le package appelé CSRFBANANA protège contre les attaques de contrefaçon de demande croisée et empêche les doubles soumis. Le package httprouterwrapper offre des fonctions d'assistance pour rendre les funcs compatibles avec httprouter. Le Package LogRequest enregistrera chaque demande faite sur le site Web vers la console. Le package PPROFHandler active PPROF, il fonctionnera donc avec httprouter. Dans Route.go, tous les itinéraires individuels utilisent Alice pour rendre le chaînage très facile.
Pour rendre l'application Web un peu plus flexible, vous pouvez apporter des modifications à différents composants en un seul endroit via le fichier config.json. Si vous souhaitez ajouter l'un de vos propres paramètres, vous pouvez les ajouter à config.json et mettre à jour les structures dans gowebapp.go et les fichiers individuels afin que vous puissiez les référencer dans votre code. C'est 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
}
}Pour activer HTTPS, définissez USEHTTPS sur true, créez un dossier appelé TLS dans la racine, puis placez le certificat et les fichiers clés dans ce dossier.
Maison publique:
À propos de:
Registre:
Se connecter:
Maison authentifiée:
Voir les notes:
Ajouter une note:
Remarque de modification:
Tous les commentaires sont les bienvenus. Faites-moi savoir si vous avez des suggestions, des questions ou des critiques. Si quelque chose n'est pas idiomatique à aller, faites-le moi savoir afin que nous puissions l'améliorer.