Grundlegende MVC -Webanwendung in Go
Dieses Projekt zeigt, wie man eine Website mithilfe der Go -Sprache ohne Framework strukturiert und erstellt. Es gibt einen Blog-Artikel, den Sie unter http://www.josephspurrier.com/go-web-app-app-example/ lesen können. Es gibt eine vollständige Anwendung, die ich mit einer früheren Version des Projekts unter https://github.com/verifiedNinja/Webapp erstellt habe. Es gibt eine API -Version dieses Projekts unter https://github.com/josephspurrier/gowebapi.
Führen Sie den folgenden Befehl aus: Führen Sie den folgenden Befehl aus:
go get github.com/josephspurrier/gowebapp
Wenn Sie auf Go 1.5 sind, müssen Sie Govendorexperiment auf 1. Setzen. Wenn Sie in Go 1.4 oder früher sind, funktioniert der Code nicht, da er den Herstellerordner verwendet.
Die Datei gowebapp.db wird erstellt, sobald Sie die Anwendung starten.
Erstellen und laufen Sie aus dem Stammverzeichnis. Öffnen Sie Ihren Webbrowser für: http: // localhost. Sie sollten die Begrüßungsseite sehen.
Navigieren Sie zur Anmeldeseite und dann zur Register -Seite. Erstellen Sie einen neuen Benutzer und Sie sollten sich anmelden können. Das war's.
Starten Sie MongoDB.
Öffnen Sie config/config.json und bearbeiten Sie den Datenbankabschnitt, damit die Verbindungsinformationen mit Ihrer MongoDB -Instanz übereinstimmen. Wechseln Sie auch den Typ von Bolt zu MongoDB.
Erstellen und laufen Sie aus dem Stammverzeichnis. Öffnen Sie Ihren Webbrowser für: http: // localhost. Sie sollten die Begrüßungsseite sehen.
Navigieren Sie zur Anmeldeseite und dann zur Register -Seite. Erstellen Sie einen neuen Benutzer und Sie sollten sich anmelden können. Das war's.
Starten Sie MySQL und importieren Sie config/mysql.sql, um die Datenbank und Tabellen zu erstellen.
Öffnen Sie config/config.json und bearbeiten Sie den Datenbankabschnitt, damit die Verbindungsinformationen Ihrer MySQL -Instanz entsprechen. Ändern Sie auch den Typ von Bolt zu MySQL.
Erstellen und laufen Sie aus dem Stammverzeichnis. Öffnen Sie Ihren Webbrowser für: http: // localhost. Sie sollten die Begrüßungsseite sehen.
Navigieren Sie zur Anmeldeseite und dann zur Register -Seite. Erstellen Sie einen neuen Benutzer und Sie sollten sich anmelden können. Das war's.
Die Web -App verfügt über eine öffentliche Homepage, eine authentifizierte Homepage, eine Anmeldeseite, eine Registrierungsseite, über die Seite und einen einfachen Notizblock, um die CRUD -Operationen zu demonstrieren.
Der Einstiegspunkt für die Web -App ist gowebapp.go. Die Datei lädt die Anwendungseinstellungen, startet die Sitzung, stellt eine Verbindung zur Datenbank her, stellt die Vorlagen ein, lädt die Routen, hängt die Middleware an und startet den Webserver.
Das vordere Ende wird mit Bootstrap mit ein paar kleinen Änderungen an Schriftarten und Abstand gebaut. Die Flash -Nachrichten werden angepasst, sodass sie unten rechts auf dem Bildschirm angezeigt werden.
Alle Fehler- und Warnmeldungen sollten entweder entweder dem Benutzer oder in der Konsole angezeigt werden. Informationsnachrichten werden dem Benutzer über Flash -Nachrichten angezeigt, die nach 4 Sekunden verschwinden. Die Flash -Nachrichten werden von JavaScript im statischen Ordner gesteuert.
Vor kurzem hat sich die Ordnerstruktur geändert. Nachdem ich mir alle Gabeln angesehen und mein Projekt an verschiedenen Orten wiederverwendet hatte, entschied ich mich, den GO -Code in den App -Ordner im Ordner des Lieferanten zu verschieben, damit der GitHub -Pfad nicht über die vielen Importe übersät ist. Ich wollte keine relativen Pfade verwenden, daher schien der Anbieterordner die beste Option zu sein.
Das Projekt ist in die folgenden Ordner organisiert:
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
Es gibt ein paar externe Pakete:
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
Die Vorlagen werden unter dem Vorlagenordner in Ordner organisiert:
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
Es gibt einige Vorlagenfunktionen, die für die Arbeit mit den Vorlagen und statischen Dateien verfügbar sind:
<!-- 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/2006Es gibt einige Variablen, die Sie auch in Vorlagen verwenden können:
<!-- 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}} " >Es ist auch einfach, vor dem Schließen und Tags einen vorlagenspezifischen Code hinzuzufügen:
<!-- 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}}Sie können eine Flash -Benachrichtigung mit JavaScript auslösen.
flashError ( "You must type in a username." ) ;
flashSuccess ( "Record created!" ) ;
flashNotice ( "There seems to be a piece missing." ) ;
flashWarning ( "Something does not seem right..." ) ; Die Controller -Dateien teilen alle den gleichen Paketnamen. Dies senkt die Anzahl der Pakete, wenn Sie die Routen abbilden. Es zwingt Sie auch, für jedes der Funcs eine gute Namenskonvention zu verwenden, damit Sie wissen, wo sich jede der Funktionen befindet und auf welche Art von HTTP -Anfrage sie jeweils zugeordnet sind.
Zugriff auf eine Gorilla -Sitzung:
// Get the current session
sess := session . Instance ( r )
...
// Close the session after you are finished making changes
sess . Save ( r , w )Trigger 1 von 4 verschiedenen Arten von Flash -Nachrichten auf der nächsten Seite (kein anderer Code erforderlich):
sess . AddFlash (view. Flash { "Sorry, no brute force :-)" , view . FlashNotice })
sess . Save ( r , w ) // Ensure you save the session after making a change to itDie Felder des Formulars sind nicht leer:
// 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
}Eine Vorlage rendern:
// 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 )Geben Sie die Flash -Nachrichten während einer AJAX -Anfrage zurück:
// 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 )Behandeln Sie die Datenbankabfrage aus:
// 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
}Senden Sie eine 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
}Validieren Sie ein Formular, wenn der Google Recaptcha in der Konfiguration aktiviert ist:
// 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 ist eine gute Idee, die Datenbankschicht abstrahieren. Wenn Sie also Änderungen vornehmen müssen, müssen Sie keine Geschäftslogik durchsuchen, um die Abfragen zu finden. Alle Abfragen werden im Modelsordner gespeichert.
Dieses Projekt unterstützt BoltDB, MongoDB und MySQL. Alle Abfragen werden in denselben Dateien gespeichert, sodass Sie die Datenbank problemlos ändern können, ohne die Konfigurationsdatei zu ändern.
Die Dateien user.go und note.go befinden sich im Root des Modellverzeichnisses und sind eine Einführung aller Abfragen für jeden Datenbanktyp. Es gibt einige Hacks in den Modellen, um die Strukturen mit allen unterstützten Datenbanken zu erarbeiten.
Stellen Sie eine Verbindung zur Datenbank her (nur wenn Sie in Ihrer Anwendung benötigt werden):
// Connect to database
database . Connect ( config . Database )Lesen Sie aus der Datenbank:
result := User {}
err := database . DB . Get ( & result , "SELECT id, password, status_id, first_name FROM user WHERE email = ? LIMIT 1" , email )
return result , errSchreiben Sie in die Datenbank:
_ , err := database . DB . Exec ( "INSERT INTO user (first_name, last_name, email, password) VALUES (?,?,?,?)" , firstName , lastName , email , password )
return err Es sind ein paar Middleware enthalten. Das Paket namens CSRFBanana schützt vor Anfragen von Anfragen von Cross-Site-Anfragen und verhindert doppelte Abgaben. Das Paket httprouterWrapper bietet Helferfunktionen, um Funktionen mit HTTProuter kompatibel zu machen. Das Paket -LogRequest protokolliert jede Anfrage, die gegen die Website an die Konsole gestellt wurde. Das Paket pProfHandler ermöglicht PPROF, so dass es mit HttProuter funktioniert. In Route.go nutzen alle einzelnen Routen Alice, um die Verkettung sehr einfach zu machen.
Um die Web -App etwas flexibler zu gestalten, können Sie über die Datei config.json Änderungen an verschiedenen Komponenten an einem Ort vornehmen. Wenn Sie eine Ihrer eigenen Einstellungen hinzufügen möchten, können Sie diese zu config.json hinzufügen und die Strukturen in gowebapp.go und die einzelnen Dateien aktualisieren, damit Sie sie in Ihrem Code verweisen können. Dies ist 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
}
}Um HTTPS zu aktivieren, setzen Sie Usehttps auf true fest, erstellen Sie einen Ordner namens TLS im Stamm und platzieren Sie dann das Zertifikat und die Schlüsseldateien in diesem Ordner.
Öffentliches Haus:
Um:
Registrieren:
Login:
Authentifiziertes Zuhause:
Notizen anzeigen:
Hinweis hinzufügen:
Hinweis bearbeiten:
Alle Feedback sind willkommen. Lassen Sie mich wissen, ob Sie Vorschläge, Fragen oder Kritikpunkte haben. Wenn etwas nicht idiomatisch ist, lassen Sie es mich bitte wissen, damit wir es besser machen können.