本項目完全使用原生開發,沒有使用任何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請求
import("io""mime/multipart""net/http""path")constBASE_IMAGE_ADDRESS ="./img/"typeControllerstruct{Datainterface{}}typeFileInfoTOstruct{//圖片id -- 暫時沒有用IDint64//縮圖路徑 -- 暫時沒有用CompressPathstring//原圖路徑 ,儲存資料庫的路徑Pathstring//原始的檔案名稱OriginalFileNamestring//隱藏檔名 如:uuidutilFileNamestring//檔案大小FileSizeint64}//擷取上傳檔案的數量func(p *Controller)GetFileNum(r *http.Request,keys ...string)int{m := r.MultipartFormifm ==nil{return0}iflen(keys) ==0{varnumintfor_,fileHeaders :=rangem.File {num +=len(fileHeaders)}returnnum}else{varnumintfor_,value :=rangekeys {num +=len(m.File[value])}returnnum}}//解析Form-data中的檔案,如果不傳keys,不管上傳的檔案的欄位名(filename)是什麼,都會解析,否則只會解析keys指定的檔案func(p *Controller)SaveFiles(r *http.Request,,relativePathstring,keys ...string)[]*FileInfoTO{r.ParseMultipartForm(32<<20)m := r.MultipartFormifm ==nil{log.Println("not multipartfrom !")returnnil}fileInfos :=make([]*FileInfoTO,0)filePath := BASE_IMAGE_ADDRESS + relativePathfileutil.MakeDir(filePath)iflen(keys) ==0{for_,fileHeaders :=rangem.File {//遍曆所有的所有的欄位名(filename)擷取FileHeadersfor_,fileHeader :=rangefileHeaders{to := p.saveFile(filePath,relativePath,fileHeader)fileInfos =append(fileInfos,to)}}}else{for_,value :=rangekeys {fileHeaders := m.File[value]//根據上傳檔案時指定的欄位名(filename)擷取FileHeadersfor_,fileHeader :=rangefileHeaders{to := p.saveFile(filePath,relativePath,fileHeader)fileInfos =append(fileInfos,to)}}}returnfileInfos}//儲存單個檔案func(p *Controller)saveFile(filePath,relativePathstring,fileHeader *multipart.FileHeader)*FileInfoTO{file,err := fileHeader.Open()iferr !=nil{log.Println(err)returnnil}deferfile.Close()name,err := uuidutil.RandomUUID()iferr !=nil{log.Println(err)returnnil}fileType := fileutil.Ext(fileHeader.Filename,".jpg")newName := name + fileTypedst,err := os.Create(filePath + newName)iferr !=nil{log.Println(err)returnnil}deferdst.Close()fileSize,err := io.Copy(dst,file)iferr !=nil{log.Println(err)returnnil}return&FileInfoTO{Path:relativePath + newName,OriginalFileName:fileHeader.Filename,FileName:newName,FileSize:fileSize}}
fileutilimport("os""path")//建立多級目錄funcMkDirAll(pathstring)bool{err := os.MkdirAll(path, os.ModePerm)iferr !=nil{returnfalse}returntrue}//檢測檔案夾或檔案是否存在funcExist(filestring)bool{if_,err := os.Stat(file);os.IsNotExist(err){returnfalse}returntrue}//擷取檔案的類型,如:.jpg//如果擷取不到,返回預設類型defaultExtfuncExt(fileNamestring,defaultExtstring)string{t := path.Ext(fileName)iflen(t) ==0{returndefaultExt}returnt}/// 檢驗檔案夾是否存在,不存在 就建立funcMakeDir(filePathstring){if!Exist(filePath) {MkDirAll(filePath)}}//刪除檔案funcRemove(namestring)bool{err := os.Remove(name)iferr !=nil{returnfalse}returntrue}
uuidtuilimport("encoding/base64""math/rand")funcRandomUUID()(string,error){b :=make([]byte,32)if_,err := rand.Read(b);err !=nil{return"",err}returnbase64.URLEncoding.EncodeToString(b),nil}
ApiController主要用於使用者體系的一個登陸狀態的資訊擷取,根據請求中的session擷取服務端儲存的使用者資訊,如果你的後台分使用者體系和管理端使用者體系,並且這兩個使用者體系分別儲存在兩個表中,這時你還可以定義一個BackController
typeApiControllerstruct{Controller}func (p *ApiController) GetUserId(w http.ResponseWriter,r *http.Request) uint{user := p.GetUser(w,r)ifuser ==nil{return0}returnuser.ID}func (p *ApiController) GetUser(w http.ResponseWriter,r *http.Request) *entity.User{session := GlobalSession().SessionStart(w,r)ifsession ==nil{returnnil}key_user := session.Get(constant.KEY_USER)ifuser,ok := key_user.(*entity.User);ok{returnuser}returnnil}
database
持久層的實現:https://blog.csdn.net/cj_286/article/details/80363796
http
http server的實現:https://blog.csdn.net/cj_286/article/details/80256988
Router
路由處理的實現,其實也就是一個轉寄的功能
type RouterHandler struct {}varmux = make(map[string]func(http.ResponseWriter,*http.Request))func (p *RouterHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {fmt.Println(r.URL.Path)iffun, ok := mux[r.URL.Path]; ok {fun(w, r)return}//靜態資源ifstrings.HasPrefix(r.URL.Path,constant.STATIC_BAES_PATH){iffun, 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 stringlock sync.Mutexprovider ProvidermaxLifeTime int64}//用於提供session儲存方式的一個介面標準,可以用於提供session儲存在記憶體、檔案、資料庫等方式type Provider interface {SessionInit(sid string)(Session,error)SessionRead(sid string)(Session,error)SessionDestroy(sid string) errorSessionGC(maxLifeTime int64)}//用於對session一些基本操作的定義type Session interface {Set(key, value interface{}) errorGet(key interface{}) interface{}Delete(key interface{}) errorSessionID()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都會檢測,要求登入才可以訪問。
imageimageimageimage
未登入狀態下調用添加意見反饋介面
image
登入狀態下調用添加意見反饋介面
image
訪問靜態資源
image
項目地址:https://github.com/xiaojinwei/cgo
參考:https://studygolang.com/articles/9197
https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/06.0.md