GO中的基本MVC Web應用程序
該項目演示瞭如何在沒有框架的情況下使用GO語言構建和構建網站。您可以在http://www.josephspurrier.com/go-web-app-example/上閱讀一篇博客文章。我在https://github.com/verifiedninja/webapp上構建了一個完整的應用程序。該項目的API版本在https://github.com/josephspurrier/gowebapi上。
要下載,請運行以下命令:
go get github.com/josephspurrier/gowebapp
如果您使用的是1.5,則需要將GovendoreXperiment設置為1。如果您使用的是1.4或更早,則該代碼將無法使用,因為它使用了供應商文件夾。
啟動應用程序後,將創建Gowebapp.db文件。
從根目錄構建和運行。打開您的Web瀏覽器至:http:// localhost。您應該看到歡迎頁面。
導航到登錄頁面,然後到達寄存器頁面。創建一個新用戶,您應該能夠登錄。就是這樣。
啟動mongodb。
打開config/config.json並編輯數據庫部分,以便連接信息匹配您的mongoDB實例。另外,將類型從螺栓更改為MongoDB。
從根目錄構建和運行。打開您的Web瀏覽器至:http:// localhost。您應該看到歡迎頁面。
導航到登錄頁面,然後到達寄存器頁面。創建一個新用戶,您應該能夠登錄。就是這樣。
啟動mySQL並導入config/mysql.sql以創建數據庫和表。
打開config/config.json並編輯數據庫部分,以便連接信息匹配您的MySQL實例。另外,將類型從螺栓更改為MySQL。
從根目錄構建和運行。打開您的Web瀏覽器至:http:// localhost。您應該看到歡迎頁面。
導航到登錄頁面,然後到達寄存器頁面。創建一個新用戶,您應該能夠登錄。就是這樣。
Web應用程序具有一個公共主頁頁面,身份驗證的主頁,登錄頁面,註冊頁面,關於頁面,以及簡單的記事本,以演示CRUD操作。
Web應用程序的入口點是Gowebapp.go。該文件加載應用程序設置,啟動會話,連接到數據庫,設置模板,加載路由,連接中間件並啟動Web服務器。
前端是使用Bootstrap構建的,對字體和間距進行了一些小更改。閃存消息是自定義的,因此它們顯示在屏幕右下方。
所有錯誤和警告消息都應顯示給用戶或在控制台中。信息消息通過閃存消息顯示給用戶4秒後消失。閃存消息由靜態文件夾中的JavaScript控制。
最近,文件夾結構更改了。在查看了所有叉子並在不同地方重用我的項目後,我決定將GO代碼移至供應商文件夾內的應用程序文件夾中,因此GitHub路徑在許多導入中都不會亂扔。我不想使用相對路徑,因此供應商文件夾似乎是最佳選擇。
該項目被組織到以下文件夾中:
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
有一些外部軟件包:
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
模板被組織到模板文件夾下的文件夾中:
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
有一些模板函數可以使使用模板和靜態文件更輕鬆地工作:
<!-- 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/2006您也可以在模板中使用一些變量:
<!-- 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}} " >在關閉和標籤之前,也很容易添加模板特定的代碼:
<!-- 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}}您可以使用JavaScript觸發Flash通知。
flashError ( "You must type in a username." ) ;
flashSuccess ( "Record created!" ) ;
flashNotice ( "There seems to be a piece missing." ) ;
flashWarning ( "Something does not seem right..." ) ; 控制器文件全部共享相同的軟件包名稱。當您映射路線時,這會減少包裝的數量。它還迫使您為每個函數使用一個良好的命名約定,以便您知道每個函數的位置以及它們每個函數映射到哪種類型的HTTP請求。
訪問大猩猩會話:
// Get the current session
sess := session . Instance ( r )
...
// Close the session after you are finished making changes
sess . Save ( r , w )觸發下一頁上的4種不同類型的閃存消息中的1個(無需其他代碼):
sess . AddFlash (view. Flash { "Sorry, no brute force :-)" , view . FlashNotice })
sess . Save ( r , w ) // Ensure you save the session after making a change to it驗證表單字段不是空的:
// 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
}渲染模板:
// 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 )在AJAX請求期間返回Flash消息:
// 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 )處理數據庫查詢:
// 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
}發送電子郵件:
// 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
}如果在配置中啟用了Google recaptcha,則驗證表單:
// Validate with Google reCAPTCHA
if ! recaptcha . Verified ( r ) {
sess . AddFlash (view. Flash { "reCAPTCHA invalid!" , view . FlashError })
sess . Save ( r , w )
RegisterGET ( w , r )
return
}最好將數據庫層抽象化是一個好主意,因此,如果您需要進行更改,則不必查看業務邏輯即可找到查詢。所有查詢都存儲在模型文件夾中。
該項目支持BoltDB,MongoDB和MySQL。所有查詢都存儲在同一文件中,因此您可以輕鬆地更改數據庫,而無需修改配置文件以外的任何內容。
user.go和note.go文件是模型目錄的根源,是每個數據庫類型的所有查詢的補充。模型中有一些黑客攻擊,以使結構與所有受支持的數據庫一起使用。
連接到數據庫(您的應用程序中只需要一次):
// Connect to database
database . Connect ( config . Database )從數據庫中讀取:
result := User {}
err := database . DB . Get ( & result , "SELECT id, password, status_id, first_name FROM user WHERE email = ? LIMIT 1" , email )
return result , err寫入數據庫:
_ , err := database . DB . Exec ( "INSERT INTO user (first_name, last_name, email, password) VALUES (?,?,?,?)" , firstName , lastName , email , password )
return err 包括一些中間件。稱為CSRFBANANA的包裹可以防止跨場地請求偽造攻擊,並防止雙重提交。軟件包HTTPROUTROUTRAPPER提供了輔助功能,以使功能與HTTPROUTER兼容。包logrequest將將針對網站提出的每個請求記錄到控制台。 PackProfhandler的軟件包可以啟用Pprof,因此它將與HttProuter一起使用。在路線上,所有各個路線都使用愛麗絲使鏈接非常容易。
為了使Web應用程序更加靈活一些,您可以通過config.json文件更改一個位置的不同組件。如果要添加自己的任何設置,則可以將它們添加到config.json中,並在gowebapp.go和單個文件中更新結構,以便您可以在代碼中引用它們。這是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
}
}要啟用HTTPS,請將USEHTTPS設置為true,在根中創建一個稱為TLS的文件夾,然後將證書和鍵文件放在該文件夾中。
公共住宅:
關於:
登記:
登入:
身份驗證的家:
查看註釋:
添加註意:
編輯註:
歡迎所有反饋。讓我知道您是否有任何建議,問題或批評。如果某件事不是慣用的,請讓我知道,以便我們可以使它變得更好。