Go基础学习-Gin

Gin框架

  • gin框架路由使用前缀树,路由注册的过程是构造前缀树的过程,路由匹配的过程就是查找前缀树的过程。
  • gin框架的中间件函数和处理函数是以切片形式的调用链条存在的,我们可以顺序调用也可以借助c.Next()方法实现嵌套调用。
  • 借助c.Set()和c.Get()方法我们能够在不同的中间件函数中传递数据。
  • 使用前需要调用gin.Default()生成gin引擎
  • 调用r.Run()结尾,可以指定端口

安装

go get -u github.com/gin-gonic/gin

静态文件解析

  • 一般放在statics文件夹下分类存放
//静态文件的处理:静态文件指html页面用到的css\js\图片
r.Static("/relativepath", "./statics") //必须在模板解析之前;前面指定相对路径(html模板中使用,后面指定静态文件位置)

//使用
<link rel="stylesheet" href="/relativepath/demo.css">
<script src="/relativepath/vendor/jquery/jquery.min.js"></script>

HTML渲染

  • HTML一般放在templates文件夹中;可以通过define 来给模板命名来定位;
  • 模板文件后缀可以是.html也可以是.tmpl
//例如
{{define "post/index.tmpl"}}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>posts/index</title>
</head>
<body>
    {{.title}}
</body>
</html>
{{end}}

//模板加载的两种方式
r.LoadHTMLFiles("templates/post/index.tmpl", "templates/user/index.tmpl")//便于解析单个文件
r.LoadHTMLGlob("templates/**/*") //便于解析多个文件 **表示目录 *表示文件
//GET提交
r.GET("/post/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "post/index.tmpl", gin.H{ //第二个参数与模板中define 的文件名保持一致
			"title": "post/index渲染",
		})
	})


  • 自定义模板
// 自定义模板函数  必须写在模板解析之前
r.SetFuncMap(template.FuncMap{
	"safe": func(s string) template.HTML {
		return template.HTML(s)
	},
})
//使用:
<body>
    <h1> {{.title}}</h1>
    <div> {{.setFuncMap|safe}}</div>
</body>
  • 模板继承(调用"github.com/gin-contrib/multitemplate"库实现)
/*
示例;
假设home.tmpl和index.tmpl继承base.tmpl
templates
├── includes
│   ├── home.tmpl
│   └── index.tmpl
├── layouts
│   └── base.tmpl
└── scripts.tmpl
*/
func loadTemplates(templatesDir string) multitemplate.Renderer {
	r := multitemplate.NewRenderer()
	layouts, err := filepath.Glob(templatesDir + "/layouts/*.tmpl")
	if err != nil {
		panic(err.Error())
	}
	includes, err := filepath.Glob(templatesDir + "/includes/*.tmpl")
	if err != nil {
		panic(err.Error())
	}
	// 为layouts/和includes/目录生成 templates map
	for _, include := range includes {
		layoutCopy := make([]string, len(layouts))
		copy(layoutCopy, layouts)
		files := append(layoutCopy, include)
		r.AddFromFiles(filepath.Base(include), files...)
	}
	return r
}


func main(){
	r := gin.Default()
	r.HTMLRender = loadTemplates("./templates")
	r.GET("/index", func (c *gin.Context){
	c.HTML(http.StatusOK, "index.tmpl", nil)
})
	r.GET("/home", func (c *gin.Context){
	c.HTML(http.StatusOK, "home.tmpl", nil)
})
	r.Run()
}

JSON渲染

//方式1
r.GET("/hello", func(c *gin.Context) {
	// c.JSON:返回JSON格式的数据
	c.JSON(200, gin.H{
		"message": "Hello world!",
	})
})
//方式2
r.GET("/helloJSON2", func(c *gin.Context) {
	type msg struct {
		Name  string `json:"name"`
		Age   int
		Hobby string
	}
	var dataObj = msg{
		"wuzb",
		12,
		"soccer",
	}
	c.JSON(http.StatusOK, dataObj)
})

获取请求参数

//获取URL中querystring参数参数
//www.xxx.com/user/search?id=2&page=1
func main() {
	r := gin.Default()
	r.GET("/user/search", func(c *gin.Context) {
		userid := c.DefaultQuery("id ", "1")
		//userid := c.Query("id")
		page:= c.Query("page")
		//输出json结果给调用方
		c.JSON(http.StatusOK, gin.H{
			"message":  "ok",
			"userid ": userid ,
			"page":  page,
		})
	})
	r.Run()
}

//获取form参数
func main() {
	r := gin.Default()
	r.POST("/user/search", func(c *gin.Context) {
		// DefaultPostForm取不到值时会返回指定的默认值
		//username := c.DefaultPostForm("username", "zs")
		username := c.PostForm("username")
		address := c.PostForm("address")
		//输出json结果给调用方
		c.JSON(http.StatusOK, gin.H{
			"message":  "ok",
			"username": username,
			"address":  address,
		})
	})
	r.Run(":8080")
}

//获取path参数
func main() {
	r := gin.Default()
	r.GET("/user/search/:username/:address", func(c *gin.Context) {
		username := c.Param("username")
		address := c.Param("address")
		c.JSON(http.StatusOK, gin.H{
			"message":  "ok",
			"username": username,
			"address":  address,
		})
	})
	r.Run(":8080")
}

