• 03 . Go框架之Gin框架从入门到熟悉(Cookie和Session,数据库操作)


    Cookie是什么

    HTTP是无状态协议,服务器不能记录浏览器的访问状态,也就是说服务器不能区分两次请求是否由同一个客户端发出

    Cookie就是解决HTTP协议无状态的方案之一,中文是小甜饼的意思

    Cookie实际上就是服务器保存在浏览器上的一段信息。浏览器有了Cookie之后,每次向服务器发送请求时都会同时将该信息发送给服务器,服务器收到请求后,就可以根据该信息处理请求

    Cookie由服务器创建,并发送给浏览器,最终由浏览器保存

    Cookie的用途

    保持用户登录状态

    Cookie的缺点
    /*
    		不安全,明文
    		增加带宽消耗
    		可以被禁用
    		Cookie有上限
    */
    
    Cookie的使用

    测试服务器发送cookie给客户端,客户端请求时携带cookie

    package main
    
    import (
    	"fmt"
    	"github.com/gin-gonic/gin"
    )
    
    func main() {
    	// 1.创建路由
    	// 默认使用了2个中间件Logger(), Recovery()
    	r := gin.Default()
    
    	// 服务端要给客户端
    	r.GET("cookie", func(c *gin.Context) {
    		// 获取客户端是否携带cookie
    		cookie,err := c.Cookie("key_cookie")
    		if err != nil {
    			cookie = "NotSet"
    			// 给客户端设置cookie
    			// maxAge int, 单位 s
    			// path cookie  所在目录
    			// domain string  域名
    			// secure  是否只能通过https访问
    			// httponly bool  是否允许别人通过js获取自己的cookie
    			c.SetCookie("key_cookie","value_cookie",60,"/","localhost",false,true)
    		}
    		fmt.Printf("cookie的值是: %s
    ",cookie)
    	})
    	r.Run()
    }
    

    模拟实现权限验证中间件

    package main
    
    import (
    	"github.com/gin-gonic/gin"
    	"net/http"
    )
    
    func AuthMiddleWare() gin.HandlerFunc {
    	return func(c *gin.Context) {
    		if cookie, err := c.Cookie("abc"); err == nil {
    			if cookie == "123" {
    				c.Next()
    				return
    			}
    		}
    		// 返回错误
    		c.JSON(http.StatusUnauthorized,gin.H{"error":"err"})
    		c.Abort()
    		return
    	}
    }
    
    func main() {
    	// 1.创建路由
    	// 默认使用了2个中间件Logger(), Recovery()
    	r := gin.Default()
    	r.GET("/login", func(c *gin.Context) {
    		c.SetCookie("abc","123",60,"/","localhost",false,true)
    		c.String(200,"Login success!")
    	})
    
    	r.GET("/home",AuthMiddleWare(), func(c *gin.Context) {
    		c.JSON(200,gin.H{"data":"home"})
    	})
    	r.Run()
    }
    
    

    Session

    Session是什么

    Session可以弥补Cookie的不足, Session必须依赖于Cookie才能使用, 生成一个SessionID放在Cookie里传到客户端就可以.

    Session中间件开发

    设计一个通用的Session服务, 支持内存存储和redis存储

    Session模块设计

    /*
    		本质上k-v系统,通过key进行增删改查.
    		Session可以存储在内存或者redis(2个版本)
    */
    

    Session接口设计

    /*
    		Set()
    		Get()
    		Del()
    		Save()  session存储, redis的实现延迟加载
    */
    

    SessionMgr接口设计

    /*
    		Init()  初始化,加载redis地址
    		CreateSession()  创建一个新的session
    		GetSession()   通过SessionID获取对应的Session对象
    */
    

    MemorySession设计

    /*
    		定义MemorySession对象 (字段:  SessionID, 存kv的map,读写锁)
    		构造函数,为了获取对象
    		Set()
    		Get()
    		Del()
    		Save()
    */
    

    MemorySessionMgr设计

    /*
    		定义MemorySessionMgr对象(字段: 存放所有session的map, 读写锁)
    		构造函数
    		Init()
    		CreateSession()
    		GetSession()
    */
    

    RedisSession设计

    /*
    		定义RedisSession对象(字段: sessionID,存kv的map, 读写锁, redis连接池, 记录内存中map是否被修改的标记)
    		
    		构造函数
    		
    		Set(): 将session存到内存中的map
    		Get(): 取数据,实现延迟加载
    		Del()
    		Save():  将session存到redis
    */
    

    RedisSessionMgr设计

    /*
    		定义RedisSessionMgr对象(字段: redis地址,redis密码, 连接池,读写锁, 大map)
    		构造函数
    		Init()
    		CreateeSession()
    		GetSession()
    */
    

    session.go

    
    

    数据库操作

    sql
    CREATE TABLE `book` (
      `id` int(50) NOT NULL AUTO_INCREMENT,
      `title` varchar(50) DEFAULT NULL,
      `price` int(50) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
    
    结构
     tree 
    .
    ├── book
    ├── db
    │   └── db.go
    ├── go.mod
    ├── go.sum
    ├── main.go
    ├── model
    │   └── book.go
    └── templates
        ├── book_list.html
        └── new_book.html
    
    db

    db.go

    // 查询单条数据示例
    package db
    
    import (
    	"database_test1/model"
    	"fmt"
    	_ "github.com/go-sql-driver/mysql" // init()
    	"github.com/jmoiron/sqlx"
    )
    
    // Go连接MySQL示例
    
    var db *sqlx.DB // 是一个连接池对象
    
    func InitDB() (err error) {
    	// 数据库信息
    	// 用户名:密码@tcp(ip:端口)/数据库的名字
    	dsn := "test:ZHOUjian.22@tcp(121.36.43.223:3306)/book?charset=utf8"
    
    	// 连接数据库
    	db, err = sqlx.Connect("mysql", dsn)
    	if err != nil {
    		return
    	}
    	db.SetMaxOpenConns(10) // 设置数据库连接池的最大连接数
    	db.SetMaxIdleConns(5)  // 设置最大空闲连接数
    	return
    }
    
    
    func QueryAllBook() (bookList []*model.Book,err error)  {
    	sqlStr := "select id,title,price from book"
    	err = db.Select(&bookList,sqlStr)
    	if err != nil {
    		fmt.Println("查询失败")
    		return
    	}
    	return
    }
    
    func InsertBook(title string,price int64) (err error)  {
    	sqlStr := "insert into book(title,price) values(?,?)"
    	_, err = db.Exec(sqlStr,title,price)
    	if err != nil {
    		fmt.Println("插入失败")
    		return
    	}
    	return
    }
    
    func DeleteBook(id int64) (err error)  {
    	sqlStr := "delete from book where id =?"
    	_,err = db.Exec(sqlStr,id)
    	if err != nil {
    		fmt.Println("删除失败")
    		return
    	}
    	return
    }
    
    model

    book.go

    package model
    
    type Book struct {
    	ID string `db:"id"`
    	Title string  `db:"title"`
    	Price int64 `db:"price"`
    }
    
    template

    book_list.html

    {{ define "book_list.html" }}
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>书籍列表</title>
    </head>
    <body>
        <div><a href="/book/new">添加新书</a></div>
        <table border="1">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>title</th>
                    <th>price</th>
                    <th>操作</th>
                </tr>
            </thead>
    
            <tbody>
                {{ range .data}}
                    <tr>
                        <td>{{ .ID }}</td>
                        <td>{{ .Title }}</td>
                        <td>{{ .Price }}</td>
                        <td><a href="/book/delete?id={{ .ID }}">删除</a></td>
                    </tr>
                {{  end }}
    
            </tbody>
    
        </table>
    
    </body>
    </html>
    {{ end }}
    

    new_book.html

    {{ define "new_book.html" }}
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>添加图书信息</title>
    </head>
    <body>
        <form action="/book/new" method="POST">
    
            <div>
                <label>书名:
                    <input type="text" name="title">
                </label>
            </div>
    
            <div>
                <label>价格:
                    <input type="number" name="price">
                </label>
            </div>
    
            <div>
                <input type="submit" value="点我">
            </div>
    
        </form>
    
    </body>
    </html>
    {{ end }}
    
    Main.go
    package main
    
    import (
    	"database_test1/db"
    	"github.com/gin-gonic/gin"
    	"net/http"
    )
    
    func main()  {
    	// 初始化数据库
    	err := db.InitDB()
    	if err != nil {
    		panic(err)
    	}
    
    	r := gin.Default()
    	r.LoadHTMLGlob("./templates/*")
    	// 查询所有图书
    	r.GET("/book/list",bookListHandler)
    	r.Run()
    }
    
    func bookListHandler(c *gin.Context)  {
    	bookList ,err := db.QueryAllBook()
    	if err != nil {
    		c.JSON(http.StatusOK,gin.H{
    			"code":1,
    			"msg":err,
    		})
    		return
    	}
    
    	// 返回数据
    	c.HTML(http.StatusOK,"book_list.html",gin.H{
    		"code":200,
    		"data":bookList,
    	})
    }
    
  • 相关阅读:
    获取设备和 App 信息
    使用 UICollectionView 实现网格化视图效果
    UIImage 读取图片内存优化
    使用 stretchableImageWithLeftCapWidth 方法实现可伸缩图片
    使用 StoryBoard 实现左右按钮切换图片的浏览效果
    二维码图片生成(扩展知识:创建带圆角效果的图片)
    Objective-C语法之扩展(Extension)的使用
    Objective-C语法之字符串NSString去掉前后空格或回车符(可以是NSCharacterSet类型的其它字符)
    Objective-C语法之可变参数
    什么时候layoutSubview会被调用
  • 原文地址:https://www.cnblogs.com/you-men/p/13906843.html
Copyright © 2020-2023  润新知