แอปพลิเคชันเว็บ MVC พื้นฐานใน GO
โครงการนี้แสดงให้เห็นถึงวิธีการจัดโครงสร้างและสร้างเว็บไซต์โดยใช้ภาษา 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 จะถูกสร้างขึ้นเมื่อคุณเริ่มแอปพลิเคชัน
สร้างและเรียกใช้จากไดเรกทอรีราก เปิดเว็บเบราว์เซอร์ของคุณเป็น: http: // localhost คุณควรดูหน้าต้อนรับ
นำทางไปยังหน้าเข้าสู่ระบบจากนั้นไปที่หน้าลงทะเบียน สร้างผู้ใช้ใหม่และคุณควรเข้าสู่ระบบ แค่นั้นแค่นั้น
เริ่ม MongoDB
เปิด config/config.json และแก้ไขส่วนฐานข้อมูลเพื่อให้ข้อมูลการเชื่อมต่อตรงกับอินสแตนซ์ MongoDB ของคุณ นอกจากนี้เปลี่ยนประเภทจาก Bolt เป็น MongoDB
สร้างและเรียกใช้จากไดเรกทอรีราก เปิดเว็บเบราว์เซอร์ของคุณเป็น: http: // localhost คุณควรดูหน้าต้อนรับ
นำทางไปยังหน้าเข้าสู่ระบบจากนั้นไปที่หน้าลงทะเบียน สร้างผู้ใช้ใหม่และคุณควรเข้าสู่ระบบ แค่นั้นแค่นั้น
เริ่ม MySQL และนำเข้า config/mysql.sql เพื่อสร้างฐานข้อมูลและตาราง
เปิด config/config.json และแก้ไขส่วนฐานข้อมูลเพื่อให้ข้อมูลการเชื่อมต่อตรงกับอินสแตนซ์ MySQL ของคุณ นอกจากนี้เปลี่ยนประเภทจาก Bolt เป็น MySQL
สร้างและเรียกใช้จากไดเรกทอรีราก เปิดเว็บเบราว์เซอร์ของคุณเป็น: http: // localhost คุณควรดูหน้าต้อนรับ
นำทางไปยังหน้าเข้าสู่ระบบจากนั้นไปที่หน้าลงทะเบียน สร้างผู้ใช้ใหม่และคุณควรเข้าสู่ระบบ แค่นั้นแค่นั้น
เว็บแอปมีโฮมเพจสาธารณะหน้าแรกที่ผ่านการรับรองความถูกต้องหน้าเข้าสู่ระบบหน้าลงทะเบียนหน้าและแผ่นจดบันทึกง่าย ๆ เพื่อแสดงให้เห็นถึงการดำเนินการ CRUD
จุดเข้าใช้งานสำหรับเว็บแอปคือ gowebapp.go ไฟล์โหลดการตั้งค่าแอปพลิเคชันเริ่มต้นเซสชันเชื่อมต่อกับฐานข้อมูลตั้งค่าเทมเพลตโหลดเส้นทางแนบมิดเดิลแวร์และเริ่มเว็บเซิร์ฟเวอร์
ปลายด้านหน้าถูกสร้างขึ้นโดยใช้ 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
มีเทมเพลต funcs ที่พร้อมใช้งานเพื่อให้ทำงานกับเทมเพลตและไฟล์คงที่ง่ายขึ้น:
<!-- 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
flashError ( "You must type in a username." ) ;
flashSuccess ( "Record created!" ) ;
flashNotice ( "There seems to be a piece missing." ) ;
flashWarning ( "Something does not seem right..." ) ; ไฟล์คอนโทรลเลอร์ทั้งหมดแชร์ชื่อแพ็คเกจเดียวกัน สิ่งนี้จะลดจำนวนแพ็คเกจเมื่อคุณกำลังแมปเส้นทาง นอกจากนี้ยังบังคับให้คุณใช้การประชุมการตั้งชื่อที่ดีสำหรับแต่ละ funcs เพื่อให้คุณรู้ว่า Funcs แต่ละตัวอยู่ที่ไหนและคำขอ HTTP ประเภทใดที่พวกเขาแต่ละคนถูกแมปไป
เข้าถึงเซสชันกอริลลา:
// Get the current session
sess := session . Instance ( r )
...
// Close the session after you are finished making changes
sess . Save ( r , w )ทริกเกอร์ 1 จาก 4 ข้อความแฟลชที่แตกต่างกันในการโหลดหน้าถัดไป (ไม่จำเป็นต้องใช้รหัสอื่น):
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:
// 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 ป้องกันการโจมตีการปลอมแปลงการร้องขอข้ามไซต์และป้องกันการส่งสองครั้ง แพ็คเกจ httprouterwrapper มีฟังก์ชั่นผู้ช่วยเพื่อให้ funcs เข้ากันได้กับ httprouter Package LogRequest จะบันทึกทุกคำขอที่ทำกับเว็บไซต์ไปยังคอนโซล แพ็คเกจ pprofhandler เปิดใช้งาน PPROF เพื่อให้ทำงานกับ httprouter ใน Route.go ทุกเส้นทางแต่ละเส้นทางใช้อลิซเพื่อทำให้การผูกมัดง่ายมาก
เพื่อให้เว็บแอปมีความยืดหยุ่นมากขึ้นเล็กน้อยคุณสามารถทำการเปลี่ยนแปลงส่วนประกอบที่แตกต่างกันในที่เดียวผ่านไฟล์ config.json หากคุณต้องการเพิ่มการตั้งค่าใด ๆ ของคุณเองคุณสามารถเพิ่มลงใน config.json และอัปเดต structs ใน 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 ให้ตั้งค่าใช้งาน isehttps เป็นจริงให้สร้างโฟลเดอร์ที่เรียกว่า TLS ในรูทแล้ววางใบรับรองและไฟล์คีย์ในโฟลเดอร์นั้น
บ้านสาธารณะ:
เกี่ยวกับ:
ลงทะเบียน:
เข้าสู่ระบบ:
บ้านที่ผ่านการรับรองความถูกต้อง:
ดูหมายเหตุ:
เพิ่มหมายเหตุ:
แก้ไขหมายเหตุ:
ยินดีต้อนรับข้อเสนอแนะทั้งหมด แจ้งให้เราทราบหากคุณมีข้อเสนอแนะคำถามหรือการวิพากษ์วิจารณ์ หากสิ่งที่ไม่ได้เป็นสำนวนโปรดแจ้งให้เราทราบเพื่อให้เราสามารถทำให้ดีขึ้น