Aplikasi Web MVC Dasar di Go
Proyek ini menunjukkan cara menyusun dan membangun situs web menggunakan bahasa GO tanpa kerangka kerja. Ada artikel blog yang dapat Anda baca di http://www.josephspurrier.com/go-web-app-example/. Ada aplikasi lengkap yang saya bangun dengan versi sebelumnya dari proyek di https://github.com/verifiedninja/webapp. Ada versi API dari proyek ini di https://github.com/josephspurrier/gowebapi.
Untuk mengunduh, jalankan perintah berikut:
go get github.com/josephspurrier/gowebapp
Jika Anda berada di Go 1.5, Anda perlu mengatur Govendorexperiment ke 1. Jika Anda berada di Go 1.4 atau lebih awal, kode tidak akan berfungsi karena menggunakan folder vendor.
File gowebapp.db akan dibuat setelah Anda memulai aplikasi.
Bangun dan jalankan dari direktori root. Buka browser web Anda ke: http: // localhost. Anda harus melihat halaman selamat datang.
Arahkan ke halaman login, lalu ke halaman register. Buat pengguna baru dan Anda harus dapat masuk. Itu saja.
Mulai Mongodb.
Buka config/config.json dan edit bagian database sehingga informasi koneksi cocok dengan instance MongoDB Anda. Juga, ubah jenis dari baut ke mongodb.
Bangun dan jalankan dari direktori root. Buka browser web Anda ke: http: // localhost. Anda harus melihat halaman selamat datang.
Arahkan ke halaman login, lalu ke halaman register. Buat pengguna baru dan Anda harus dapat masuk. Itu saja.
Mulai MySQL dan impor config/mysql.sql untuk membuat database dan tabel.
Buka config/config.json dan edit bagian database sehingga informasi koneksi cocok dengan instance MySQL Anda. Juga, ubah jenis dari baut ke mysql.
Bangun dan jalankan dari direktori root. Buka browser web Anda ke: http: // localhost. Anda harus melihat halaman selamat datang.
Arahkan ke halaman login, lalu ke halaman register. Buat pengguna baru dan Anda harus dapat masuk. Itu saja.
Aplikasi Web memiliki halaman beranda publik, beranda yang diautentikasi, halaman login, halaman register, tentang halaman, dan notepad sederhana untuk menunjukkan operasi CRUD.
Entrypoint untuk aplikasi web adalah gowebapp.go. File memuat pengaturan aplikasi, memulai sesi, terhubung ke database, mengatur templat, memuat rute, melampirkan middleware, dan memulai server web.
Ujung depan dibangun menggunakan bootstrap dengan beberapa perubahan kecil pada font dan jarak. Pesan flash disesuaikan sehingga muncul di kanan bawah layar.
Semua pesan kesalahan dan peringatan harus ditampilkan baik untuk pengguna atau di konsol. Pesan informasi ditampilkan kepada pengguna melalui pesan flash yang hilang setelah 4 detik. Pesan flash dikendalikan oleh JavaScript di folder statis.
Baru -baru ini, struktur folder berubah. Setelah melihat semua garpu dan menggunakan kembali proyek saya di berbagai tempat, saya memutuskan untuk memindahkan kode Go ke folder aplikasi di dalam folder vendor sehingga jalur github tidak dikotori di seluruh impor. Saya tidak ingin menggunakan jalur relatif sehingga folder vendor sepertinya pilihan terbaik.
Proyek ini diatur ke dalam folder berikut:
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
Ada beberapa paket eksternal:
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
Template diatur ke dalam folder di bawah folder template :
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
Ada beberapa fungsi template yang tersedia untuk membuat bekerja dengan templat dan file statis lebih mudah:
<!-- 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/2006Ada beberapa variabel yang dapat Anda gunakan dalam templat juga:
<!-- 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}} " >Juga mudah untuk menambahkan kode khusus templat sebelum penutupan dan tag:
<!-- 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}}Anda dapat memicu pemberitahuan flash menggunakan 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..." ) ; File pengontrol semuanya berbagi nama paket yang sama. Ini mengurangi jumlah paket saat Anda memetakan rute. Ini juga memaksa Anda untuk menggunakan konvensi penamaan yang baik untuk masing -masing fungsi sehingga Anda tahu di mana masing -masing fungsi berada dan jenis permintaan HTTP apa yang masing -masing dipetakan.
Akses sesi gorila:
// Get the current session
sess := session . Instance ( r )
...
// Close the session after you are finished making changes
sess . Save ( r , w )Pemicu 1 dari 4 jenis pesan flash yang berbeda pada pemuatan halaman berikutnya (tidak ada kode lain yang diperlukan):
sess . AddFlash (view. Flash { "Sorry, no brute force :-)" , view . FlashNotice })
sess . Save ( r , w ) // Ensure you save the session after making a change to itValidasi bidang formulir tidak kosong:
// 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
}Membuat template:
// 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 )Mengembalikan pesan flash selama permintaan 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 )Tangani kueri basis data:
// 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
}Kirim email:
// 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
}Validasi formulir jika Google Recaptcha diaktifkan di konfigurasi:
// Validate with Google reCAPTCHA
if ! recaptcha . Verified ( r ) {
sess . AddFlash (view. Flash { "reCAPTCHA invalid!" , view . FlashError })
sess . Save ( r , w )
RegisterGET ( w , r )
return
}Merupakan ide bagus untuk mengabstraksi lapisan basis data, jadi jika Anda perlu melakukan perubahan, Anda tidak perlu melihat melalui logika bisnis untuk menemukan kueri. Semua pertanyaan disimpan di folder model.
Proyek ini mendukung BoltDB, MongoDB, dan MySQL. Semua kueri disimpan dalam file yang sama sehingga Anda dapat dengan mudah mengubah database tanpa memodifikasi apa pun kecuali file konfigurasi.
File user.go dan note.go berada di root dari direktori model dan merupakan kompleks dari semua kueri untuk setiap jenis database. Ada beberapa peretasan dalam model untuk membuat struct bekerja dengan semua database yang didukung.
Sambungkan ke database (hanya diperlukan sekali di aplikasi Anda):
// Connect to database
database . Connect ( config . Database )Baca dari database:
result := User {}
err := database . DB . Get ( & result , "SELECT id, password, status_id, first_name FROM user WHERE email = ? LIMIT 1" , email )
return result , errTulis ke database:
_ , err := database . DB . Exec ( "INSERT INTO user (first_name, last_name, email, password) VALUES (?,?,?,?)" , firstName , lastName , email , password )
return err Ada beberapa potong middleware. Paket yang disebut Csrfbanana melindungi terhadap serangan pemalsuan permintaan lintas situs dan mencegah pengiriman ganda. Paket HTTProUterWrapper menyediakan fungsi penolong untuk membuat fungsi kompatibel dengan httprouter. Paket LogRequest akan mencatat setiap permintaan yang dibuat terhadap situs web ke konsol. Paket PPROFHANDLER memungkinkan PPROF sehingga akan bekerja dengan HTTProUter. Dalam rute.go, semua rute individual menggunakan Alice untuk membuat rantai sangat mudah.
Untuk membuat aplikasi web sedikit lebih fleksibel, Anda dapat membuat perubahan pada berbagai komponen di satu tempat melalui file config.json. Jika Anda ingin menambahkan pengaturan Anda sendiri, Anda dapat menambahkannya ke config.json dan memperbarui struct di gowebapp.go dan masing -masing file sehingga Anda dapat merujuknya dalam kode Anda. Ini 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
}
}Untuk mengaktifkan HTTPS, atur UseHTTPS ke True, buat folder yang disebut TLS di root, dan kemudian tempatkan sertifikat dan file kunci di folder itu.
Rumah Publik:
Tentang:
Daftar:
Login:
Rumah yang diautentikasi:
Lihat catatan:
Tambahkan Catatan:
Edit Catatan:
Semua umpan balik diterima. Beri tahu saya jika Anda memiliki saran, pertanyaan, atau kritik. Jika ada sesuatu yang tidak idiomatik untuk pergi, beri tahu saya tahu agar kami bisa membuatnya lebih baik.