歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> 使用Golang快速構建WEB應用

使用Golang快速構建WEB應用

日期:2017/3/1 9:31:47   编辑:Linux編程

我們從來都不開發代碼,我們只是代碼的搬運工。— 阿飛

希望大家都變卡卡西。 — 啊賤

大家copy愉快,文檔只做參考。如果發現問題或者有好的建議請回復我我回及時更正。

1.Abstract

在學習web開發的過程中會遇到很多困難,因此寫了一篇類似綜述類的文章。作為路線圖從web開發要素的index出發來介紹golang開發的學習流程以及Example代碼。

在描述中多是使用代碼來描述使用方法不會做過多的說明。最後可以方便的copy代碼來實現自己的需求。

本文適應對象:

  1. 對web開發有一定經驗的人
  2. 能夠靈活使用ajax的人(至少懂得前後分離)
  3. golang web 開發有一定了解,至少略讀過一些golang web開發的書籍

看完本文之後您會收獲:

  1. golang web開發的一些技巧
  2. golang web開發的一些實用API

本文在描述的時候為了解釋盡量詳細,已經把解釋寫到代碼注釋中。

2.golang web 開發check list

略過的部分:基本流程控制,OOP等基礎語法知識。

3.路由器

路由器是整個網站對外的靈魂,如果路由做的不好URL會非常惡心。所以這部分設計成第一個要說的內容。

路由分兩種一種是手動路由為了通過tul調度固定的功能,另外一點就是資源的獲取,通過url的分析來模仿靜態頁的方式來獲取資源(類似get)

自動路由,主要使用OOP的COMMAND模式來實現。所有功能使用post,統一入口,方便權限管理,安全管理,跨域管理。但是如此強大的功能還是交給框架來做吧。這裡就不給新手做參考了。

3.1手動路由

  1. package main
  2. import(
  3. "log"
  4. "net/http"
  5. )
  6. func main(){
  7. RouterBinding()// 路由綁定函數
  8. err := http.ListenAndServe(":9090",nil)//設置監聽的端口
  9. if err !=nil{
  10. log.Fatal("ListenAndServe: ", err)
  11. }
  12. }

在httpserver運行之前先綁定路由

3.2 手動路由的綁定

3.2.1 靜態文件

  1. http.Handle("/pages/", http.StripPrefix("/pages/", http.FileServer(http.Dir("./pages"))))

3.2.2 固定函數與資源獲取

他們都是一樣的

  1. http.HandleFunc("/images/", fileUpload.DownloadPictureAction)

4.頁面加載

4.1 純靜態頁(HTML)

直接交給路由就行了。自動就訪問那個文件夾了。不過生產環境果然還得是cdn,如果自己服務器比較多。可以nginx反向代理。

主要好處前後分離,能上CDN就是通訊次數多了。不過通過優化改善之類的都還ok啦。

4.2 模板頁面的加載

  1. commonPage, err :=template.ParseFiles("pages/common/head.gtpl",//加載模板
  2. "pages/common/navbar.gtpl","pages/common/tail.gtpl")
  3. if err !=nil{
  4. panic(err.Error())
  5. }
  6. navArgs := map[string]string{"Home":"home","User":"yupengfei"}//復雜的參數開始往裡塞
  7. knowledgePage, err :=template.ParseFiles("pages/knowledge/knowledge.gtpl")
  8. knowledgeArgs := map[string]interface{}{"Head":"This is a test title",
  9. "Author":"kun.wang","PublishDatetime":"2014-09-14",
  10. "Content":template.HTML("<p style=\"text-indent: 2em\">為什麼要用語義呢?</p>")}//不是不好,只是做字符串分析會影響工程效率
  11. commonPage.ExecuteTemplate(w,"header",nil)// render 開始
  12. commonPage.ExecuteTemplate(w,"navbar", navArgs)
  13. knowledgePage.ExecuteTemplate(w,"knowledge", knowledgeArgs)
  14. commonPage.ExecuteTemplate(w,"tail",nil)

僅提供關鍵代碼。

  • 其他的都還挺好,就是頁面渲染用服務器是不是有點太奢侈了。
  • 字符串數組作為輸入參數差錯比較困難
  • 總結:雖然減少的通訊次數,但是沒辦法上CDN蛋疼,另外,模板的mapping蛋疼。

5.表示層腳本

表示層腳本做的比較困難也不是很好學。但是一旦搞定了,代碼的復用性會有非常可觀的提升。

就普通情況而言JS開發效率是非常高的靈活度高,並且使用的是客戶端的cpu性能好,免費資源多,學習的人也多,好招聘。

5.1 require.js

5.1.1 加載

  1. <scriptdata-main="/reqmod/login_main"language="JavaScript"deferasync="true"src="js/r.js"></script>

整個網頁之留這麼一個加載腳本的入口(每個頁面最好只有一個js文件)

