Go後臺專案實戰
本專案完全使用原生開發,沒有使用任何WEB框架(如:gin,beego,Martini等),和ORM(如:gorm,xorm,beego)
三層架構
三層架構(3-tier architecture) 通常意義上的三層架構就是將整個業務應用劃分為:介面層(User Interface layer)、業務邏輯層(Business Logic Layer)、資料訪問層(Data access layer)。區分層次的目的即為了“高內聚低耦合”的思想。在軟體體系架構設計中,分層式結構是最常見,也是最重要的一種結構。
控制層/介面層
因為我的專案中並沒有寫WEB頁面,所以就拿控制層來說,就是將你的請求從頁面傳到後臺程式碼
服務層/業務邏輯層
針對具體問題的操作,也可以說是對資料層的操作,對資料業務邏輯處理。(關鍵在於由原始資料抽象出邏輯資料)能夠提供interface\API層次上所有的功能。,“中間業務層”的實際目的是將“資料訪問層”的最基礎的儲存邏輯組合起來,形成一種業務規則
持久層/資料訪問層
該層所做事務直接操作資料庫,針對資料的增添、刪除、修改、查詢等。(關鍵在於粒度的把握)要保證“資料訪問層”的中的函式功能的原子性!即最小性和不可再分。“資料訪問層”只管負責儲存或讀取資料就可以了。
Controller組合封裝
Controller"基類"封裝,主要提供了一個儲存檔案的方法,主要用於form-data請求
type Controller struct { Data interface{} } type FileInfoTO struct { //圖片id -- 暫時沒有用 ID int64 //縮圖路徑 -- 暫時沒有用 CompressPath string //原圖路徑 ,儲存資料庫的路徑 Path string //原始的檔名 OriginalFileName string //儲存檔名 如:uuidutil FileName string //檔案大小 FileSize int64 } //解析Form-data中的檔案,不管上傳的檔案的欄位名(fieldname)是什麼,都會解析 func (p *Controller) SaveFiles(r *http.Request,relativePath string) []*FileInfoTO { r.ParseMultipartForm(32 << 20) m := r.MultipartForm if m == nil { log.Println("not multipartfrom !") return nil } fileInfos := make([]*FileInfoTO,0) filePath := constant.BASE_IMAGE_ADDRESS + relativePath utils.MakeDir(filePath) //files := m.File["files"] //根據上傳檔案時指定的欄位名(fieldname)獲取FileHeaders for _,fileHeaders := range m.File { //遍歷所有的所有的欄位名(fieldname)獲取FileHeaders for _,fileHeader := range fileHeaders{ file,err := fileHeader.Open() if err != nil { log.Println(err) return fileInfos } defer file.Close() name,err := utils.RandomUUID() if err != nil { log.Println(err) return fileInfos } fileType := utils.Ext(fileHeader.Filename,".jpg") newName := name.String() + fileType dst,err := os.Create(filePath + newName) if err != nil { log.Println(err) return fileInfos } fileSize,err := io.Copy(dst,file) if err != nil { log.Println(err) return fileInfos } fileInfos = append(fileInfos, &FileInfoTO{Path:relativePath + newName,OriginalFileName:fileHeader.Filename,FileName:newName,FileSize:fileSize}) } } return fileInfos }
ApiController主要用於使用者體系的一個登陸狀態的資訊獲取,根據請求中的session獲取服務端儲存的使用者資訊,如果你的後臺分使用者體系和管理端使用者體系,並且這兩個使用者體系分別儲存在兩個表中,這時你還可以定義一個BackController
type ApiController struct { Controller } func (p *ApiController) GetUserId(w http.ResponseWriter,r *http.Request) uint { user := p.GetUser(w,r) if user == nil { return 0 } return user.ID } func (p *ApiController) GetUser(w http.ResponseWriter,r *http.Request) *entity.User { session := GlobalSession().SessionStart(w,r) if session == nil { return nil } key_user := session.Get(constant.KEY_USER) if user,ok := key_user.(*entity.User);ok{ return user } return nil }
database
http
Router
路由處理的實現,其實也就是一個轉發的功能
type RouterHandler struct {
}
var mux = make(map[string]func(http.ResponseWriter,*http.Request))
func (p *RouterHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.URL.Path)
if fun, ok := mux[r.URL.Path]; ok {
fun(w, r)
return
}
//靜態資源
if strings.HasPrefix(r.URL.Path,constant.STATIC_BAES_PATH){
if fun, ok := mux[constant.STATIC_BAES_PATH]; ok {
fun(w, r)
return
}
}
http.Error(w, "error URL:"+r.URL.String(), http.StatusBadRequest)
}
func (p *RouterHandler) Router(relativePath string, handler func(http.ResponseWriter, *http.Request)) {
mux[relativePath] = handler
}
session
如果有登入功能,所以需要用到session來記住使用者的狀態,以下是session技術實現的主要型別與介面定義,(摘自:https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/06.0.md),會話過期時間可以自行設定,如果設定為一小時,在停止會話一小時後session就會過期,該session就會被自動刪除,如果回話都保持在一小時之內就可以一直訪問,session不會過期
//主要用於session的管理,過期處理等
type Manager struct {
cookieName string
lock sync.Mutex
provider Provider
maxLifeTime int64
}
//用於提供session儲存方式的一個介面標準,可以用於提供session儲存在記憶體、檔案、資料庫等方式
type Provider interface {
SessionInit(sid string)(Session,error)
SessionRead(sid string)(Session,error)
SessionDestroy(sid string) error
SessionGC(maxLifeTime int64)
}
//用於對session一些基本操作的定義
type Session interface {
Set(key, value interface{}) error
Get(key interface{}) interface{}
Delete(key interface{}) error
SessionID()string
}
靜態資源
靜態資源處理需要用到http.FileServer和http.StripPrefix函式,http.FileServer通常要跟http.StripPrefix結合使用
http.StripPrefix函式的作用之一,就是在將請求定向到你通過引數指定的請求處理處之前,將特定的prefix從URL中過濾出去。下面是一個瀏覽器或HTTP客戶端請求資源的例子:
/static/example.png
StripPrefix 函式將會過濾掉/static/,並將修改過的請求定向到http.FileServer所返回的Handler中去,因此請求的資源將會是:
/example.png
http.FileServer 返回的Handler將會進行查詢,並將與資料夾或檔案系統有關的內容以引數的形式返回給你(在這裡你將"static"作為靜態檔案的根目錄)。因為你的"example.txt"檔案在靜態目錄中,你必須定義一個相對路徑去獲得正確的檔案路徑。
根據需要定製訪問路徑
http.Handle("/tmpfiles/",http.StripPrefix("/tmpfiles/", http.FileServer(http.Dir("/tmp"))))
FileServer 已經明確靜態檔案的根目錄在"/tmp",但是我們希望URL以"/tmpfiles/"開頭。如果有人請求"/tempfiles/example.txt",我們希望伺服器能將檔案傳送給他。為了達到這個目的,我們必須從URL中過濾掉"/tmpfiles", 而剩下的路徑是相對於根目錄"/tmp"的相對路徑。如果我們按照如上做法,將會得到如下結果:
/tmp/example.png
演示
粗略的設計了幾個API,以下就是訪問API的請求與響應截圖,以下除了註冊和登入不會去檢測session,其它API都會檢測,要求登入才可以訪問。
未登入狀態下呼叫新增意見反饋介面
登入狀態下呼叫新增意見反饋介面
訪問靜態資源