//通过bind(基于反射)来自动绑定参数到结构体
type Login struct {
	Username string `form:"name"json:"name"`
	Password string `form:"pwd"json:"pwd"`
}
//shouldBind() 能够基于请求自动提取JSON、form表单和QueryString类型的数据,并把值绑定到指定的结构体对象。
//绑定JSON的示例 ({"name": "wz", "pwd": "123456"})
r.GET("/shudBind", func(c *gin.Context) {
	var u Login
	if err := c.ShouldBind(&u); err == nil {
		c.JSON(http.StatusOK, gin.H{
			"name": u.Username,
			"pwd":  u.Password,
		})
	} else {
		c.JSON(http.StatusOK, gin.H{
			"error": err.Error(),
		})
	}
})

重定向

//HTTP重定向 
r.GET("/test", func(c *gin.Context) {
	c.Redirect(http.StatusMovedPermanently, "http://www.sogo.com/")
})
//路由重定向
r.GET("/test", func(c *gin.Context) {
    // 指定重定向的URL
    c.Request.URL.Path = "/test2"
    r.HandleContext(c)
})
r.GET("/test2", func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{"hello": "world"})
})

路由

//get
r.GET("/get", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.html", gin.H{})
	})
//post
r.POST("/post", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.html", gin.H{})
	})
//any可以匹配所有请求方法
r.Any("/any", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.html", gin.H{})
	})
//NoRoute:用于处理非注册的路由,报404页面
r.NoRoute(func(c *gin.Context) {
		c.HTML(http.StatusNotFound, "404.html", nil)
	})
//路由组
ginRoute := gin.Default()
prodGroup := ginRoute.Group("/prod")
{
	prodGroup.POST("/list", func(c *gin.Context) {c.HTML(http.StatusOK,"index.html", gin.H{})})
	prodGroup.GET("/detail/:pid",func(c *gin.Context){c.HTML(http.StatusOK,"index.html", gin.H{})})
}
return ginRoute

中间件

//中间件函数(统计请求处理函数的耗时)
func m1() gin.HandlerFunc {
	//连接数据库或者其他的一些操作
	return func(c *gin.Context) {
		//存放具体的逻辑
		start := time.Now()
		c.Next() //调用后续的函数
		// c.Abort() //阻止后续的函数
		//c.Set()//往上下文中存值;通过c.Get()取值;实现中间件的值传递
		cost := time.Since(start)
		fmt.Printf("cost %d times", cost)
	}

}

//中间件使用
//1.单个路由注册
r.GET("/middleware", m1(), func(c *gin.Context) { //某个路由单独注册中间体
		c.JSON(http.StatusOK, gin.H{
			"msg": "middleware",
		})
	})
//2.注册一个全局中间件
r.Use(m1())
//3.组注册中间件
r.Use(m1()){
把组路由给包裹进来
}
//或者
prodGroup := ginRoute.Group("/prod",m1())
{
	prodGroup.POST("/list", func(c *gin.Context) {c.HTML(http.StatusOK,"index.html", gin.H{})})
	prodGroup.GET("/detail/:pid",func(c *gin.Context){c.HTML(http.StatusOK,"index.html", gin.H{})})
}

文件上传

//html
<form action="/upload" method="post" enctype="multipart/form-data">
    <input type="file" name="f1">
    <input type="submit" value="上传">
</form>
//go
//单文件上传处理
func main() {
	router := gin.Default()
	// 处理multipart forms提交文件时默认的内存限制是32 MiB
	// 可以通过下面的方式修改
	// router.MaxMultipartMemory = 8 << 20  // 8 MiB
	router.POST("/upload", func(c *gin.Context) {
		// 单个文件
		file, err := c.FormFile("f1")
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{
				"message": err.Error(),
			})
			return
		}

		log.Println(file.Filename)
		dst := fmt.Sprintf("C:/tmp/%s", file.Filename)
		// 上传文件到指定的目录
		c.SaveUploadedFile(file, dst)
		c.JSON(http.StatusOK, gin.H{
			"message": fmt.Sprintf("'%s' uploaded!", file.Filename),
		})
	})
	router.Run()
}
//多文件上传处理
func main() {
	router := gin.Default()
	// 处理multipart forms提交文件时默认的内存限制是32 MiB
	// 可以通过下面的方式修改
	// router.MaxMultipartMemory = 8 << 20  // 8 MiB
	router.POST("/upload", func(c *gin.Context) {
		// Multipart form
		form, _ := c.MultipartForm()
		files := form.File["file"]

		for index, file := range files {
			log.Println(file.Filename)
			dst := fmt.Sprintf("C:/tmp/%s_%d", file.Filename, index)
			// 上传文件到指定的目录
			c.SaveUploadedFile(file, dst)
		}
		c.JSON(http.StatusOK, gin.H{
			"message": fmt.Sprintf("%d files uploaded!", len(files)),
		})
	})
	router.Run()
}

注意:

//异步(无法使用他的原始上下文,必须使用他的只读副本)
r.GET("/request-async", func(c *gin.Context) {
	copyC := c.Copy()
	go func() {
		time.Sleep(3 * time.Second)
		fmt.Println("异步请求:", copyC)
	}()
})

/*
gin.Default()默认使用了Logger和Recovery中间件:
Logger中间件将日志写入gin.DefaultWriter,即使配置了GIN_MODE=release。
Recovery中间件会recover任何panic。如果有panic的话,会写入500响应码。
*/
//需要建立个空白中间件的gin引擎的话可以使用
gin.New()

猜你喜欢

转载自blog.csdn.net/wzb_wzt/article/details/107411582