第一个简单gin程序
func hello(c *gin.Context) {
c.String(200, "love")
c.JSON(200, gin.H{
"age": "20",
"name": "zhy",
})
}
func main() {
r := gin.Default()
r.GET("/hello", hello)
r.Run()
}
2 登录页面
**login.html
<body>
<form action="/login" METHOD="post">
用户名:<input TYPE="text" name="username"><br>
密 码:<INPUT TYPE="password" name="password"><br>
<input TYPE="submit" VALUE="登录">
</form>
</body>
welcome.html**
<body>
欢迎{{.username}} <br>
请记住你的密码{{.password}}
</body>
main.go
func main() {
r := gin.Default()
r.LoadHTMLGlob("templates/*")
r.GET("/login", func(context *gin.Context) {
context.HTML(200, "login.html", gin.H{})
})
r.POST("/login", func(context *gin.Context) {
username := context.PostForm("username")
password := context.PostForm("password")
context.HTML(200, "welcome.html", gin.H{
"username": username,
"password": password,
})
})
r.Run()
}
-------------------------------------------------------------------
问答:虽然二者路由都是 /login 但因为一个是get方法,一个是 post 方法,所以没有影响。
执行顺序 先访问 GET方法里的/login 加载 login.html文件,因为此文件中的表单操作 跳转是发出post请求给/login ,因此 就会执行post方法里的welcome.html
3. GET请求参数
func main() {
engine := gin.Default()
engine.GET("/params", func(context *gin.Context) {
//获取url中名为username的值
username := context.Query("username")
//获取url中名为password的值 123为默认值 如果没有密码 默认输出123
password := context.DefaultQuery("password", "123")
context.JSON(200, gin.H{
"username": username,
"password": password,
})
})
engine.Run()
}
访问 http://localhost:8080/params?username=zhy&password=456
4. 路径参数 restful风格 c.Param(“key”)
http://localhost:8080/testPath/zhy/22/大三
//http://localhost:8080/testPath/zhy/22/大三
engine.GET("/testPath/:name/:age/:grade", func(context *gin.Context) {
param := context.Param("name")
s := context.Param("age")
s2 := context.Param("grade")
context.JSON(200, gin.H{
"param": param,
"age": s,
"grade": s2,
})
})
5. 既有GET也有POST
query.html
<body>
<form action="/search?page=5" method="post">
请输入查询关键字:<input type="text" name="key" >
<input type="submit" value="查询">
</form>
</body>
main.go
func main() {
engine := gin.Default()
engine.LoadHTMLGlob("templates/*")
engine.GET("/gosearch", func(context *gin.Context) {
context.HTML(200, "query.html", nil)
})
engine.POST("/search", func(context *gin.Context) {
query := context.DefaultQuery("page", "0")
form := context.PostForm("key")
context.JSON(200, gin.H{
"page": query,
"key": form,
})
})
engine.Run()
}
**-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
6. form表单处理
test_form.html
<body>
<h1>用户注册页面</h1>
<form action="/register" method="post">
用户名:<input type="text" name="username" ><br>
密 码:<input type="password" name="password" ><br>
爱好:
<!-- checkbook 复选框-->
游泳<input type="checkbox" name="hobby" value="swimming">
篮球<input type="checkbox" name="hobby" value="basketball">
足球<input type="checkbox" name="hobby" value="football">
<br>
性别:
<!-- radio 单选按钮-->
男<input type="radio" name="gender" id="1" value="male">
女<input type="radio" name="gender" id="2" value="female">
<br>
<!-- 下拉列表-->
城市:<select name="city" >
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="xianyang">咸阳</option>
</select>
<br>
<input type="submit" value="注册">
</form>
</body>
main.go
func main() {
engine := gin.Default()
engine.LoadHTMLGlob("templates/*")
engine.GET("/goregister", func(context *gin.Context) {
context.HTML(200, "test_form.html", nil)
})
engine.POST("/register", func(context *gin.Context) {
username := context.PostForm("username")
password := context.PostForm("password")
hobby := context.PostFormArray("hobby") //这个需要注意一下 和其他不一样
gender := context.PostForm("gender")
city := context.PostForm("city")
context.JSON(200, gin.H{
"username": username,
"password": password,
"hobby": hobby,
"gender": gender,
"city": city,
})
})
engine.Run()
}
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
7. Gin数据绑定
7.1 绑定Form表单
test_fom 同上
main.go
type User struct {
//对应表单中的username
Username string `form:"username"`
Password string `form:"password"`
Hobby []string `form:"hobby"`
Gender string `form:"gender"`
City string `form:"city"`
}
func main() {
engine := gin.Default()
engine.LoadHTMLGlob("templates/*")
engine.GET("/register", func(context *gin.Context) {
context.HTML(200, "test_form.html", nil)
})
engine.POST("/register", func(c *gin.Context) {
var u User
//将post过来的数据绑定到结构体
c.ShouldBind(&u)
c.String(200, "form data is %s", u)
})
engine.Run()
}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
8. 绑定查询参数
main.go
type Admin struct {
//对应表单中的username
Username string `form:"username"`
Password string `form:"password"`
}
func main() {
engine := gin.Default()
engine.LoadHTMLGlob("templates/*")
//http://localhost:8080/testGetBind?username=ZHY&password=123465
engine.GET("/testGetBind", func(context *gin.Context) {
var a Admin
err := context.ShouldBind(&a)
if err != nil {
log.Fatal(err)
}
context.String(200, "admin data is : %s ", a)
})
engine.Run()
}
浏览器输入 :http://localhost:8080/testGetBind?username=ZHY&password=123465
9. 路径请求参数绑定
main.go
type Admin2 struct {
//对应表单中的username uri 对应 url
Username string `uri:"username"`
Password string `uri:"password"`
}
func main() {
engine := gin.Default()
engine.LoadHTMLGlob("templates/*")
//http://localhost:8080/testurlBind/winnie/3.1415926
engine.GET("/testurlBind/:username/:password", func(context *gin.Context) {
var a2 Admin2
//url绑定
err := context.ShouldBindUri(&a2)
if err != nil {
log.Fatal(err)
}
context.String(200, "admin data is : %s ", a2)
})
engine.Run()
}
10. Gin访问静态文件集成BootStrap框架
https://getbootstrap.com/ 下载 框架 css js 文件夹 找这里的Compiled CSS and JS download
test_static.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<!-- 下面两行是加载-->
<link rel="stylesheet" href="/ass/css/bootstrap.min.css">
<script src="/ass/js/bootstrap.min.js"></script>
</head>
<body>
<!--可以去这里找相关组件 https://getbootstrap.com/docs/5.1/components/pagination/-->
<!--可以查看相关css文件。-->
<div class="container">
<div class="btn-group" role="group" aria-label="Basic example">
<button type="button" class="btn btn-primary">Left</button>
<button type="button" class="btn btn-primary">Middle</button>
<button type="button" class="btn btn-primary">Right</button>
</div>
<nav aria-label="Page navigation example">
<ul class="pagination">
<li class="page-item"><a class="page-link" href="#">Previous</a></li>
<li class="page-item"><a class="page-link" href="#">1</a></li>
<li class="page-item"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item"><a class="page-link" href="#">Next</a></li>
</ul>
</nav>
</div>
</body>
</html>
main.go
func main() {
engine := gin.Default()
engine.LoadHTMLGlob("templates/*")
//加载静态文件 第一个参数 相对路径,第二个参数 文件名路径
engine.Static("/ass", "./assets")
engine.GET("/static", func(context *gin.Context) {
context.HTML(200, "test_static.html", nil)
})
engine.Run()
}
11. Gin BasicAuth中间件
gin提供了BasicAuth中间件,用来对网站资源的访问保护
//模拟私人数据
var secrets = gin.H{
"foot": gin.H{"email": "foot@qq.com", "phone": 123456789},
"basket": gin.H{"email": "basket@qq.com", "phone": 987654321},
"Winnie": gin.H{"email": "Winnie@qq.com", "phone": 3.1415926},
}
func Handler(c *gin.Context) {
//获取用户,它是由BasicAuth中间件设置的
//AuthUserKey 是 "user" 的名称
//MustGet 返回 给定键的值
user := c.MustGet(gin.AuthUserKey).(string)
fmt.Println(user) //打印user 拿到的是 登录用户
//如果secrets里的键和user的值对应 那么将该键的值返回给secret
if secret, ok := secrets[user]; ok {
c.JSON(200, gin.H{"user": user, "secret": secret})
} else {
c.JSON(200, gin.H{"user": user, "secret": "NO SECRET:("})
}
}
func main() {
r := gin.Default()
//这些用户数据 可以从数据库查询 gorm
//BasicAuth,它接受 map[string]字符串作为参数,其中键是用户名,值是密码。
//type Accounts map[string]string Accounts为授权登录的用户/传递列表定义一个键/值
group := r.Group("/admin")
group.Use(gin.BasicAuth(gin.Accounts{ //路由分组中配置中间件
//登陆的用户名,密码。用户名对应 secrets里的键名
"foot": "bar",
"austin": "1234",
"basket": "hello2",
"Winnie": "zining",
}))
{
group.GET("/secret1", Handler)
}
r.Run()
}
http://localhost:8080/admin/secret1
-
我们先拿"austin": "1234"来测试,因为austin不属于secrets里的键名
-
我们拿"Winnie": "zining"来测试,因为Winnie 属于secrets里的键名
11.1 中间件第二种配置方式
func main() {
r := gin.Default()
//这些用户数据 可以从数据库查询 gorm
//BasicAuth,它接受 map[string]字符串作为参数,其中键是用户名,值是密码。
//type Accounts map[string]string Accounts为授权登录的用户/传递列表定义一个键/值
group := r.Group("/admin", gin.BasicAuth(gin.Accounts{ //路由分组中配置中间件
"foot": "bar",
"austin": "1234",
"basket": "hello2",
"Winnie": "zining",
}))
{
group.GET("/secret1", Handler)
}
r.Run()
}
12. Gin cookie 使用
cookie是服务器向客户端写的一些数据,可以实现像自动登录等功能。
基于安全考虑,需要给cookie加上Secure和HttpOnly属性,HttpOnly比较好理解,设置httponly=true
的cookie不能被js获取到,无法用document.cookie
打出cookie内容,
Secure属性是说如果一个cookie被设置了secure=true
,那么这个cookie只能用https协议发送给服务器,用HTTP协议是不发送的。
func handler(c *gin.Context) {
//Cookie返回请求中提供的命名Cookie
s, err := c.Cookie("username")
if err != nil {
s = "zhy"
c.SetCookie("username", s, 60*60, "/", "localhost", false, true)
}
c.String(200, "测试cookie")
}
func main() {
r := gin.Default()
r.GET("/testcookie", handler)
r.Run()
}
13. Gin使用Session
因为http是无状态、短链接,如何保存客户端和服务器直接的会话状态呢,可以用session
使用gin session中间件
import (
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
)
func handler(c *gin.Context) {
// 初始化session对象
session := sessions.Default(c)
//获得session值 看是否 "我是" 的值为 "Winnie"
if session.Get("我是") != "Winnie" {
//设置
session.Set("我是", "Winnie")
//保存
session.Save()
}
c.JSON(200, gin.H{"我是": session.Get("我是")})
}
func main() {
r := gin.Default()
// 初始化一个存储引擎,参数为密钥,用于加解密
// 创建基于cookie的存储引擎,secret 参数是用于加密的密钥
store := cookie.NewStore([]byte("secret"))
// 设置Sessions中间件,参数 mysession,指的是 session 的名字,也是cookie的名字
// store是前面创建的存储引擎,我们可以替换成其他存储引擎
r.Use(sessions.Sessions("mysession", store))
r.GET("/session", handler)
r.Run()
}
14. Gin实现restful风格的CRUD
CRUD说的就是增查改删
type User struct {
Uid int `json:"uid"`
Name string `json:"name"`
Age int `json:"age"`
}
var users = make([]User, 3)
func init() {
u1 := User{1, "张三", 20}
u2 := User{2, "五四", 21}
u3 := User{3, "王麻子", 22}
users = append(users, u1)
users = append(users, u2)
users = append(users, u3)
fmt.Println(users)
}
//根据ID查找用户
func Find(uid int) (*User, int) {
for index, value := range users {
if value.Uid == uid {
return &value, index
}
}
return nil, -1
}
//根据 url中uid位置的值 查找用户
func FindUser(c *gin.Context) {
param := c.Param("uid")
atoi, _ := strconv.Atoi(param)
find, i := Find(atoi)
c.JSON(200, gin.H{
"USER": find,
"INDEDX": i,
})
}
//增添
func AddUser(c *gin.Context) {
u4 := User{4, "Winnie", 23}
users = append(users, u4)
c.JSON(200, users)
}
//删除
func DelUser(c *gin.Context) {
//获取 url中 uid位置 的值
param := c.Param("uid")
//被转换为int类型。 查找 id
id, _ := strconv.Atoi(param)
// 找到id对应的在users中的 下标
_, i := Find(id)
//对当前位置进行覆盖
// 设有一切片值为 users[0 1 2 3 4 5 6 7] i=4
//users[:4]=[0 1 2 3] 因为 s[:high] 从切片s的索引位置0到high处所得切片 len=high
//users[i+1:]=users[5:]=[5 6 7]
//append(users[:i], users[i+1:]...) 意思为 将users[5:]增添至users[:4]后面 从而覆盖掉了users[4]的值 从而达到删除作用
users = append(users[:i], users[i+1:]...)
c.JSON(200, users)
}
//修改
func UpdateUser(c *gin.Context) {
uid := c.Param("uid")
id, _ := strconv.Atoi(uid)
user, index := Find(id)
user.Name = "123"
users[index] = *(user)
c.JSON(200, users)
}
func main() {
engine := gin.Default()
engine.GET("/user/:uid", FindUser)
engine.PUT("/user/:uid", UpdateUser)
engine.DELETE("/user/:uid", DelUser)
engine.POST("/user/", AddUser)
engine.Run()
}
15. 文件上传
test_upload.html
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
请你选择上传的文件:<input type="file" name="file">
<br>
<input type="submit" value="提交上传">
</form>
</body>
func upload(c *gin.Context) {
//单文件
//获取上传的文件 , "file" 对应 html里的 "file"
file, _ := c.FormFile("file")
log.Println(file.Filename) //file.Filename表示获取文件名称
//上传文件到 项目根目录,使用原文件名
//没有设置路径的话 默认上传至 根目录
c.SaveUploadedFile(file, file.Filename)
c.String(200, fmt.Sprintf("%s upload!", file.Filename))
}
func doupload(c *gin.Context) {
c.HTML(200, "test_upload.html", nil)
}
func main() {
engine := gin.Default()
//限制处理该文件所占用的最大内存
engine.MaxMultipartMemory = 8 << 20 //8 MiB
engine.LoadHTMLGlob("templates/*")
engine.GET("/upload", doupload)
engine.POST("/upload", upload)
engine.Run()
}