1.Abstract
在學(xué)習(xí)web開(kāi)發(fā)的過(guò)程中會(huì)遇到很多困難,因此寫(xiě)洗一篇類(lèi)似綜述類(lèi)的文章。作為路線圖從web開(kāi)發(fā)要素的index出發(fā)來(lái)介紹golang開(kāi)發(fā)的學(xué)習(xí)流程以及Example代碼。
在描述中多是使用代碼來(lái)描述使用方法不會(huì)做過(guò)多的說(shuō)明。最后可以方便的copy代碼來(lái)實(shí)現(xiàn)自己的需求。
本文適應(yīng)對(duì)象:
對(duì)web開(kāi)發(fā)有一定經(jīng)驗(yàn)的人
能夠靈活使用ajax的人(至少懂得前后分離)
golang web 開(kāi)發(fā)有一定了解,至少略讀過(guò)一些golang web開(kāi)發(fā)的書(shū)籍
看完本文之后您會(huì)收獲:
golang web開(kāi)發(fā)的一些技巧
golang web開(kāi)發(fā)的一些實(shí)用API
本文在描述的時(shí)候?yàn)榱私忉尡M量詳細(xì),已經(jīng)把解釋寫(xiě)到代碼注釋中。
2.golang web 開(kāi)發(fā)check list
略過(guò)的部分:基本流程控制,OOP等基礎(chǔ)語(yǔ)法知識(shí)。
2.1本章節(jié)提供golang web開(kāi)發(fā)的知識(shí)面參考。
1.Abstract
2.golang web 開(kāi)發(fā)check list
2.1 本章節(jié)提供golang_web開(kāi)發(fā)的知識(shí)面參考。
3.路由器
3.1 手動(dòng)路由
3.2 手動(dòng)路由的綁定
3.2.1 靜態(tài)文件
3.2.2 固定函數(shù)與資源獲取
4.頁(yè)面加載
4.1 純靜態(tài)頁(yè)(HTML)
4.2 模板頁(yè)面的加載
5.表示層腳本
5.1 require.js
5.1.1 加載
5.1.2 頁(yè)面Business
5.2 JQuery
6.業(yè)務(wù)層
7.持久層
7.1 Mysql
7.2 Mongodb
8.單元測(cè)試注意事項(xiàng)
9.LOG
總結(jié)
3.路由器
路由器是整個(gè)網(wǎng)站對(duì)外的靈魂,如果路由做的不好URL會(huì)非常惡心。
所以這部分設(shè)計(jì)成第一個(gè)要說(shuō)的內(nèi)容。
路由分兩種一種是手動(dòng)路由為了通過(guò)tul調(diào)度固定的功能,另外一點(diǎn)就是資源
的獲取,通過(guò)url的分析來(lái)模仿靜態(tài)頁(yè)的方式來(lái)獲取資源(類(lèi)似get)
自動(dòng)路由,主要使用OOP的COMMAND模式來(lái)實(shí)現(xiàn)。所有功能使用post,
統(tǒng)一入口,方便權(quán)限管理,安全管理,跨域管理。但是如此強(qiáng)大的功能還是
交給框架來(lái)做吧。這里就不給新手做參考了。
3.1手動(dòng)路由
package main
import (
"log"
"net/http"
)
func main() {
RouterBinding() // 路由綁定函數(shù)
err := http.ListenAndServe(":9090", nil) //設(shè)置監(jiān)聽(tīng)的端口
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
在httpserver運(yùn)行之前先綁定路由
3.2 手動(dòng)路由的綁定
3.2.1 靜態(tài)文件
http.Handle("/pages/", http.StripPrefix("/pages/", http.FileServer(http.Dir("./pages"))))
3.2.2 固定函數(shù)與資源獲取
他們都是一樣的
http.HandleFunc("/images/", fileUpload.DownloadPictureAction)
4.頁(yè)面加載
4.1 純靜態(tài)頁(yè)(HTML)
直接交給路由就行了。自動(dòng)就訪問(wèn)那個(gè)文件夾了。不過(guò)生產(chǎn)環(huán)境果然還得是cdn,如果自己服務(wù)器比較多。可以nginx反向代理。
主要好處前后分離,能上CDN就是通訊次數(shù)多了。不過(guò)通過(guò)優(yōu)化改善之類(lèi)的都還ok啦。
4.2 模板頁(yè)面的加載僅提供關(guān)鍵代碼。
commonPage, err := template.ParseFiles("pages/common/head.gtpl", //加載模板
"pages/common/navbar.gtpl", "pages/common/tail.gtpl")
if err != nil {
panic(err.Error())
}
navArgs := map[string]string{"Home": "home", "User": "yupengfei"}//復(fù)雜的參數(shù)開(kāi)始往里塞
knowledgePage, err := template.ParseFiles("pages/knowledge/knowledge.gtpl")
knowledgeArgs := map[string]interface{}{"Head": "This is a test title",
"Author": "kun.wang", "PublishDatetime": "2014-09-14",
"Content": template.HTML("<p style="text-indent: 2em">為什么要用語(yǔ)義呢?</p>")}//不是不好,只是做字符串分析會(huì)影響工程效率
commonPage.ExecuteTemplate(w, "header", nil)// render 開(kāi)始
commonPage.ExecuteTemplate(w, "navbar", navArgs)
knowledgePage.ExecuteTemplate(w, "knowledge", knowledgeArgs)
commonPage.ExecuteTemplate(w, "tail", nil)
僅提供關(guān)鍵代碼。
字符串?dāng)?shù)組作為輸入?yún)?shù)差錯(cuò)比較困難
總結(jié):雖然減少的通訊次數(shù),但是沒(méi)辦法上CDN蛋疼,另外,模板的mapping蛋疼。
5.表示層腳本
表示層腳本做的比較困難也不是很好學(xué)。
但是一旦搞定了,代碼的復(fù)用性會(huì)有非??捎^的提升。
就普通情況而言JS開(kāi)發(fā)效率是非常高的靈活度高,并且使用的是客戶端的cpu
性能好,免費(fèi)資源多,學(xué)習(xí)的人也多,好招聘。
5.1 require.js
5.1.1 加載
<script data-main="/reqmod/login_main" language="JavaScript" defer async="true" src="js/r.js"></script>
整個(gè)網(wǎng)頁(yè)之留這么一個(gè)加載腳本的入口(每個(gè)頁(yè)面最好只有一個(gè)js文件)
好處
js是延遲加載。不會(huì)出現(xiàn)網(wǎng)頁(yè)卡死的情況
最大化使用緩存。(HTTP 304)
一個(gè)網(wǎng)頁(yè)只用一個(gè)js
dom事件綁定,不用在html控件上寫(xiě)js綁定了
壞處
學(xué)習(xí)比較難
網(wǎng)站更新始終有緩存沒(méi)更新的瀏覽器。造成錯(cuò)誤(所以有些情況客戶自己就知道多刷新幾次了,已經(jīng)成用戶習(xí)慣了)
參數(shù)解釋
`data-main` 業(yè)務(wù)邏輯入口,載入當(dāng)前字符串.js這個(gè)文件
`language` 不解釋
`defer async` 字面意思
`src` r.js就是require.js的意思。代碼到處都能搞到。
5.1.2 頁(yè)面Business
加載依賴文件
require.baseUrl = "/"
require.config({
baseUrl: require.baseUrl,
paths: {
"jquery": "js/jquery-1.10.2.min",
"domready" : "reqmod/domReady",
"pm" : "reqmod/pmodal",
"cookie":"reqmod/cookie",
"user":"reqmod/user",
"bootstrap": "reqmod/bootstrap.min",
"nav":"reqmod/nav"
},
shim: {
'bootstrap': {
deps: ['jquery']
}
}
});
//直接copy全搞定。
執(zhí)行頁(yè)面business
執(zhí)行里面做的最多的就是dom跟事件綁定而已。加載各種js庫(kù)直接引用。
代碼美觀,開(kāi)發(fā)效率,執(zhí)行效率都是非常棒的。
require(['nav','domready', 'jquery', 'user','pm'], function (nav,doc, $, user,pm){
//這個(gè)函數(shù)的第一個(gè)`數(shù)組`參數(shù)是選擇的依賴的模塊。1. 網(wǎng)站絕對(duì)路徑。 2. 使用加載依賴模塊的時(shí)候選擇export的內(nèi)容
//數(shù)組的順序要跟function順序一致,如果有兩個(gè)模塊依賴比如說(shuō)jquery插件,就寫(xiě)道最后不用變量,直接使用`$`
doc(function () { // domReady
pm.load();//加載各種插件HTML模板之類(lèi)的都o(jì)k
$('#btn_login')[0].onclick = function(){user.login();}//button 事件綁定
});
});
頁(yè)面MODEL
define(['jquery','reqmod/cookie','user','bootstrap'],function ($,cookie,user){
//define 函數(shù)的參數(shù)內(nèi)容require是一樣的。
// 這里依賴的模塊要在調(diào)用此模塊中的模塊中有path配置。不然會(huì)死的很慘,報(bào)錯(cuò)的時(shí)候不會(huì)說(shuō)缺少什么什么地方錯(cuò)了。
var nav_load = function () { // 注意函數(shù)定義的方式copy就行
$.get('/nav.html', function(result){
var newNode = document.createElement("div");
newNode.innerHTML = result;
$('body')[0].insertBefore(newNode,$('body')[0].firstChild);
//document.body.innerHTML = result + document.body.innerHTML;
$('#btn_login')[0].onclick = function(){user.login();}
$('#btn_reg')[0].onclick = function(){window.location='/register.html'}
$.post('/login_check',{},function(data){
if(data==0){
Form_login.style.display=""
}
else{
form_userInfo.style.display=""
}
})
});
}
return {//這里類(lèi)似微型路由。非常靈活,非常方便
load :nav_load
};
});
5.2 JQuery
JQ的功能只要require.js引用了之后基本上都是一樣的。
如果有需要可以到w3school上學(xué)習(xí)一下。
6.業(yè)務(wù)層
Post分析
func XXXAction(w http.ResponseWriter, r *http.Request) {
r.parseForm() //有這個(gè)才能獲取參數(shù)
r.Form["Email"] // 獲取Email 參數(shù)(String)
// 寫(xiě)接下來(lái)的業(yè)務(wù)。
}
資源入口函數(shù)資源require分析(url分析固定寫(xiě)法)
func Foo(w http.ResponseWriter, r *http.Request) {
queryFile := strings.Split(r.URL.Path, "/")
queryResource := queryFile[len(queryFile)-1] // 解析文件
}
//完成字符串分割之后,按照需求來(lái)獲取資源就可以了。
直接輸入object
data, err := ioutil.ReadAll(r.Body) //直接讀取form為 json 字符串
if err != nil {
utility.SimpleFeedBack(w, 10, "failed to read body")
pillarsLog.PillarsLogger.Print("failed to read body")
return
}
k := 【BUSINESS OBJECT】
err = json.Unmarshal(data, &k)
if err != nil {
utility.SimpleFeedBack(w, 13, "Pramaters failed!")
pillarsLog.PillarsLogger.Print("Pramaters failed!")
return
}
//方便快捷。再訪問(wèn)參數(shù)的時(shí)候,直接調(diào)用結(jié)構(gòu)體參數(shù)就可以了。
//注意ajax調(diào)用函數(shù)的時(shí)候需要做出一些調(diào)整代碼如下:
$.ajax([dist],JSON.stringify([data]),function(){},'json');//注意JSON
7.持久層
7.1 Mysql
其實(shí)不管什么語(yǔ)言的Mysql驅(qū)動(dòng)都是從PRO*C來(lái)的,所以會(huì)PRO*C之后,啥都好說(shuō)
Insert Delete Update
stmt, err := mysqlUtility.DBConn.Prepare("INSERT INTO credit (credit_code, user_code, credit_rank) VALUES (?, ?, ?)")
if err != nil {
pillarsLog.PillarsLogger.Print(err.Error())
return false, err
}
defer stmt.Close()
_, err = stmt.Exec(credit.CreditCode, credit.UserCode, credit.CreditRank)
if err != nil {
return false, err
} else {
return true, err
}
//還是比較方便的
Query
stmt, err := mysqlUtility.DBConn.Prepare(`SELECT commodity_code, commodity_name, description, picture,
price, storage, count, status,
insert_datetime, update_datetime FROM commodity WHERE commodity_code = ?`)
if err != nil {
return nil, err
}
defer stmt.Close()
result, err := stmt.Query(commodityCode)
if err != nil {
return nil, err
}
defer result.Close()
var commodity utility.Commodity
if result.Next() {
err = result.Scan(&(commodity.CommodityCode), &(commodity.CommodityName), &(commodity.Description),
&(commodity.Picture), &(commodity.Price), &(commodity.Storage), &(commodity.Count), &(commodity.Status),
&(commodity.InsertDatetime), &(commodity.UpdateDatetime))
if err != nil {
pillarsLog.PillarsLogger.Print(err.Error())
return nil, err
}
}
return &commodity, err
7.2 Mongodb
err := mongoUtility.PictureCollection.Find(bson.M{"picturecode":*pictureCode}).One(&picture)
這里只給出最簡(jiǎn)單的例子。具體的看mgo的開(kāi)發(fā)文檔就ok。還是比較簡(jiǎn)單的。
8.單元測(cè)試注意事項(xiàng)
測(cè)試命令 go test -v (沒(méi)有其他參數(shù)了?。。。?如果不帶-v只顯示結(jié)果,不顯示調(diào)試過(guò)程,主要是調(diào)試開(kāi)發(fā)的時(shí)候用
文件格式 xxx_test.go 但是建議改成 xxx_test0.go 或者喜歡改成別的也可以。
由于測(cè)試先行的原則,在開(kāi)發(fā)的時(shí)候一次測(cè)試也就一兩個(gè)函數(shù)。
這樣相當(dāng)于把其他測(cè)試注釋掉
測(cè)試的時(shí)候的配置文件要放到測(cè)試目錄下面。別忘了。
心態(tài),錯(cuò)誤太多一個(gè)一個(gè)來(lái),要有個(gè)好心態(tài)。
9.LOG
注意在調(diào)試中Log的不可缺失性。
下面api如果不知道從何而來(lái)直接doc搜索就可以了。
package utility
import "log"
import "os"
import "fmt"
// Logger Model min variable.
var Logger *log.Logger
var outFile *os.File
// init function if Logger if not inited will invoke this function
func init() {
if Logger == nil {
propertyMap := ReadProperty("pic.properties")
logFileName := propertyMap["LogFile"]
fmt.Println("Initial and Open log file ", logFileName)
var err error
outFile, err = os.OpenFile(logFileName, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
if err != nil {
panic(err.Error())
}
Logger = log.New(outFile, "", log.Ldate|log.Ltime|log.Llongfile)
}
}
// CloseLogFile function : close Logger invoke file.
func CloseLogFile() {
outFile.Close()
}
使用方法:
utility.Logger.Println("Log test")
總結(jié)
看完這里copy代碼日常工作還是能好應(yīng)付一點(diǎn)。
如果是新手看完這個(gè)之后,看那么厚的書(shū)就有一定的目標(biāo)性了。能方便一點(diǎn)在學(xué)習(xí)web開(kāi)發(fā)的過(guò)程中會(huì)遇到很多困難,因此寫(xiě)洗一篇類(lèi)似綜述類(lèi)的文章。作為路線圖從web開(kāi)發(fā)要素的index出發(fā)來(lái)介紹golang開(kāi)發(fā)的學(xué)習(xí)流程以及Example代碼。
更多信息請(qǐng)查看IT技術(shù)專(zhuān)欄