go进阶(1) gin框架
gin介绍
- Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确,具有快速灵活,容错方便等特点;
- 对于golang而言,web框架的依赖要远比Python,Java之类的要小。自身的
net/http
足够简单,性能也非常不错; - 借助框架开发,不仅可以省去很多常用的封装带来的时间,也有助于团队的编码风格和形成规范;
gin安装
在go工作空间中创建新的目录,自定义名,使用
go mod init example/gin
对该包进行初始化;
创建一个go文件;
使用命令
go get -u github.com/gin-gonic/gin
在包中导入gin框架;
还可以导入
import "net/http"
方便使用HTTP状态码常量;
gin开始
在文件中写入
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
// 1.创建路由
r := gin.Default()
// 2.绑定路由规则,执行的函数
// gin.Context,封装了request和response
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "hello World!")
})
// 3.监听端口,默认在8080
// Run("里面不指定端口号默认为8080")
r.Run(":8000")
}
使用
go run .
启动项目后,在浏览器中输入http://localhost:8000,回车后界面上显示Hello World!并且命令行中输出
则运行成功;
param配置
在地址中添加param,可以使用 : 与 ***** 对param进行标注
func main() {
r := gin.Default()
r.GET("/user/:name/*action",func (c *gin.Context) {
name:=c.Param("name")
action:=c.Param("action")
action=strings.Trim(action,"/")
c.String(http.StatusOK,name+" is "+action)
})
r.Run(":8000")
}
url地址中*param
可以省略不写,但 :name
必须存在;
举例:http://localhost:8000/user/lxh
可以访问,但http://localhost:8000/user
不可以访问;
*param
会携带 '/' ,需要使用strings.Trim()
来去除字符串头尾的 '/';
query配置
在地址中添加query,若不添加可以使用DefaultQuery(key,defaultValue)
来使用配置的默认值
func main() {
r := gin.Default()
r.GET("/user", func(c *gin.Context) {
//指定默认值
//http://localhost:8000/user 才会打印出来默认的值
name := c.DefaultQuery("name", "枯藤")
c.String(http.StatusOK, fmt.Sprintf("hello %s", name))
})
r.Run(":8000")
}
若地址为http://localhost:8000/user?name=lxh
则输出hello lxh
;
若地址为http://localhost:8000/user
则输出hello 枯藤
;
表单配置
表单传输为post请求,http常见的传输格式为四种:
- application/json
- application/x-www-form-urlencoded
- application/xml
- multipart/form-data
表单参数可以通过PostForm()方法获取,该方法默认解析的是x-www-form-urlencoded或from-data格式的参数
对于前端代码为:
<!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>Document</title>
</head>
<body>
<form action="http://localhost:8000/form" method="post" action="application/x-www-form-urlencoded">
用户名:<input type="text" name="userName" placeholder="请输入你的用户名"> <br>
密 码:<input type="password" name="userpassword" placeholder="请输入你的密码"> <br>
<input type="submit" value="提交">
</form>
</body>
</html>
后端go代码为:
func main() {
r := gin.Default()
r.POST("/form",func (c *gin.Context) {
types:=c.DefaultPostForm("type","post")
userName:=c.PostForm("userName")
password:=c.PostForm("userpassword") c.String(http.StatusOK,fmt.Sprintf("userName:%s,password:%s,type:%s",userName,password,types))
})
r.Run(":8000")
}
则当前端跳转到http://localhost:8000/form
提交数据则界面显示为:
userName:lxh,password:123456,type:post
;
可以使用原生的Request获得更详细的前端数据,如请求头、文件大小等;
func main() {
r := gin.Default()
r.POST("/upload", func(c *gin.Context) {
_, headers, err := c.Request.FormFile("file")
if err != nil {
log.Printf("Error when try to get file: %v", err)
}
//headers.Size 获取文件大小
if headers.Size > 1024*1024 {
fmt.Println("文件太大了")
return
}
//headers.Header.Get("Content-Type")获取上传文件的类型
if headers.Header.Get("Content-Type") != "image/png" {
fmt.Println("只允许上传png图片")
return
}
c.SaveUploadedFile(headers, "./video/"+headers.Filename)
c.String(http.StatusOK, headers.Filename)
})
r.Run(":8000")
}
对于多文件获取:
func main() {
r := gin.Default()
r.MaxMultipartMemory = 8 << 20
r.POST("/upload", func(c *gin.Context) {
form, err := c.MultipartForm()
if err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("get err %s", err.Error()))
}
// 获取所有图片
files := form.File["files"]
// 遍历所有图片
for _, file := range files {
// 逐个存
if err := c.SaveUploadedFile(file, file.Filename); err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("upload err %s", err.Error()))
return
}
}
c.String(200, fmt.Sprintf("upload ok %d files", len(files)))
})
r.Run(":8000")
}
使用
c.MultipartForm()
来获得所有表单集合数据;
通过
form.File["files"]
来获取file类型的数据集合,对其遍历存储到本地中;
routes group
routes group是为了管理一些相同的URL;
func main() {
// 1.创建路由
// 默认使用了2个中间件Logger(), Recovery()
r := gin.Default()
// 路由组1 ,处理GET请求
v1 := r.Group("/v1")
// {} 是书写规范
{
v1.GET("/login", login)
v1.GET("submit", submit)
}
v2 := r.Group("/v2")
{
v2.POST("/login", login)
v2.POST("/submit", submit)
}
r.Run(":8000")
}
func login(c *gin.Context) {
name := c.DefaultQuery("name", "jack")
c.String(200, fmt.Sprintf("hello %s\n", name))
}
func submit(c *gin.Context) {
name := c.DefaultQuery("name", "lily")
c.String(200, fmt.Sprintf("hello %s\n", name))
}
数据解析和绑定
json
对于post的提交方式,提交数据格式为application/json,需要定义一个结构体来解析请求体request的body数据,还可以使用Tag来指定json数据的key值,binding字段可以设为必选;
// 定义接收数据的结构体
type Login struct {
// binding:"required"修饰的字段,若接收为空值,则报错,是必须字段
User string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"`
Pssword string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"`
}
func main() {
// 1.创建路由
// 默认使用了2个中间件Logger(), Recovery()
r := gin.Default()
// JSON绑定
r.POST("loginJSON", func(c *gin.Context) {
// 声明接收的变量
var json Login
// 将request的body中的数据,自动按照json格式解析到结构体
if err := c.ShouldBindJSON(&json); err != nil {
// 返回错误信息
// gin.H封装了生成json数据的工具
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 判断用户名密码是否正确
if json.User != "root" || json.Pssword != "admin" {
c.JSON(http.StatusBadRequest, gin.H{"status": "304"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "200"})
})
r.Run(":8000")
}
使用
c.ShouldBindJSON(&json)
将数据储存到了定义的结构体变量json中,再对其进行处理;
最终结果为:
form
使用Bind()来解析form类型的数据;
// 定义接收数据的结构体
type Login struct {
// binding:"required"修饰的字段,若接收为空值,则报错,是必须字段
User string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"`
Pssword string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"`
}
func main() {
// 1.创建路由
// 默认使用了2个中间件Logger(), Recovery()
r := gin.Default()
// JSON绑定
r.POST("/loginForm", func(c *gin.Context) {
// 声明接收的变量
var form Login
// Bind()默认解析并绑定form格式
// 根据请求头中content-type自动推断
if err := c.Bind(&form); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 判断用户名密码是否正确
if form.User != "root" || form.Pssword != "admin" {
c.JSON(http.StatusBadRequest, gin.H{"status": "304"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "200"})
})
r.Run(":8000")
}
url
对于URL中的param数据,也可以通过
c.ShouldBindUri()
来进行解析获取;
func main() {
// 1.创建路由
// 默认使用了2个中间件Logger(), Recovery()
r := gin.Default()
// JSON绑定
r.GET("/:user/:password", func(c *gin.Context) {
// 声明接收的变量
var login Login
// Bind()默认解析并绑定form格式
// 根据请求头中content-type自动推断
if err := c.ShouldBindUri(&login); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 判断用户名密码是否正确
if login.User != "root" || login.Pssword != "admin" {
c.JSON(http.StatusBadRequest, gin.H{"status": "304"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "200"})
})
r.Run(":8000")
}
gin函数
gin.DisableConsoleColor()
禁用控制台颜色
gin.H类型
gin.H封装了生成json数据的工具,定义的map[String]any类型
gin.Default() 函数
默认赋值给变量r
r.GET(relativePath string, handlers ...gin.HandlerFunc)
作用:对于GET的restful请求方式进行捕获;
参数:第一个参数为地址路由名,第二个参数为内嵌函数用于处理逻辑;
举例:r.GET("/example", func(c *gin.Context) {})
r.POST(relativePath string, handlers ...gin.HandlerFunc)
作用:对于POST的restful请求方式进行捕获;
参数:第一个参数为相对地址路由名,第二个参数为内嵌函数用于处理逻辑;
举例:r.POST("/example", func(c *gin.Context) {})
r.LoadHTMLFiles(files ...string)
作用:gin框架获取html文件地址,可以在接口设计中使用该html文件地址;
参数:html文件地址;
举例:r.LoadHTMLFiles("index.html")
r.LoadHTMLGlob(pattern string)
作用:gin框架获取一个地址匹配项的所有html文件;
参数:正则匹配项;
举例:r.LoadHTMLGlob(".template/*")
r.Run(addr ...string)
作用:将路由器附加到http上,并开始监听服务器,不填参数默认为8080端口;
参数:port端口;
举例:r.Run(":8000")
gin.Context结构体
默认参数名为c
c.Param(key string) string
作用:捕获URL地址的param,相当于c.Params.ByName(key),有两种捕获方式 ':param' 与 *'param' ,对于第一种可以直接获得对应param的值,第二种获得开头携带有 '/' 的值;
参数:param名;
举例:name:=c.Param("name")
c.DefaultQuery(key string, defaultValue string) string
作用:捕获URL地址的query,若捕获不到数据可以使用参数defaultValue的值;
参数:第一个参数为query的参数名,第二个参数为默认query参数值;
举例:name:=c.DefaultQuery("name","lxh")
c.PostForm(key string) (value string)
作用:捕获前端表单中对应key的值;
参数:表单数据里属性name的键值;
举例:userName:=c.PostForm("userName")
c.DefaultPostForm(key string, defaultValue string) string
作用:捕获前端表单中对应key的值,若捕获不到数据可以使用参数defaultValue的值;
参数:第一个参数为属性name的参数名,第二个参数为默认name的参数值;
举例:types:=c.DefaultPostForm("type","post")
c.FormFile(name string) (*multipart.FileHeader, error)
作用:捕获前端提交的文件;
参数:前端input标签属性type为file的数据;
举例:file, err := c.FormFile("file")
c.Request.FormFile(key string) (multipart.File, *multipart.FileHeader, error)
作用:获得原生Request的formFile,可以获得更多请求的数据;
参数:前端input标签属性type为file的name值;
举例:_, headers, err := c.Request.FormFile("file")
c.SaveUploadedFile(file *multipart.FileHeader, dst string) error
作用:保存文件数据到本地中;
参数:第一个参数为二进制文件,第二个参数为保存的路径文件名;
举例:c.SaveUploadedFile(file, file.Filename)
c.String(code int, format string, values ...any)
作用:在页面中显示字符串;
参数:第一个参数为code状态码,一般可以使用net/http
包的状态码常量,第二个参数为要输出的字符串;
举例:c.String(http.StatusOK,"ok")
c.HTML(code int, name string, obj any)
作用:响应返回给前端一个在服务端地址为name的html文件;
参数:第一个参数为code状态码,第二个参数为html文件地址,第三个参数为json数据;
举例:c.HTML(http.StatusOK, "index.html", gin.H{"title": "Posts",})
c.JSON(code int, obj any)
作用:在响应体中放入json数据;
参数:第一个参数为code状态码,一般可以使用net/http
包的状态码常量,第二个参数为要输出的json数据,一般使用gin.H{"key" : value,...}的gin自定义类型来存储json数据;
举例:c.JSON(200, gin.H{"message": file.Filename})
c.AsciiJSON(code int, obj any)
作用:在响应体中放入只包含可以转换成ASCLL码的json数据,相比较c.JSON(),可以满足部分只准许ascll码的中间件或者客户端;
参数:第一个参数为code状态码,一般可以使用net/http
包的状态码常量,第二个参数为要输出的json数据,一般使用gin.H{"key" : value,...}的gin自定义类型来存储json数据;
举例:c.JSON(200, gin.H{"message": file.Filename})
c.JSONP(code int, obj any)
作用:跨域在响应体中放入json数据,传输格式为application/javascript
;
参数:第一个参数为code状态码,一般可以使用net/http
包的状态码常量,第二个参数为要输出的json数据,一般使用gin.H{"key" : value,...}的gin自定义类型来存储json数据;
举例:c.JSONP(200, gin.H{"message": file.Filename})
c.PureJSON(code int, obj any)
作用:在响应体中放入json数据,对特殊字符不使用unicode实体来替换,而是使用字面;
参数:第一个参数为code状态码,一般可以使用net/http
包的状态码常量,第二个参数为要输出的json数据,一般使用gin.H{"key" : value,...}的gin自定义类型来存储json数据;
举例:c.JSONP(200, gin.H{"message": <br>加粗</br>})
c.SecureJSON(code int, obj any)
作用:在响应体中放入json数据,并为了防止劫持json的列表数据,默认增加 while(1);
前缀,也可以通过r.SecureJsonPrefix(")]}',\n")
自定义前缀,传输格式为application/json
;
参数:第一个参数为code状态码,一般可以使用net/http
包的状态码常量,第二个参数为要输出的json数据,一般使用gin.H{"key" : value,...}的gin自定义类型来存储json数据;
举例:c.SecureJSON(http.StatusOK,[]string{"hello"," world"})
c.MultipartForm() (*multipart.Form, error)
作用:获得解析后的多表单数据集合;
c.ShouldBindJSON(obj any) error
作用:将request的body中的数据,自动按照json格式解析到结构体;
参数:属于json类型的指针变量;
举例:err := c.ShouldBindJSON(&json)
c.ShouldBindUri(obj any) error
作用:将url中的param,解析到结构体中;
参数:属于json类型的指针变量;
举例:err := c.ShouldBindUri(&login)
c.ShouldBind(obj any) error
作用:自动解析数据格式,对于解析失败,可以在服务端捕捉报错信息,并对返回的数据进行处理;
c.Bind(obj any) error
作用:根据Method与Content-Type来自动选择解析的类型,相对于ShouldBind()
,Bind()
会在解析失败后自动返回400状态码的报错;
参数:指针变量;
举例:err := c.Bind(&form);
c.ShouldBindBodyWith(obj any, bb binding.BindingBody) (err error)
作用:按照参数bb的类型来解析body数据,并可以多次复用;
参数:第一个参数为指针变量,第二个参数是对应解析的类型;
举例:errB := c.ShouldBindBodyWith(&obj,binding.JSON);
c.DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string)
作用:用于从 io.Reader
接口中读取数据并发送给客户端的高级响应方法,适用于处理动态生成或大数据量的响应场景;
参数:第一个参数为code状态码,第二个参数为二进制文本长度,第三个参数为传输格式,第四个参数为response响应的body,第五个参数为附加json数据;
举例:c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders)
c.Cookie(name string) (string, error)
作用:获得客户端cookie中key为name的value值;
参数:cookie的key值;
举例:c.Cookie("gin_cookie")
c.SetCookie(name string, value string, maxAge int, path string, domain string, secure bool, httpOnly bool)
作用:在客户端中创建一个cookie数据,key为name,并设置了过期时间maxAge,cookie可访问的路径path(若为“/”则默认为全网站都可以访问),cookie的域名(若为"localhost"就是本地可访问),secure为true则只允许HTTPS协议访问,为false则可以允许HTTP协议访问,httpOnly为true则不会被客户端的JavaScript代码访问;
参数:第一个参数为cookie的key值,第二个参数为value值,第三个参数为超时时间(s),第四个参数为可访问路径,第五个参数为可访问ip,第六个参数为是否允许安全协议,第七个参数为是否允许客户端JavaScript代码访问;
举例:c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true)