好處

  • js是延遲加載。不會出現網頁卡死的情況
  • 最大化使用緩存。(HTTP 304)
  • 一個網頁只用一個js
  • dom事件綁定,不用在html控件上寫js綁定了

壞處

  • 學習比較難
  • 網站更新始���有緩存沒更新的浏覽器。造成錯誤(所以有些情況客戶自己就知道多刷新幾次了,已經成用戶習慣了)

參數解釋

  • `data-main` 業務邏輯入口,載入當前字符串.js這個文件
  • `language` 不解釋
  • `defer async` 字面意思
  • `src` r.js就是require.js的意思。代碼到處都能搞到。

5.1.2 頁面Business

加載依賴文件

  1. require.baseUrl ="/"
  2. require.config({
  3. baseUrl:require.baseUrl,
  4. paths:{
  5. "jquery":"js/jquery-1.10.2.min",
  6. "domready":"reqmod/domReady",
  7. "pm":"reqmod/pmodal",
  8. "cookie":"reqmod/cookie",
  9. "user":"reqmod/user",
  10. "bootstrap":"reqmod/bootstrap.min",
  11. "nav":"reqmod/nav"
  12. },
  13. shim:{
  14. 'bootstrap':{
  15. deps:['jquery']
  16. }
  17. }
  18. });
  19. //直接copy全搞定。

執行頁面business

執行裡面做的最多的就是dom跟事件綁定而已。加載各種js庫直接引用。

代碼美觀,開發效率,執行效率都是非常棒的。

  1. require(['nav','domready','jquery','user','pm'],function(nav,doc, $, user,pm){
  2. //這個函數的第一個`數組`參數是選擇的依賴的模塊。1. 網站絕對路徑。 2. 使用加載依賴模塊的時候選擇export的內容
  3. //數組的順序要跟function順序一致,如果有兩個模塊依賴比如說jquery插件,就寫道最後不用變量,直接使用`$`
  4. doc(function(){// domReady
  5. pm.load();//加載各種插件HTML模板之類的都ok
  6. $('#btn_login')[0].onclick =function(){user.login();}//button 事件綁定
  7. });
  8. });

頁面MODEL

  1. define(['jquery','reqmod/cookie','user','bootstrap'],function($,cookie,user){
  2. //define 函數的參數內容require是一樣的。
  3. // 這裡依賴的模塊要在調用此模塊中的模塊中有path配置。不然會死的很慘,報錯的時候不會說缺少什麼什麼地方錯了。
  4. var nav_load =function(){// 注意函數定義的方式copy就行
  5. $.get('/nav.html',function(result){
  6. var newNode = document.createElement("div");
  7. newNode.innerHTML = result;
  8. $('body')[0].insertBefore(newNode,$('body')[0].firstChild);
  9. //document.body.innerHTML = result + document.body.innerHTML;
  10. $('#btn_login')[0].onclick =function(){user.login();}
  11. $('#btn_reg')[0].onclick =function(){window.location='/register.html'}
  12. $.post('/login_check',{},function(data){
  13. if(data==0){
  14. Form_login.style.display=""
  15. }
  16. else{
  17. form_userInfo.style.display=""
  18. }
  19. })
  20. });
  21. }
  22. return{//這裡類似微型路由。非常靈活,非常方便
  23. load :nav_load
  24. };
  25. });

5.2 JQuery

JQ的功能只要require.js引用了之後基本上都是一樣的。

如果有需要可以到w3school上學習一下。

6.業務層

Post分析

  1. func XXXAction(w http.ResponseWriter, r *http.Request){
  2. r.parseForm()//有這個才能獲取參數
  3. r.Form["Email"]// 獲取Email 參數(String)
  4. // 寫接下來的業務。
  5. }

資源入口函數資源require分析(url分析固定寫法)

  1. func Foo(w http.ResponseWriter, r *http.Request){
  2. queryFile := strings.Split(r.URL.Path,"/")
  3. queryResource := queryFile[len(queryFile)-1]// 解析文件
  4. }
  5. //完成字符串分割之後,按照需求來獲取資源就可以了。

直接輸入object

  1. data, err := ioutil.ReadAll(r.Body)//直接讀取form為 json 字符串
  2. if err !=nil{
  3. utility.SimpleFeedBack(w,10,"failed to read body")
  4. pillarsLog.PillarsLogger.Print("failed to read body")
  5. return
  6. }
  7. k :=【BUSINESS OBJECT】
  8. err = json.Unmarshal(data,&k)
  9. if err !=nil{
  10. utility.SimpleFeedBack(w,13,"Pramaters failed!")
  11. pillarsLog.PillarsLogger.Print("Pramaters failed!")
  12. return
  13. }
  14. //方便快捷。再訪問參數的時候,直接調用結構體參數就可以了。
  15. //注意ajax調用函數的時候需要做出一些調整代碼如下:
  16. $.ajax([dist],JSON.stringify([data]),function(){},'json');//注意JSON

