由于业务需要,需要开发游戏的管理后台,由于之前未接触过前端,只对GO有比较粗浅的了解,所以使用GO来开发后台
我在搜索的时候被误导了一下,有知乎网友说,Golang就不用框架
的确,书上或者网上的DEMO都比较简单,对于有编程基础的人而言,的确很容易上手,但是对于长期做后端的人而言,其实并不会深刻感觉到有坑
忙碌了一段时间,终于闲下来了,打算再次重构一下,现有的后台
了解了一段时间之后,打算用beego框架,不过尝试了一个比较小的项目测试DEMO之后,发现一些小问题,虽说beego封装了好多好用的功能,但是对使用者而言,有时候,并非是好事,例如配置文件,日志文件这一块,如果要用beego的话,现有的变动比较大
在Golang交流中群中请教了一下大家,大家的都建议使用GIN,而不是beego,于是乎选择了GIN来重构,花费了不到两天时间,使用GIN框架重构了我所有的代码,总代码量(Golang:2万 JS:2万)
原先代码
func Processer(w http.ResponseWriter, r *http.Request) {
r.ParseForm() //坑死人,如果忘记写了,死活也获取不到name
fmt.Println(r.Form.Get("name"))
}
使用GIN框架之后
func Processer(c *gin.Context) {
fmt.Println(r.Query("name")) //GET name
fmt.Println(r.DefaultQuery("name", "Roger")) //GET name 增加了默认参数
fmt.Println(r.PostForm("name")) //POST name
fmt.Println(r.DefaultPostForm("name", "luojie")) //POST name 增加了默认参数
}
虽然GIN框架对于GET和POST有区别,但是方便之处增加了默认参数,同时对于开发人员的要求就是要对GET和POST有清晰的认识
func uploadConfig(w http.ResponseWriter, r *http.Request) {
file, handler, err := r.FormFile("uploadfile")
if err != nil {
log.Println("form file err: ", err)
//fmt.Fprintf(w, "文件名一定要输入")
return
}
defer file.Close()
fmt.Fprintf(w, "%v", handler.Header)
//创建上传的目的文件
f, err := os.OpenFile("./files/"+handler.Filename, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666)
if err != nil {
log.Println("open file err: ", err)
fmt.Fprintf(w, "文件打开失败")
return
}
defer f.Close()
//拷贝文件
io.Copy(f, file)
//返回
}
GIN框架
router.POST("/upload", func(c *gin.Context) {
// 单文件
file, _ := c.FormFile("file")
log.Println(file.Filename)
// 上传文件到指定的路径
c.SaveUploadedFile(file, dst)
//返回
})
很明显代码简单了很多,对于做文件上传,不再感觉到有压力,因为GIN极大简化了我们出错的机率
原来的代码
type MyServeMux struct{}
func (mux *MyServeMux) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) {
pattern = "/v1" + pattern
http.DefaultServeMux.HandleFunc(pattern, handler)
}
func main() {
mux := &MyServeMux{}
mux.HandleFunc("/login", loginEndpoint)
mux.HandleFunc("/submit", submitEndpoint)
mux.HandleFunc("/read", readEndpoint)
}
GIN框架
func main() {
router := gin.Default()
// Simple group: v1
v1 := router.Group("/v1")
{
v1.POST("/login", loginEndpoint)
v1.POST("/submit", submitEndpoint)
v1.POST("/read", readEndpoint)
}
}
开发人员,所要关注的东西更少了
原来的代码
func (mux *MyServeMux) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) {
pattern = "/v1" + pattern
http.DefaultServeMux.HandleFunc(pattern, handler)
}
func (mux *MyServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
//验证成功之后调用 http.DefaultServeMux.ServeHTTP(w, r)
//验证失败 return
}
func main() {
mux := &MyServeMux{}
mux.HandleFunc("/login", loginEndpoint)
mux.HandleFunc("/submit", submitEndpoint)
mux.HandleFunc("/read", readEndpoint)
}
GIN框架
func main() {
router := gin.Default()
// Simple group: v1
v1 := router.Group("/v1", checkAuth)
{
v1.POST("/login", loginEndpoint)
v1.POST("/submit", submitEndpoint)
v1.POST("/read", readEndpoint)
}
}
func checkAuth(c *gin.Context) {
//验证成功 调用c.Next()
//验证失败 调用c.Abort()
}
方便之处和前一条一致
没有参数绑定之前
func RechargeMakeUp(w http.ResponseWriter, r *http.Request) {
ip := util.GetRequestIP(r)
requestData := &model.WebRechargeMakeup{ActUserName: actUserName, ActIP: ip}
data, err := ioutil.ReadAll(r.Body)
if err != nil {
logger.Logger.Errorf("request data error")
util.SetErrorAjaxResponse(w, err)
return
} else {
logger.Logger.Debugf("data[%s]", string(data))
if err := json.Unmarshal(data, requestData); err != nil {
logger.Logger.Errorf("json Unmarshal error")
util.SetErrorAjaxResponse(w, err)
return
}
//....
}
}
可以绑定之后
func RechargeMakeUp(c *gin.Context) {
ip := model.GetRequestIP(c.Request)
requestData := &model.WebRechargeMakeup{ActUserName: actUserName, ActIP: ip}
if err := c.ShouldBindJSON(requestData); err != nil {
model.Logger.Errorf("json Unmarshal error")
model.SetErrorAjaxResponse(c, err)
return
}
//...
}
极大减少了代码,提高了我们的工作效率
原来的静态文件设计
func main() {
http.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir("static"))))
}
GIN框架
func main() {
r := gin.Default()
r.Static(/static/, "./static")
}
讲道理,前一个设计太反人类,我曾在这里面,蛋疼了好久,才正常的将静态目录配置正常,而GIN框架里面简单明了
原来的代码
func process(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%s", "Hello World!
") //返回网页
fmt.Fprintf(w, "{"message":"hello world"}") //返回JSON 结构体还需要手动定义
fmt.Fprintf(w, "/static/login.data") //由浏览器来解析是不是文件
//如果返回是网页的话,还需要使用模板解析非常麻烦
}
GIN框架
func process(c *gin.Context) {
c.HTML(http.StatusOK, "hello.html", nil) //返回网页
c.JSON(http.StatusOK, gin.H{"message":"Hello World!"}) //返回JSON
c.String(http.StatusOK, "Hello World!") //返回字符串
c.File("/static/login.data") //返回文件
//... 还有其它
}
非常喜欢这么丰富的返回,之前的痛点在于许多Ajax的返回都使用的是JS,每个返回我们必须手动转化成字符串再返回,虽然可以直接写一个通用返回将interface转成字符串,但是欠缺了相应了思维,在不断了解新框架的过程中,可以不断完善自己的认知,这种感觉真棒
func formatAsDate(t time.Time) string {
year, month, day := t.Date()
return fmt.Sprintf("%d%02d/%02d", year, month, day)
}
func main() {
r := gin.Default()
r.Delims("{[{", "}]}") //可以自定义模板分隔符
r.SetFuncMap(template.FuncMap{
"formatAsDate": formatAsDate,
}) //传递模板函数
r.LoadHTMLGlob("./view/**/*") //直接读取所有的模板文件,如果有语法错误,启动就会报错
}
目前对于GIN框架的其他好用功能还在不断探索之中