1. 程式人生 > >Go後臺專案實戰

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都會檢測,要求登入才可以訪問。





未登入狀態下呼叫新增意見反饋介面


登入狀態下呼叫新增意見反饋介面



訪問靜態資源