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的文件夹,然后将证书和键文件放在该文件夹中。
公共住宅:
关于:
登记:
登录:
身份验证的家:
查看注释:
添加注意:
编辑注:
欢迎所有反馈。让我知道您是否有任何建议,问题或批评。如果某件事不是惯用的,请让我知道,以便我们可以使它变得更好。