初始gin
go
package main
import "github.com/gin-gonic/gin"
type Response struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data any `json:"data"`
}
func index(c *gin.Context) {
c.JSON(200, Response{
Code: 0,
Msg: "111",
Data: 222,
})
}
func main() {
gin.SetMode("release")
//1.初始化
r := gin.Default()
//挂载路由
r.GET("/index", index)
//绑定端口,运行
r.Run(":8081")
}
响应
enter.go
go
package res
import "github.com/gin-gonic/gin"
type Response struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data any `json:"data"`
}
var codeMap = map[int]string{
1001: "权限错误",
1002: "角色错误",
}
func response(c *gin.Context, code int, data any, msg string) {
c.JSON(200, Response{
Code: code,
Data: data,
Msg: msg,
})
}
func Ok(c *gin.Context, data any, msg string) {
response(c, 0, data, msg)
}
func OkWithMsg(c *gin.Context, msg string) {
Ok(c, gin.H{}, msg)
}
func OkWithData(c *gin.Context, data any) {
Ok(c, data, "成功")
}
func Fail(c *gin.Context, code int, data any, msg string) {
response(c, code, data, msg)
}
func FailWithMsg(c *gin.Context, msg string) {
response(c, 1001, nil, msg)
}
func FailWithCode(c *gin.Context, code int) {
msg, ok := codeMap[code]
if !ok {
msg = "服务错误"
}
response(c, code, nil, msg)
}
1.json.go
go
package main
import (
"gin_study/study_response/res"
"github.com/gin-gonic/gin"
)
func index(c *gin.Context) {
c.JSON(200, gin.H{"code": 0, "meg": "成功", "data": gin.H{}})
}
func main() {
r := gin.Default()
r.GET("/login", func(c *gin.Context) {
res.OkWithMsg(c, "登录成功")
})
r.GET("/users", func(c *gin.Context) {
res.OkWithData(c, map[string]any{
"name": 1,
})
})
r.POST("/users", func(c *gin.Context) {
res.FailWithMsg(c, "参数错误")
})
r.Run(":8080")
}
响应html
html.go
go
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.LoadHTMLGlob("study_response/templates/*") //加载所有
//r.LoadHTMLFiles("study_response/templates/index.html") //加载单个
r.GET("/", func(c *gin.Context) {
c.HTML(200, "index.html", map[string]any{
"Title": "1111111",
})
})
r.Run(":8080")
}
index.html
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{.Title}}</title>
</head>
<body>
00000000000
</body>
</html>
响应文件
go
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("", func(c *gin.Context) {
c.Header("Content-Type", "application/octet-stream")//表示是文件流,唤起浏览器下载,一般设置了这个,就要设置文件名
c.Header("Content-Disposition", "attachment; filename=3.file.go") // 用来指定下载下来的文件名
fmt.Println("XXX")
c.File("study_response/3.file.go")//文件路径
})
r.Run(":8080")
}
静态文件
go
r.Static("static", "static") // 第一个参数是别名,第二个才是实际路径
r.StaticFile("abcd", "static/abc.txt")
静态文件的路径不能在被路由使用
go
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.Static("st", "study_response/static") //static为根目录,st为别名
r.StaticFile("abc", "study_response/static/a.txt")
r.Run(":8090")
}
请求
查询参数
127.0.0.1:8085/?name=tom&age=20&key=1111&key=2222
go
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
name := c.Query("name")
age := c.DefaultQuery("age", "25") //不传参数默认值25
keyList := c.QueryArray("key")
fmt.Println(name)
fmt.Println(age)
fmt.Println(keyList)
})
r.Run(":8085")
}
动态参数
go
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("users/:id", func(c *gin.Context) {
userID := c.Param("id")
fmt.Println(userID)
})
r.GET("users/:id/:name", func(c *gin.Context) {
userID := c.Param("id")
userName := c.Param("name")
fmt.Println(userName)
fmt.Println(userID)
})
r.Run(":8086")
}
表单参数
go
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.POST("users", func(c *gin.Context) {
name := c.PostForm("name")
age, ok := c.GetPostForm("age")
fmt.Println(name)
fmt.Println(age, ok)
})
r.Run(":8080")
}
文件上传
go
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"io"
"os"
)
func main() {
r := gin.Default()
r.POST("users", func(c *gin.Context) {
fileHeader, err := c.FormFile("file")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(fileHeader.Filename)
fmt.Println(fileHeader.Size)
file, _ := fileHeader.Open()
byteDate, _ := io.ReadAll(file)
err = os.WriteFile("1.jpg", byteDate, 0666)
fmt.Println(err)
})
r.Run(":8080")
}
简单方式
go
err = c.SaveUploadedFile(fileHeader, "uploads/xxx/yyy/"+fileHeader.Filename)
fmt.Println(err)
多文件上传
go
r.POST("users", func(c *gin.Context) {
form, err := c.MultipartForm()
if err != nil {
fmt.Println(err)
return
}
for _, headers := range form.File {
for _, header := range headers {
c.SaveUploadedFile(header, "uploads/"+header.Filename)
}
}
})
原始内容
解决body阅后即焚问题
go
package main
import (
"bytes"
"fmt"
"github.com/gin-gonic/gin"
"io"
)
func main() {
r := gin.Default()
r.POST("", func(c *gin.Context) {
byteData, _ := io.ReadAll(c.Request.Body)
fmt.Println(string(byteData))
//读了之后body就没了,阅后即焚
//添加
c.Request.Body = io.NopCloser(bytes.NewReader(byteData))
name := c.PostForm("name")
fmt.Println(name)
})
r.Run(":8080")
}
form-data
go
----------------------------853882779395683818968400
Content-Disposition: form-data; name="name"
枫枫
----------------------------853882779395683818968400
Content-Disposition: form-data; name="age"
1234
----------------------------853882779395683818968400--
对应的分隔符为
go
Content-Type:[multipart/form-data; boundary=--------------------------052455317193517003536866]
x-www-form-urlencoded
url编码
name=%E6%9E%AB%E6%9E%AB&age=1234
json
go
{
"name": "枫枫",
"age": 23
}
bind绑定器
查询参数
go
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
type user struct {
Name string `form:"name"`
Age int `form:"age"`
}
var u user
err := c.ShouldBindQuery(&u)
if err != nil {
fmt.Println(err)
}
fmt.Println(u)
})
r.Run(":8080")
}
路径参数
http://localhost:8080/users/1/www
go
type user struct {
Name string `uri:"name"`
Id int `uri:"id"`
}
var u user
err := c.ShouldBindUri(&u)
fmt.Println(err, u)
})
表单参数
go
//表单参数
r.POST("form", func(c *gin.Context) {
type user struct {
Name string `form:"name"`
Age int `form:"age"`
}
var u user
err := c.ShouldBind(&u)
fmt.Println(u, err)
})
json
go
r.POST("json", func(c *gin.Context) {
type user struct {
Name string `json:"name"`
Age int `json:"age"`
}
var u user
err := c.ShouldBindJSON(&u)
fmt.Println(u, err)
})
header参数
go
r.POST("header", func(c *gin.Context) {
type user struct {
Name string `header:"Name"`
Age int `header:"Age"`
ContentType string `header:"Content-Type"`
}
var u user
err := c.ShouldBindHeader(&u)
fmt.Println(u, err)
})
binding内置规则
go
// 不能为空,并且不能没有这个字段
required: 必填字段,如:binding:"required"
// 针对字符串的长度
min 最小长度,如:binding:"min=5"
max 最大长度,如:binding:"max=10"
len 长度,如:binding:"len=6"
// 针对数字的大小
eq 等于,如:binding:"eq=3"
ne 不等于,如:binding:"ne=12"
gt 大于,如:binding:"gt=10"
gte 大于等于,如:binding:"gte=10"
lt 小于,如:binding:"lt=10"
lte 小于等于,如:binding:"lte=10"
// 针对同级字段的
eqfield 等于其他字段的值,如:PassWord string `binding:"eqfield=Password"`
nefield 不等于其他字段的值
- 忽略字段,如:binding:"-" 或者不写
// 枚举 只能是red 或green
oneof=red green
// 字符串
contains=fengfeng // 包含fengfeng的字符串
excludes // 不包含
startswith // 字符串前缀
endswith // 字符串后缀
// 数组
dive // dive后面的验证就是针对数组中的每一个元素
// 网络验证
ip
ipv4
ipv6
uri
url
// uri 在于I(Identifier)是统一资源标示符,可以唯一标识一个资源。
// url 在于Locater,是统一资源定位符,提供找到该资源的确切路径
// 日期验证 1月2号下午3点4分5秒在2006年
datetime=2006-01-02
5. gin中间件和路由
路由
路由(api)分组
go
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
apiGroup := r.Group("api")
UserGroup(apiGroup)
r.Run(":8080")
}
func UserGroup(r *gin.RouterGroup) {
r.GET("users", UserView)
r.POST("users", UserView)
r.PUT("users", UserView)
r.PATCH("users", UserView)
r.DELETE("users", UserView)
}
func UserView(c *gin.Context) {
path := c.Request.URL
fmt.Println(c.Request.Method)
fmt.Println(path)
}
RESTFul Api规范
尽量使用名词复数来定义
go
// 在没有resetful规范正确,表示创建用户,删除用户
/api/user_create
/api/users/create
/api/users/add
/api/add_user
/api/user/delete
/api/user_remove
// 使用resetful规范
GET /api/users 用户列表
POST /api/users 创建用户
PUT /api/users/:id 更新用户信息
DELETE /api/users 批量删除用户
DELETE /api/users/:id 删除单个用户
中间件