7.持久層

7.1 Mysql

其實不管什麼語言的Mysql驅動都是從PRO*C來的,所以會PRO*\C之後,啥都好說

Insert/Delete/Update

  1. stmt, err := mysqlUtility.DBConn.Prepare("INSERT INTO credit (credit_code, user_code, credit_rank) VALUES (?, ?, ?)")
  2. if err !=nil{
  3. pillarsLog.PillarsLogger.Print(err.Error())
  4. returnfalse, err
  5. }
  6. defer stmt.Close()
  7. _, err = stmt.Exec(credit.CreditCode, credit.UserCode, credit.CreditRank)
  8. if err !=nil{
  9. returnfalse, err
  10. }else{
  11. returntrue, err
  12. }
  13. //還是比較方便的

Query

  1. stmt, err := mysqlUtility.DBConn.Prepare(`SELECT commodity_code, commodity_name, description, picture,
  2. price, storage, count, status,
  3. insert_datetime, update_datetime FROM commodity WHERE commodity_code = ?`)
  4. if err !=nil{
  5. returnnil, err
  6. }
  7. defer stmt.Close()
  8. result, err := stmt.Query(commodityCode)
  9. if err !=nil{
  10. returnnil, err
  11. }
  12. defer result.Close()
  13. var commodity utility.Commodity
  14. if result.Next(){
  15. err = result.Scan(&(commodity.CommodityCode),&(commodity.CommodityName),&(commodity.Description),
  16. &(commodity.Picture),&(commodity.Price),&(commodity.Storage),&(commodity.Count),&(commodity.Status),
  17. &(commodity.InsertDatetime),&(commodity.UpdateDatetime))
  18. if err !=nil{
  19. pillarsLog.PillarsLogger.Print(err.Error())
  20. returnnil, err
  21. }
  22. }
  23. return&commodity, err

7.2 Mongodb

  1. err := mongoUtility.PictureCollection.Find(bson.M{"picturecode":*pictureCode}).One(&picture)

這裡只給出最簡單的例子。具體的看mgo的開發文檔就ok。還是比較簡單的。

8.單元測試注意事項

  • 測試命令 go test -v (沒有其他參數了!!!) 如果不帶-v只顯示結果,不顯示調試過程,主要是調試開發的時候用
  • 文件格式 xxx_test.go 但是建議改成 xxx_test0.go 或者喜歡改成別的也可以。
    • 由於測試先行的原則,在開發的時候一次測試也就一兩個函數。
    • 這樣相當於把其他測試注釋掉
  • 測試的時候的配置文件要放到測試目錄下面。別忘了。
  • 心態,錯誤太多一個一個來,要有個好心態。

9.LOG

注意在調試中Log的不可缺失性。

下面api如果不知道從何而來直接doc搜索就可以了。

  1. package utility
  2. import"log"
  3. import"os"
  4. import"fmt"
  5. // Logger Model min variable.
  6. varLogger*log.Logger
  7. var outFile *os.File
  8. // init function if Logger if not inited will invoke this function
  9. func init(){
  10. ifLogger==nil{
  11. propertyMap :=ReadProperty("pic.properties")
  12. logFileName := propertyMap["LogFile"]
  13. fmt.Println("Initial and Open log file ", logFileName)
  14. var err error
  15. outFile, err = os.OpenFile(logFileName, os.O_CREATE|os.O_APPEND|os.O_RDWR,0666)
  16. if err !=nil{
  17. panic(err.Error())
  18. }
  19. Logger= log.New(outFile,"", log.Ldate|log.Ltime|log.Llongfile)
  20. }
  21. }
  22. // CloseLogFile function : close Logger invoke file.
  23. func CloseLogFile(){
  24. outFile.Close()
  25. }

使用方法:

  1. utility.Logger.Println("Log test")

總結

  1. 看完這裡copy代碼日常工作還是能好應付一點。
  2. 如果是新手看完這個之後,看那麼厚的書就有一定的目標性了。能方便一點

Ubuntu 14.04 上搭建 Golang 開發環境配置 http://www.linuxidc.com/Linux/2015-02/113977.htm

Linux系統入門學習-在Linux中安裝Go語言 http://www.linuxidc.com/Linux/2015-02/113159.htm

Ubuntu 安裝Go語言包 http://www.linuxidc.com/Linux/2013-05/85171.htm

《Go語言編程》高清完整版電子書 http://www.linuxidc.com/Linux/2013-05/84709.htm

Go語言並行之美 -- 超越 “Hello World” http://www.linuxidc.com/Linux/2013-05/83697.htm

我為什麼喜歡Go語言 http://www.linuxidc.com/Linux/2013-05/84060.htm

Go語言內存分配器的實現 http://www.linuxidc.com/Linux/2014-01/94766.htm

Copyright © Linux教程網 All Rights Reserved