局部中间件
直接用于单个路由
go
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func Home(c *gin.Context) {
fmt.Println("Home")
c.String(200, "Home")
}
func M1(c *gin.Context) {
fmt.Println("M1 请求部分")
c.Next()
fmt.Println("M1 响应部分")
}
func M2(c *gin.Context) {
fmt.Println("M2 请求部分")
c.Abort()
fmt.Println("M2 响应部分")
}
func main() {
r := gin.Default()
r.GET("", M1, M2, Home)
r.Run(":8080")
}
全局中间件
全局也就是路由组。这也就是给路由分组的意义
go
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func Home(c *gin.Context) {
fmt.Println("Home")
c.String(200, "Home")
}
func M1(c *gin.Context) {
fmt.Println("M1 请求部分")
c.Next()
fmt.Println("M1 响应部分")
}
func M2(c *gin.Context) {
fmt.Println("M2 请求部分")
c.Abort()
fmt.Println("M2 响应部分")
}
func GM1(c *gin.Context) {
fmt.Println("GM1 请求部分")
c.Next()
fmt.Println("GM2 响应部分")
}
func GM2(c *gin.Context) {
fmt.Println("GM2 请求部分")
c.Next()
fmt.Println("GM2 响应部分")
}
func main() {
r := gin.Default()
g := r.Group("api")
g.Use(GM1, GM2)
g.GET("users", Home)
r.GET("", M1, M2, Home)
r.Run(":8080")
}
中间件传递参数
go
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
type UserInfo struct {
Name string
}
func Home(c *gin.Context) {
fmt.Println("Home")
userName, ok := c.Get("user")
if ok {
name, ok := userName.(UserInfo)
if ok {
fmt.Println(name)
}
}
fmt.Println(c.Get("GM1"))
fmt.Println(c.Get("GM2"))
c.String(200, "Home")
}
func M1(c *gin.Context) {
fmt.Println("M1 请求部分")
c.Next()
fmt.Println("M1 响应部分")
}
func M2(c *gin.Context) {
fmt.Println("M2 请求部分")
c.Abort()
fmt.Println("M2 响应部分")
}
func GM1(c *gin.Context) {
fmt.Println("GM1 请求部分")
c.Set("GM1", "GM1")
var user = UserInfo{Name: "aaa"}
c.Set("user", user)
c.Next()
fmt.Println("GM2 响应部分")
}
func GM2(c *gin.Context) {
fmt.Println("GM2 请求部分")
c.Set("GM2", "GM2")
c.Next()
fmt.Println("GM2 响应部分")
}
func main() {
r := gin.Default()
g := r.Group("api")
g.Use(GM1, GM2)
g.GET("users", Home)
r.GET("", M1, M2, Home)
r.Run(":8080")
}