gin框架学习合集

前言知识

  • gin.Default() 创建一个带有默认中间件的路由引擎

  • GET方法

    • GET(路由地址,函数)
    • 函数的参数表示一个HTTP 请求的上下文,包括请求和响应

ps: POST方法类似

  • Run方法

    • 执行服务程序,参数为【地址:端口】,如:“127.0.0.1:9090”
  • http.StatusOK 表示 状态码200,其他类似

get 请求

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()
	r.GET("/get", getMsg)
	r.Run(":9090")
}

func getMsg(context *gin.Context) {
	// 获取传入的 name
	name := context.Query("name")
	// 返回 字符串 格式
	context.String(http.StatusOK, "欢迎您: %s", name)
	// 返回 json 格式
	context.JSON(http.StatusOK, gin.H{
		"code": http.StatusOK,
		"msg":  "返回信息",
		"data": "欢迎您:" + name,
	})
}

在这里插入图片描述

post 请求

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()
	r.POST("/post", postMsg)
	r.Run(":9090")
}

func postMsg(context *gin.Context) {
	name := context.DefaultPostForm("name", "Gin")
	fmt.Println(name)
	form, b := context.GetPostForm("name")
	fmt.Println(form, b)
	context.JSON(http.StatusOK, "欢迎您:"+name)
}

在这里插入图片描述

重定向

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()
	// 外部链接
	r.GET("/redirect1", func(context *gin.Context) {
		url := "http://www.baidu.com"
		context.Redirect(http.StatusMovedPermanently, url)
	})
	
	// 自定义重定向
	r.GET("/redirect2", func(context *gin.Context) {
		context.Request.URL.Path = "/testRedirect"
		r.HandleContext(context)
	})
	
	// 自定义链接
	r.GET("/testRedirect", func(context *gin.Context) {
		context.JSON(http.StatusOK, gin.H{
			"code": http.StatusOK,
			"msg":  "响应成功",
		})
	})
	
	r.Run(":9090")
}

在这里插入图片描述

在这里插入图片描述

获取第三方数据

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()
	r.GET("/getOtherData", func(context *gin.Context) {
		url := "https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png"
		// 使用http.Get方法向指定的URL发送一个GET请求,并接收响应
		res, err := http.Get(url)
		// 检查是否在请求过程中发生错误,或者响应的状态码是否不是200(OK)
		if err != nil || res.StatusCode != http.StatusOK {
			context.Status(http.StatusServiceUnavailable)
			return
		}
		// 请求体内容
		body := res.Body
		// 请求体长度
		contentLen := res.ContentLength
		// 请求媒体类型
		contentType := res.Header.Get("Content-Type")
		// 将获取到的数据以及相关信息发送给客户端
		context.DataFromReader(http.StatusOK, contentLen, contentType, body, nil)
	})

	r.Run(":9090")
}

在这里插入图片描述

多形式渲染

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()
	r.GET("/json", func(context *gin.Context) {
		context.JSON(http.StatusOK, gin.H{
			"html": "<b>hello</b>",
		})
	})

	r.GET("/html", func(context *gin.Context) {
		context.PureJSON(http.StatusOK, gin.H{
			"html": "<b>hello</b>",
		})
	})

	r.GET("/xml", func(context *gin.Context) {
		type Message struct {
			Name string
			Msg  string
			Age  int
		}
		info := Message{}
		info.Name = "FAN"
		info.Msg = "hello"
		info.Age = 23
		context.XML(http.StatusOK, info)
	})

	r.GET("yaml", func(context *gin.Context) {
		context.YAML(http.StatusOK, gin.H{
			"message": "hello",
			"status":  200,
		})
	})
	r.Run(":9090")
}

在这里插入图片描述

文件服务器

package main

import "github.com/gin-gonic/gin"

func main() {
	r := gin.Default()
	r.GET("/file", fileServer)

	r.Run(":9090")
}

func fileServer(context *gin.Context) {
	// 存储路径
	path := "D:/2-golang/img/"
	// 文件名
	fileName := path + context.Query("name")
	context.File(fileName)
}

在这里插入图片描述

单文件上传

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()
	r.POST("/upload", func(context *gin.Context) {
		file, err := context.FormFile("fileName")
		if err != nil {
			context.String(http.StatusBadRequest, "文件上传错误!")
		}
		dst := "D:/2-golang/img/"
		context.SaveUploadedFile(file, dst+file.Filename)
		context.String(http.StatusOK, fmt.Sprintf("%s 上传成功", file.Filename))
	})

	r.Run(":9090")
}

在这里插入图片描述

在这里插入图片描述

多文件上传

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()
	r.POST("/allUpload", fileServe)
	r.Run(":9090")
}

func fileServe(context *gin.Context) {
	form, err := context.MultipartForm()
	if err != nil {
		context.String(http.StatusBadRequest, "上传文件错误!")
	}
	// 多文件上传原理是一样的,只是多了个循环来保存文件
	dst := "D:/2-golang/img/"
	files := form.File["file_key"]
	for _, file := range files {
		context.SaveUploadedFile(file, dst+file.Filename)
	}
	context.String(http.StatusOK, fmt.Sprintf("%d 个文件上传成功!", len(files)))
}

自定义中间件

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"strconv"
)

func main() {
	//r := gin.New() //没有任何中间件的路由引擎
	r := gin.Default() //默认路由引擎:Logger and Recovery middleware
	r.Use(Middleware()) //先执行中间件,相当于过滤,减少不必要的服务器性能损耗
	r.GET("/middleware", func(context *gin.Context) {
		fmt.Println("服务端开始执行...")
		name := context.Query("name")
		ageStr := context.Query("age")
		age, _ := strconv.Atoi(ageStr)
		res := struct {
			Name string `json:"name"` //添加标签
			Age  int    `json:"age"`
		}{name, age}
		context.JSON(http.StatusOK, res)
	})
	r.Run(":9090")
}

func Middleware() gin.HandlerFunc {
	return func(context *gin.Context) {
		fmt.Println("中间件开始执行...")
		name := context.Query("name")
		ageStr := context.Query("age")
		age, err := strconv.Atoi(ageStr)
		//失败拦截
		if err != nil {
			context.AbortWithStatusJSON(http.StatusBadRequest, "输入格式错误,年龄不是整数")
		}
		if len(name) < 6 || len(name) > 12 {
			context.AbortWithStatusJSON(http.StatusBadRequest, "用户名只能是6-12位")
		}
		if age < 0 || age > 100 {
			context.AbortWithStatusJSON(http.StatusBadRequest, "年龄非法")
		}
		//成功进入服务端
		context.Next()
	}
}

在这里插入图片描述

登录中间件

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()
	r.Use(AuthMiddleware())
	r.GET("/login", func(context *gin.Context) {
		user := context.MustGet(gin.AuthUserKey).(string)
		context.JSON(http.StatusOK, "登录成功"+"欢迎您:"+user)
	})
	r.Run(":9090")
}

func AuthMiddleware() gin.HandlerFunc {
	// 静态添加
	account := gin.Accounts{
		"admin":  "adminpw",
		"system": "systempw",
	}
	// 动态添加
	account["go"] = "go123"
	account["gin"] = "gin123"
	return gin.BasicAuth(account)
}

在这里插入图片描述

同步异步

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"strconv"
	"time"
)

func main() {
	r := gin.Default()

	r.GET("/sync", func(context *gin.Context) {
		sync(context)
		context.JSON(http.StatusOK, ">>>主程序同步已经执行<<<")
	})

	r.GET("/async", func(context *gin.Context) {
		for i := 0; i < 6; i++ {
			cCp := context.Copy()
			go async(cCp, i) // 异步通过 go程 实现
		}
		context.JSON(http.StatusOK, ">>>主程序同步已经执行<<<")
	})

	r.Run(":9090")
}

func async(cp *gin.Context, i int) {
	fmt.Println("第" + strconv.Itoa(i) + "个 go 程序开始执行:" + cp.Request.URL.Path)
	time.Sleep(time.Second * 3)
	println("第" + strconv.Itoa(i) + "个同步任务执行完成")
}

func sync(context *gin.Context) {
	println("开始执行同步任务:" + context.Request.URL.Path)
	time.Sleep(time.Second * 3)
	println("同步任务执行完成")
}

在这里插入图片描述

在这里插入图片描述

多服务器程序运行

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"golang.org/x/sync/errgroup"
	"net/http"
	"time"
)

var g errgroup.Group // 使用 errgroup.Group 来并发启动这两个服务器

func main() {
	// 服务器1
	server01 := &http.Server{
		Addr:         ":9090", 
		Handler:      router01(),
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
	}
	// 服务器2
	server02 := &http.Server{
		Addr:         ":9092", // 别问为啥不用 91,问就是被占用了
		Handler:      router02(),
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
	}
	// 分别启动监听
	g.Go(func() error {
		return server01.ListenAndServe()
	})
	g.Go(func() error {
		return server02.ListenAndServe()
	})
	// 通过 g.Wait() 来等待它们的执行结果
	if err := g.Wait(); err != nil {
		fmt.Println("执行失败", err)
	}
}

func router01() http.Handler {
	r1 := gin.Default()
	r1.GET("/MyServer01", func(context *gin.Context) {
		context.JSON(http.StatusOK, gin.H{
			"code": http.StatusOK,
			"msg":  "服务器程序1启动成功",
		})
	})
	return r1
}

func router02() http.Handler {
	r1 := gin.Default()
	r1.GET("/MyServer02", func(context *gin.Context) {
		context.JSON(http.StatusOK, gin.H{
			"code": http.StatusOK,
			"msg":  "服务器程序2启动成功",
		})
	})
	return r1
}

在这里插入图片描述

路由组

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

type ResGroup struct {
	Data string
	Path string
}

func main() {
	router := gin.Default()
	// 路由分组1
	v1 := router.Group("/v1") // /v1
	{
		// 路由分组1.1
		r := v1.Group("/user") // /v1/user
		r.GET("/login", login) // /v1/user/login
		// // 路由分组1.1.1
		r2 := r.Group("showInfo")     // /v1/user/showInfo
		r2.GET("/abstract", abstract) // /v1/user/showInfo/abstract
		r2.GET("/detail", detail)     // /v1/user/showInfo/detail
	}

	// 路由分组2
	v2 := router.Group("/v2") // v2
	{
		v2.GET("/other", other) // /v2/other
	}
	router.Run(":9090")
}

func other(context *gin.Context) {
	context.JSON(http.StatusOK, ResGroup{"detail", context.Request.URL.Path})
}

func detail(context *gin.Context) {
	context.JSON(http.StatusOK, ResGroup{"detail", context.Request.URL.Path})
}

func abstract(context *gin.Context) {
	context.JSON(http.StatusOK, ResGroup{"abstract", context.Request.URL.Path})
}

func login(context *gin.Context) {
	context.JSON(http.StatusOK, ResGroup{"login", context.Request.URL.Path})
}

在这里插入图片描述

路由结构

  • v1
    • user
      • login
      • showInfo
        • abstract
        • detail
  • v2
    • other

gin框架 bind

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

type Login struct {
	UserName string `json:"user_name" binding:"required"` // 后端字段 UserName → 前端参数 user_name,必需
	Password string `json:"password" binding:"required"`
	Remark   string `json:"remark" binding:"required"`
}

func main() {
	r := gin.Default()
	r.POST("/login", func(context *gin.Context) {
		var login Login
		// Bind的执行相当于:context从客户端接收数据,跟绑定的结构体进行比较
		// 比较时,根据结构体编写的 json 名称、规则进行校验
		err := context.Bind(&login) // 执行绑定,注意参数是地址(引用)
		if err != nil {
			context.JSON(http.StatusBadRequest, gin.H{
				"msg":  "绑定失败,参数错误",
				"data": err.Error(),
			})
			return
		}
		if login.UserName == "user" && login.Password == "123456" {
			context.JSON(http.StatusBadRequest, gin.H{
				"msg":  "登陆成功",
				"data": "OK",
			})
			return
		}
		context.JSON(http.StatusBadRequest, gin.H{
			"msg":  "登陆失败",
			"data": "error",
		})
		return
	})
	r.Run(":9090")
}

在这里插入图片描述

调用 restful 接口

ps:将上一模块的代码打包成exe文件,并执行起来,注意端口冲突问题(命令:go build 文件名)

在这里插入图片描述

  • 逻辑顺序:
    • 客户端(真实) → 服务端
    • 服务端(模拟客户端) →第三方接口
    • 第三方接口→服务端(模拟客户端)
    • 服务端→客户端(真实)
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"io/ioutil"
	"net/http"
	"time"
)

// 第三方接口字段
type UserAPI struct {
	UserName string `json:"user_name"`
	Password string `json:"password"`
	Remark   string `json:"remark"`
}

// 第三方接口返回字段封装
type TempData struct {
	Msg  string `json:"msg"`
	Data string `json:"data"`
}

// 客户端请求字段
type ClientRequest struct {
	UserName string      `json:"user_name"`
	Password string      `json:"password"`
	Remark   string      `json:"remark"`
	Other    interface{} `json:"other"`
}

// 服务端返回字段
type ClientResponse struct {
	Code int         `json:"code"`
	Msg  string      `json:"msg"`
	Data interface{} `json:"data"`
}

func main() {
	//testAPI()
	r := gin.Default()
	r.POST("/getOtherAPI", getOtherAPI)
	r.Run(":9092")
}

func getOtherAPI(context *gin.Context) {
	var requestData ClientRequest
	var response ClientResponse
	err := context.Bind(&requestData) // 绑定请求数据
	if err != nil {
		response.Code = http.StatusBadRequest
		response.Msg = "请求的参数错误"
		response.Data = err
		context.JSON(http.StatusBadRequest, response)
		return
	}
	url := "http://127.0.0.1:9090/login"
	user := UserAPI{requestData.UserName, requestData.Password, requestData.Remark}
	data, err := getRestfulAPI(url, user, "application/json")
	var temp TempData
	json.Unmarshal(data, &temp) // 反序列化
	response.Code = http.StatusOK
	response.Msg = "请求数据成功"
	response.Data = temp
	context.JSON(http.StatusOK, response)
}

//func testAPI() {
//	url := "http://127.0.0.1:9090/login"
//	user := UserAPI{"user", "123456", "说明"}
//	data, err := getRestfulAPI(url, user, "application/json")
//	fmt.Println(data, err)
//	var temp TempData
//	json.Unmarshal(data, &temp)
//	fmt.Println(temp)
//}

func getRestfulAPI(url string, data interface{}, contentType string) ([]byte, error) {
	client := &http.Client{Timeout: 5 * time.Second}                     // 创建一个http客户端示例,去请求第三方
	jsonStr, _ := json.Marshal(data)                                     // 序列化数据
	resp, err := client.Post(url, contentType, bytes.NewBuffer(jsonStr)) // 发起post请求
	if err != nil {
		fmt.Println("调用API接口出现了错误")
		return nil, err
	}
	res, err := ioutil.ReadAll(resp.Body)
	return res, err
}

在这里插入图片描述

参数校验(一)

uuid包:https://github.com/satori/go.uuid
因为作者更改了参数限制,导致会出问题 → 问题解决

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	uuid "github.com/gofrs/uuid"
	"net/http"
	"unicode/utf8"
)

type UserInfo struct {
	Id   string `validate:"uuid" json:"id"`           // UUID 类型
	Name string `validate:"checkName" json:"name"`    // 自定义校验
	Age  uint8  `validate:"min=0,max=130" json:"age"` // 大于0小于130
}

var validate *validator.Validate

// 校验初始化
func init() {
	validate = validator.New()                              // 初始化校验示例
	validate.RegisterValidation("checkName", checkNameFunc) // 自定义校验方法
}

func checkNameFunc(fl validator.FieldLevel) bool {
	count := utf8.RuneCountInString(fl.Field().String()) // 获取 Name 的字符串表示,并计算 Unicode 字符的数量
	if count >= 2 && count <= 12 {
		return true
	}
	return false
}

func main() {
	uuid.Must(uuid.NewV4()) // 由 uuid包 生成 uuid

	r := gin.Default()
	var user UserInfo
	r.POST("/validate", func(context *gin.Context) {
		err := context.Bind(&user)
		if err != nil {
			context.JSON(http.StatusBadRequest, "请求参数错误")
			return
		}
		err = validate.Struct(user)
		if err != nil {
			for _, e := range err.(validator.ValidationErrors) {
				fmt.Println("错误的字段:", e.Field())
				fmt.Println("错误的值:", e.Value())
				fmt.Println("错误的tag:", e.Tag())
			}
			context.JSON(http.StatusBadRequest, "数据校验失败")
			return
		}
		context.JSON(http.StatusOK, "数据校验成功")
	})
	r.Run(":9090")
}

在这里插入图片描述

参数校验(二)

validator包:https://github.com/go-playground/validator

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	"net/http"
)

type ValUser struct {
	Name  string `validate:"required" json:"name"`
	Age   uint8  `validate:"gte=0,lte=130" json:"age"`
	Email string `validate:"required,email" json:"email"`
	// 切片数据类型
	Address []ValAddress `validate:"dive" json:"address"`
}

type ValAddress struct {
	Province string `validate:"required" json:"province"`
	City     string `validate:"required" json:"city"`
	Phone    string `validate:"numeric,len=11" json:"phone"`
}

var validate *validator.Validate

func init() {
	validate = validator.New() // 初始化
}

func main() {
	r := gin.Default()
	var user ValUser
	r.POST("/validate", func(context *gin.Context) {
		//testData(context)

		err := context.Bind(&user)
		if err != nil {
			context.JSON(http.StatusBadRequest, "参数错误,绑定失败")
			return
		}
		// 参数校验
		if validateUser(user) {
			context.JSON(http.StatusOK, "数据校验成功")
			return
		}
		context.JSON(http.StatusBadRequest, "校验失败")
		return
	})
	r.Run(":9090")
}

//func testData(context *gin.Context) {
//	address := ValAddress{
//		Province: "浙江省",
//		City:     "杭州市",
//		Phone:    "13575121689",
//	}
//	user := ValUser{
//		Name:    "张三",
//		Age:     15,
//		Email:   "1993036922@qq.com",
//		Address: []ValAddress{address},
//	}
//	context.JSON(http.StatusOK, user)
//}

func validateUser(u ValUser) bool {
	err := validate.Struct(u)
	if err != nil {
		for _, e := range err.(validator.ValidationErrors) {
			fmt.Println("错误的字段:", e.Field())
			fmt.Println("错误的值:", e.Value())
			fmt.Println("错误的tag:", e.Tag())
		}
		return false
	}
	return true
}

在这里插入图片描述

swagger

swagger地址:https://github.com/swaggo/gin-swagger

引入步骤

  • 下载swag及相关包
    • go get -u github.com/swaggo/swag/cmd/swag
    • go get -u github.com/swaggo/gin-swagger
    • go get -u github.com/swaggo/files
  • 实现 Api 代码
  • 编写注释:参考https://github.com/swaggo/swag/blob/master/README.md#declarative-comments-format
  • 在代码中使用 swagger 中间件
  • 引入docs:如我的引入为 _ “2-golang/docs”,_ 表示不为包赋名
  • 执行终端命令:swag init

如果出现 swag 不是命令,无法识别,则先执行: go install github.com/swaggo/swag/cmd/swag@latest

ps:文件名必须叫 main

package main

import (
	_ "2-golang/docs"
	"fmt"
	"github.com/gin-gonic/gin"
	swaggerFiles "github.com/swaggo/files"
	ginSwagger "github.com/swaggo/gin-swagger"
	"net/http"
)

type User struct {
	UserName string `json:"user_name"`
	Password string `json:"password"`
}

type Response struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
	Data string `json:"data"`
}

func main() {
	r := gin.Default()
	// 使用 swagger 中间件
	r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))

	r.GET("/login", login)

	r.POST("/register", register)

	r.Run(":9090")
}

// @Tags 注册接口
// @Summary 注册
// @Description register
// @Accept json
// @Produce json
// @Param username formData string true "用户名"
// @Param password formData string true "密码"
// @Success 200 {string} json "{"code": 200, "data":"{"name":"username","password":"password"}","msg":"OK"}"
// @Router /register [post]
func register(context *gin.Context) {
	var user User
	// get 使用的是 query,post 使用的是 formData,而 Bind 只能绑定 query
	// err := context.Bind(&user)
	err := context.BindQuery(&user)

	if err != nil {
		context.JSON(http.StatusBadRequest, "数据错误")
		return
	}

	res := Response{
		Code: http.StatusOK,
		Msg:  "注册成功",
		Data: "OK",
	}

	context.JSON(http.StatusOK, res)
}

// @Tags 登录接口
// @Summary 登录
// @Description login
// @Accept json
// @Produce json
// @Param username query string true "用户名"
// @Param password query string false "密码"
// @Success 200 {string} json "{"code": 200, "data":"{"name":"username","password":"password"}","msg":"OK"}"
// @Router /login [get]
func login(context *gin.Context) {
	userName := context.Query("name")
	pwd := context.Query("pwd")

	fmt.Println(userName, pwd)

	res := Response{}
	res.Code = http.StatusOK
	res.Msg = "登陆成功"
	res.Data = "OK"

	context.JSON(http.StatusOK, res)
}

在这里插入图片描述

gin框架cookie

package main

import (
	"encoding/hex"
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
)

var cookieName string
var cookieValue string

func main() {
	r := gin.Default()

	r.Use(cookieAuth())

	r.GET("/cookie", func(context *gin.Context) {
		name := context.Query("name")

		if len(name) <= 0 {
			context.JSON(http.StatusOK, "数据错误")
			return
		}

		cookieName = "cookie_" + name
		cookieValue = hex.EncodeToString([]byte(cookieName + "value"))
		val, _ := context.Cookie(cookieName)

		if val == "" {
			context.String(http.StatusOK, "Cookie已经下发,下次登录有效", cookieName)
			return
		}
		context.String(http.StatusOK, "验证成功,cookie值为:%s", val)
	})

	r.Run(":9090")
}

func cookieAuth() gin.HandlerFunc {
	return func(context *gin.Context) {
		val, _ := context.Cookie(cookieName)

		if val == "" {
			context.SetCookie(cookieName, cookieValue, 3600, "/", "localhost", true, true)
			fmt.Println("cookie已经保存完成!")
		}
	}
}

在这里插入图片描述

gin框架session

package main

import (
	"net/http"

	"github.com/gin-contrib/sessions"
	"github.com/gin-contrib/sessions/cookie"
	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()

	// 加入 session 中间件
	store := cookie.NewStore([]byte("session_secret")) // 创建基于cookie的session存储
	r.Use(sessions.Sessions("mySession", store))       // 使用session中间件,并指定session名称和存储方式

	r.GET("/session", func(context *gin.Context) {
		name := context.Query("name")
		if len(name) <= 0 {
			context.JSON(http.StatusOK, "数据错误")
			return
		}

		sessionName := "session_" + name        // 构造session名称
		sessionValue := "session_value_" + name // 构造session值

		session := sessions.Default(context) // 获取默认的session实例
		sessionData := session.Get(sessionName)

		if sessionData != sessionValue { // 如果session中不存在对应值,则为首次访问
			session.Set(sessionName, sessionValue) // 设置 session
			session.Save()                         // 保存 session

			context.JSON(http.StatusOK, "首次访问,session已保存:"+sessionValue)
			return
		}
		context.JSON(http.StatusOK, "访问成功,您的session是:"+sessionData.(string))
	})

	r.Run(":9090") // 启动HTTP服务器,监听9090端口
}

gin框架Https

在这里插入图片描述

package main

import (
	"github.com/gin-gonic/gin"
	"github.com/unrolled/secure"
	"net/http"
)

// HttpRes 结构体用于定义HTTP响应格式
type HttpRes struct {
	Code   int    `json:"code"`   // 响应状态码
	Result string `json:"result"` // 响应结果消息
}

func main() {
	r := gin.Default() // 创建默认的gin路由引擎

	// 使用HTTPS处理程序中间件
	r.Use(httpsHandler())

	// 定义GET路由"/https_test"
	r.GET("/https_test", func(context *gin.Context) {
		// 返回JSON格式的成功消息
		context.JSON(http.StatusOK, HttpRes{
			Code:   http.StatusOK,
			Result: "测试成功",
		})
	})

	path := "D:/2-golang/CA/"                       // 证书路径
	r.RunTLS(":9090", path+"ca.crt", path+"ca.key") // 使用TLS在9090端口运行服务器
}

// httpsHandler 返回一个处理HTTPS请求的中间件函数
func httpsHandler() gin.HandlerFunc {
	return func(context *gin.Context) {
		// 创建secure中间件实例
		secureMiddleware := secure.New(secure.Options{
			SSLRedirect:          true, // 强制SSL重定向
			STSSeconds:           1536000,
			STSIncludeSubdomains: true,
			STSPreload:           true,
			FrameDeny:            true,
			ContentTypeNosniff:   true,
			BrowserXssFilter:     true,
		})
		// 处理HTTPS请求
		err := secureMiddleware.Process(context.Writer, context.Request)
		if err != nil {
			// 如果出现错误,返回数据不安全的响应
			context.AbortWithStatusJSON(http.StatusBadRequest, "数据不安全")
			return
		}
		if status := context.Writer.Status(); status > 300 && status < 399 {
			// 如果响应状态码是重定向类型,则终止请求
			context.Abort()
			return
		}
		context.Next() // 继续处理下一个中间件或路由处理函数
	}
}

在这里插入图片描述

在这里插入图片描述

安全认证(一 静态方法)

package main

import (
	"github.com/gin-gonic/gin"
	"github.com/pjebs/restgate"
	"net/http"
)

func main() {
	r := gin.Default()

	// 使用自定义的认证中间件
	r.Use(authMiddleware())

	// 定义一个需要认证的路由
	r.GET("/auth1", func(context *gin.Context) {
		// 返回验证通过的JSON响应
		resData := struct {
			Code int         `json:"code"`
			Msg  string      `json:"msg"`
			Data interface{} `json:"data"`
		}{http.StatusOK, "验证通过", "OK"}

		context.JSON(http.StatusOK, resData)
	})

	// 启动HTTP服务器并监听9090端口
	r.Run(":9090")
}

// authMiddleware 是一个自定义的 Gin 中间件,用于身份验证
func authMiddleware() gin.HandlerFunc {
	return func(context *gin.Context) {
		// 创建一个 restgate 实例用于验证
		gate := restgate.New(
			"X-Auth-Key",
			"X-Auth-Secret",
			restgate.Static,
			restgate.Config{
				Key:                []string{"admin"},
				Secret:             []string{"adminpw"},
				HTTPSProtectionOff: true,
			},
		)
		
		// 定义一个变量来标记是否调用了下一个中间件
		nextCalled := false
		
		// 定义一个适配器函数,用于在调用下一个中间件时设置标记
		nextAdapter := func(http.ResponseWriter, *http.Request) {
			nextCalled = true
			context.Next()
		}
		
		// 使用 restgate 实例来验证请求,并传入适配器函数
		gate.ServeHTTP(context.Writer, context.Request, nextAdapter)
		
		// 如果没有调用下一个中间件,则返回未经授权的状态码
		if !nextCalled {
			context.AbortWithStatus(http.StatusUnauthorized)
		}
	}
}

在这里插入图片描述

产生警告:WARNING: HTTPS Protection is off. This is potentially insecure!
解决方案:按照 https 流程完成即可

安全认证(二 数据库方法)

package main

import (
	"database/sql"
	"github.com/gin-gonic/gin"
	_ "github.com/go-sql-driver/mysql"
	"github.com/pjebs/restgate"
	"net/http"
)

// main 函数是程序的入口点
func main() {
	// 创建一个默认的 Gin 引擎实例
	r := gin.Default()

	// 使用自定义的认证中间件
	r.Use(authMiddleware())

	// 定义一个需要认证的路由
	r.GET("/auth2", func(context *gin.Context) {
		// 返回验证通过的JSON响应
		resData := struct {
			Code int    `json:"code"`
			Msg  string `json:"msg"`
			Data string `json:"data"`
		}{http.StatusOK, "验证通过", "OK"}

		context.JSON(http.StatusOK, resData)
	})

	// 启动HTTP服务器并监听9090端口
	r.Run(":9090")
}

var db *sql.DB

// init 函数用于初始化数据库连接
func init() {
	// 初始化数据库连接
	db, _ = SqlDB()
}

// SqlDB 函数用于连接数据库
func SqlDB() (*sql.DB, error) {
	// 定义数据库连接参数
	DB_TYPE := "mysql"
	DB_HOST := "localhost"
	DB_PORT := "3306"
	DB_USER := "root"
	DB_NAME := "api_secure"
	DB_PASSWORD := "123456"
	openString := DB_USER + ":" + DB_PASSWORD + "@tcp(" + DB_HOST + ":" + DB_PORT + ")/" + DB_NAME

	// 打开数据库连接
	db, err := sql.Open(DB_TYPE, openString)
	return db, err
}

// authMiddleware 是一个自定义的 Gin 中间件,用于身份验证
func authMiddleware() gin.HandlerFunc {
	return func(context *gin.Context) {
		// 创建一个 restgate 实例用于验证
		gate := restgate.New(
			"X-Auth-Key",
			"X-Auth-Secret",
			restgate.Database,
			restgate.Config{
				DB:                 db,
				Key:                []string{"keys"},
				Secret:             []string{"secrets"},
				TableName:          "users",
				HTTPSProtectionOff: true,
			},
		)

		// 定义一个变量来标记是否调用了下一个中间件
		nextCalled := false

		// 定义一个适配器函数,用于在调用下一个中间件时设置标记
		nextAdapter := func(http.ResponseWriter, *http.Request) {
			nextCalled = true
			context.Next()
		}

		// 使用 restgate 实例来验证请求,并传入适配器函数
		gate.ServeHTTP(context.Writer, context.Request, nextAdapter)

		// 如果没有调用下一个中间件,则返回未经授权的状态码
		if !nextCalled {
			context.AbortWithStatus(http.StatusUnauthorized)
		}
	}
}

日志框架logrus(一)

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"   // 引入 gin web框架
	"github.com/sirupsen/logrus" // 引入 logrus 日志库
	"net/http"
	"os"
)

// 全局变量,创建 logrus 的新实例,用于记录日志
var log = logrus.New()

// initLogrus 初始化日志系统
func initLogrus() error {
	log.Formatter = &logrus.JSONFormatter{} // 设置日志格式为 JSON
	// 打开文件(没有就创建),以追加、创建和写入模式打开或创建日志文件
	file, err := os.OpenFile("./gin_log.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		// 如果创建文件或者打开文件失败,打印错误提示并返回错误
		fmt.Println("创建文件/打开文件失败!")
		return err
	}

	// 设置 logrus 实例的输出为日志文件
	log.Out = file

	// 设置 gin web 框架的运行模式为 ReleaseMode,通常用于产品部署时减少日志输出和调试信息
	gin.SetMode(gin.ReleaseMode)
	// 将 gin 框架的默认日志写入到 logrus 设置的输出中去,使得 gin 框架日志与业务日志同步
	gin.DefaultWriter = log.Out
	// 设置日志级别为 InfoLevel,此级别下,只有 Info 及以上级别的日志会被输出
	log.Level = logrus.InfoLevel
	return nil
}

func main() {
	// 初始化 logrus 日志设置
	err := initLogrus()
	if err != nil {
		// 初始化失败,输出错误信息后返回
		fmt.Println(err)
		return
	}

	// 创建一个默认的 gin 路由器实例
	r := gin.Default()
	// 响应 GET 请求,路径为 "/logrus",后面的匿名函数为处理该路径请求的回调函数
	r.GET("/logrus", func(context *gin.Context) {
		// 使用 logrus 记录请求的相关信息
		log.WithFields(logrus.Fields{
			"url":    context.Request.RequestURI,    // 请求的 URL
			"method": context.Request.Method, // 请求的方法,如 GET、POST 等
			"params": context.Query("name"),  // 请求的查询参数
			"IP":     context.ClientIP(),     // 发起请求的客户端 IP
		}).Info() // 将这些字段信息打印为 Info 级别的日志

		// 定义响应数据结构,并初始化值
		resData := struct {
			Code int         `json:"code"` // 响应状态码
			Msg  string      `json:"msg"`  // 响应信息
			Data interface{} `json:"data"` // 响应数据
		}{http.StatusOK, "响应成功", "OK"}

		// 使用 JSON 格式响应请求
		context.JSON(http.StatusOK, resData)
	})

	// 启动监听,并在 9090 端口上运行
	r.Run(":9090")
}

在这里插入图片描述

日志框架logrus(二)

package main

import (
	"fmt"
	// 引入必要的包
	// Gin框架用于Web服务器的构建和请求的处理
	"github.com/gin-gonic/gin"
	// file-rotatelogs用于日志文件的自动分割
	"github.com/lestrrat-go/file-rotatelogs"
	// lfshook用于为logrus提供钩子功能,根据日志级别输出到不同的目的地
	"github.com/rifflock/lfshook"
	// logrus是Go的一个结构化日志库,提供了比标准库更丰富的日志功能
	"github.com/sirupsen/logrus"
	"net/http"
	"os"
	"path"
	"time"
)

// 定义日志文件的路径和名称
var (
	logFilePath = "./"
	logFileName = "system.log"
)

func main() {
	// 创建Gin的路由器实例
	r := gin.Default()

	// 添加自定义的日志中间件
	r.Use(logMiddleware())

	// 处理GET请求,路径为"/logrus"
	r.GET("/logrus", func(context *gin.Context) {
		// 以JSON格式响应请求
		context.JSON(http.StatusOK, gin.H{
			"code": 200,
			"msg":  "响应成功",
			"data": "OK",
		})
	})

	// 在9090端口上启动服务器
	r.Run(":9090")
}

// logMiddleware返回一个实现了Gin的中间件HandlerFunc的函数
// 用来为每个请求记录日志
func logMiddleware() gin.HandlerFunc {
	// 拼接日志文件的完整路径
	fileName := path.Join(logFilePath, logFileName)

	// 尝试打开或创建日志文件
	file, err := os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		fmt.Println(err)
	}

	// 创建logrus的logger
	logger := logrus.New()

	// 设置日志级别为DebugLevel
	logger.SetLevel(logrus.DebugLevel)

	// 设置日志输出到上面打开的文件
	logger.Out = file

	// 配置日志文件的分割策略
	logWriter, err := rotatelogs.New(
		fileName+".%Y%m%d.log",
		rotatelogs.WithLinkName(fileName),        // 设置软连接指向最新的日志文件
		rotatelogs.WithMaxAge(7*24*time.Hour),    // 设置日志文件的最大保存时间
		rotatelogs.WithRotationTime(1*time.Hour)) // 设置日志分割的时间间隔

	// 定义将日志写入到分割的日志文件中
	writerMap := lfshook.WriterMap{
		logrus.InfoLevel:  logWriter, // 根据日志级别使用不同的输出
		logrus.FatalLevel: logWriter,
		logrus.DebugLevel: logWriter,
		logrus.WarnLevel:  logWriter,
		logrus.ErrorLevel: logWriter,
		logrus.PanicLevel: logWriter,
	}

	// 为logrus添加一个钩子,使用自定义的分割策略和JSON格式器
	logger.AddHook(lfshook.NewHook(writerMap, &logrus.JSONFormatter{
		TimestampFormat: "2024-05-06 15:49:50", // 自定义的时间戳格式
	}))

	// 返回一个Gin的中间件,记录每次请求的详细信息
	return func(context *gin.Context) {
		context.Next() // 处理请求的其它部分

		// 获取请求的一些基本信息
		method := context.Request.Method
		uri := context.Request.RequestURI
		code := context.Writer.Status()
		ip := context.ClientIP()

		// 使用这些信息打印日志
		logger.WithFields(logrus.Fields{
			"code":   code,
			"ip":     ip,
			"method": method,
			"uri":    uri,
		}).Info() // 记录日志
	}
}

在这里插入图片描述

原生数据库使用

导入模块:go get -u github.com/go-sql-driver/mysql

安装 mysql 数据库

安装数据库可能遇到的问题:(网上的方法基本可以解决)
ERROR 1045 (28000): Access denied for user ‘-root’@‘localhost’ (using password: YES)
ERROR 2003 (HY000): Can’t connect to MySQL server on ‘localhost’ (10061)

可视化数据库操作

在这里插入图片描述

// 导包内容
package main

import (
    "database/sql" // 导入sql包处理数据库操作
    "fmt" // 导入fmt包进行格式化输出和错误输出
    "github.com/gin-gonic/gin" // 导入gin包快速构建REST API
    _ "github.com/go-sql-driver/mysql" // 导入MySQL驱动
    "net/http" // 导入http包处理HTTP请求
)

注意别漏了:_ “github.com/go-sql-driver/mysql” // 导入MySQL驱动

var sqlDb *sql.DB // 声明一个sql.DB指针,用于后续的数据库操作
var sqlResponse SqlResponse // sqlResponse是全局的用于封装http返回的结构体
/** 请求结构体 和 响应结构体 */
// SqlUser结构体映射数据库中的user表
type SqlUser struct {
    Name    string `json:"name"` // 用户名
    Age     int    `json:"age"` // 年龄
    Address string `json:"address"` // 地址
}
// SqlResponse结构体用于封装http响应数据
type SqlResponse struct {
    Code    int         `json:"code"` // 响应状态码
    Message string      `json:"message"` // 响应消息
    Data    interface{} `json:"data"` // 响应数据
}
// init函数用于初始化数据库
func init() {
    // MySQL连接字符串
    sqlStr := "root:123456@tcp(127.0.0.1:3306)/testdb?charset=utf8&parseTime=true&loc=Local"
    var err error
    // 打开MySQL连接
    sqlDb, err = sql.Open("mysql", sqlStr)
    if err != nil {
        // 如果连接出错,则输出错误并返回
        fmt.Println("数据库打开出现了问题:", err)
        return
    }
    // 测试与数据库的连接是否存活
    err = sqlDb.Ping()
    if err != nil {
        fmt.Println("数据库打开出现了问题:", err)
        return
    }
}
// 主函数
func main() {
	r := gin.Default()

	r.POST("/sql/insert", insertData)
	r.GET("/sql/read", readData)
	r.GET("/sql/readAll", readAllData)
	r.PUT("/sql/update", updateData)
	r.DELETE("/sql/del", delData)

	r.Run(":9090")
}

// insertData函数处理插入数据的请求
func insertData(context *gin.Context) {
    var u SqlUser
    // 绑定请求中的JSON数据到u结构体
    err := context.Bind(&u)
    if err != nil {
        // 如果绑定出错,设置响应结构体并返回JSON响应
        sqlResponse.Code = http.StatusBadRequest
        sqlResponse.Message = "参数错误"
        sqlResponse.Data = "error"
        context.JSON(http.StatusOK, sqlResponse)
        return
    }
    // 定义插入SQL语句
    sqlStr := "insert into user(name, age, address) values (?,?,?)"
    // 执行插入操作
    ret, err := sqlDb.Exec(sqlStr, u.Name, u.Age, u.Address)
    if err != nil {
        // 如果插入出错,设置响应结构体并返回JSON响应
        fmt.Println(err)
        sqlResponse.Code = http.StatusBadRequest
        sqlResponse.Message = "写入失败"
        sqlResponse.Data = "error"
        context.JSON(http.StatusOK, sqlResponse)
        return
    }
    // 插入成功,设置响应结构体
    sqlResponse.Code = http.StatusOK
    sqlResponse.Message = "写入成功"
    sqlResponse.Data = "OK"
    // 返回JSON响应
    context.JSON(http.StatusOK, sqlResponse)
    // 打印插入的ID
    fmt.Println(ret.LastInsertId())
}

在这里插入图片描述

// readData函数处理读取数据的请求:单条数据
func readData(context *gin.Context) {
    name := context.Query("name") // 从查询参数获取name
    // 定义查询SQL语句
    sqlStr := "select age,address from user where name=?"
    var u SqlUser
    // 执行查询,并扫描结果到u结构体
    err := sqlDb.QueryRow(sqlStr, name).Scan(&u.Age, &u.Address)
    if err != nil {
        // 如果查询出错,设置响应结构体并返回JSON响应
        fmt.Println(err)
        sqlResponse.Code = http.StatusBadRequest
        sqlResponse.Message = "读取失败"
        sqlResponse.Data = "error"
        context.JSON(http.StatusOK, sqlResponse)
        return
    }
    // 查询成功,填充姓名,并设置响应结构体
    u.Name = name
    sqlResponse.Code = http.StatusOK
    sqlResponse.Message = "读取成功"
    sqlResponse.Data = u
    // 返回JSON响应
    context.JSON(http.StatusOK, sqlResponse)
}

在这里插入图片描述

func readAllData(context *gin.Context) {
    // 从Http请求中获取地址参数(address)
    address := context.Query("address")

    // 定义SQL查询语句,根据特定地址(address)查询与之匹配的所有用户名称(name)和用户年龄(age)
    sqlStr := "select name, age from user where address=?"

    // 使用预定义的sqlDb对象执行刚刚定义的SQL查询语句
    rows, err := sqlDb.Query(sqlStr, address)

    // 如果在执行SQL查询语句过程中出现错误,将错误信息打印到控制台,并向HTTP响应中写入相应的错误信息,然后退出当前函数
    if err != nil {
        fmt.Println(err)
        sqlResponse.Code = http.StatusBadRequest
        sqlResponse.Message = "读取失败"
        sqlResponse.Data = "error"
        context.JSON(http.StatusOK, sqlResponse)
        return
    }

    // 定时关闭数据库连接
    defer rows.Close()

    // 定义一个空切片,用来存储所有查询到的用户数据
    resUser := make([]SqlUser, 0)

    // 遍历数据库查询结果集rows,提取所有的数据并添加到之前定义的用于存储用户数据的切片中
    for rows.Next() {
        var userTemp SqlUser
        // 将从结果集中获取到的每一行数据相应的字段(name, age)提取出来,赋值给用户结构体(userTemp)对应的字段
        rows.Scan(&userTemp.Name, &userTemp.Age)
        // 将地址字段值设置为查询参数
        userTemp.Address = address
        // 将该用户结构体添加到用户切片中
        resUser = append(resUser, userTemp)
    }

    // 设置HTTP响应的状态码、消息和数据内容,然后将其写入到HTTP响应中。这里写入的数据内容就是查询到的所有用户数据
    sqlResponse.Code = http.StatusOK
    sqlResponse.Message = "读取成功"
    sqlResponse.Data = resUser
    context.JSON(http.StatusOK, sqlResponse)
}

在这里插入图片描述

func updateData(context *gin.Context) {
    // 定义一个SqlUser类型的变量u,用于存储请求中的用户数据
    var u SqlUser
    // 定义一个整型变量count,用于存储数据库查询返回的计数结果
    var count int

    // 使用context.Bind()方法从HTTP请求体中提取数据并绑定到变量u中
    err := context.Bind(&u)
    // 如果在数据绑定过程中发生错误,则向客户端发送参数错误的响应
    if err != nil {
        sqlResponse.Code = http.StatusBadRequest
        sqlResponse.Message = "参数错误"
        sqlResponse.Data = "error"
        context.JSON(http.StatusOK, sqlResponse)
        return
    }

    // 定义SQL查询字符串,用于检查具有特定名称的用户是否存在于数据库中
    sqlStr := "select count(*) from user where name=?"
    // 执行SQL查询,将查询结果(即用户的数量)存储于变量count中
    err = sqlDb.QueryRow(sqlStr, u.Name).Scan(&count)
    // 如果查询结果显示用户数量为0或查询时发生错误,则向客户端发送数据不存在的响应
    if count <= 0 || err != nil {
        sqlResponse.Code = http.StatusBadRequest
        sqlResponse.Message = "更新的数据不存在"
        sqlResponse.Data = "error"
        context.JSON(http.StatusOK, sqlResponse)
        return
    }

    // 定义SQL更新字符串,用于更新用户的年龄(age)和地址(address)
    upStr := "update user set age=?, address=? where name=?"
    // 执行SQL更新操作
    ret, err := sqlDb.Exec(upStr, u.Age, u.Address, u.Name)
    // 如果在执行更新操作时发生错误,则向客户端发送更新失败的响应
    if err != nil {
        fmt.Println(err)
        sqlResponse.Code = http.StatusBadRequest
        sqlResponse.Message = "更新失败"
        sqlResponse.Data = "error"
        context.JSON(http.StatusOK, sqlResponse)
        return
    }

    // 如果更新操作成功,向客户端发送更新成功的响应
    sqlResponse.Code = http.StatusOK
    sqlResponse.Message = "更新成功"
    sqlResponse.Data = "OK"
    context.JSON(http.StatusOK, sqlResponse)

    // 打印到控制台SQL操作返回的结果,这通常用于调试
    // 注意:这里的LastInsertId()在更新操作中可能不会返回有意义的值,需要注意其适用场景
    fmt.Println(ret.LastInsertId())
}

在这里插入图片描述

func delData(context *gin.Context) {
    // 从HTTP请求的查询参数中获取需要删除的用户的名称
    name := context.Query("name")
    // 定义一个整型变量count,用于存储数据库查询返回的计数结果
    var count int

    // 定义SQL查询字符串,用于检查具有特定名称的用户是否存在于数据库中
    sqlStr := "select count(*) from user where name=?"
    // 执行SQL查询,将查询结果(即用户的数量)存储于变量count中
    err := sqlDb.QueryRow(sqlStr, name).Scan(&count)
    // 如果查询结果显示用户数量为0或查询时发生错误,则向客户端发送数据不存在的响应
    if count <= 0 || err != nil {
        sqlResponse.Code = http.StatusBadRequest
        sqlResponse.Message = "删除的数据不存在"
        sqlResponse.Data = "error"
        context.JSON(http.StatusOK, sqlResponse)
        return
    }

    // 定义SQL删除字符串,用于从数据库中删除具有特定名称的用户
    delStr := "delete from user where name=?"
    // 执行SQL删除操作
    ret, err := sqlDb.Exec(delStr, name)
    // 如果在执行删除操作时发生错误,则向客户端发送删除失败的响应
    if err != nil {
        sqlResponse.Code = http.StatusBadRequest
        sqlResponse.Message = "删除的数据不存在"
        sqlResponse.Data = "error"
        context.JSON(http.StatusOK, sqlResponse)
        return
    }

    // 如果删除成功,向客户端发送删除成功的响应
    sqlResponse.Code = http.StatusOK
    sqlResponse.Message = "删除成功"
    sqlResponse.Data = "OK"
    context.JSON(http.StatusOK, sqlResponse)

    // 打印到控制台SQL操作返回的结果,这通常用于调试
    // 注意:这里的LastInsertId()在删除操作中可能不会返回有意义的值,因为它更适用于插入操作
    fmt.Println(ret.LastInsertId())
}

在这里插入图片描述

xorm框架

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	_ "github.com/go-sql-driver/mysql" // 导入MySQL驱动
	"github.com/go-xorm/xorm"
	"net/http"
	"time"
)

var x *xorm.Engine
var xormResponse XormResponse

type Stu struct {
	Id      int64     `xorm:"pk autoincr" json:"id"`
	StuNum  string    `xorm:"unique" json:"stu_num"`
	Name    string    `json:"name"`
	Age     int       `json:"age"`
	Created time.Time `xorm:"created" json:"created"`
	Updated time.Time `xorm:"updated" json:"updated"`
}

type XormResponse struct {
	Code    int         `json:"code"`    // 响应状态码
	Message string      `json:"message"` // 响应消息
	Data    interface{} `json:"data"`    // 响应数据
}
func init() {
	sqlStr := "root:123456@tcp(127.0.0.1:3306)/xorm?charset=utf8&parseTime=true&loc=Local"
	var err error
	x, err = xorm.NewEngine("mysql", sqlStr)
	if err != nil {
		fmt.Println("数据库连接失败", err)
		return
	}
	err = x.Sync(new(Stu))
	if err != nil {
		fmt.Println("数据库同步错误", err)
		return
	}
	fmt.Println("数据库初始化成功", err)
}
func main() {
	r := gin.Default()

	r.POST("/xorm/insert", insertData)
	r.GET("/xorm/read", readData)
	r.GET("/xorm/mulread", mulReadData)
	r.PUT("/xorm/update", xormUpdateData)
	r.DELETE("/xorm/del", delData)

	r.Run(":9090")
}
func insertData(context *gin.Context) {
	var s Stu
	err := context.Bind(&s)
	if err != nil {
		xormResponse.Code = http.StatusBadRequest
		xormResponse.Message = "参数错误"
		xormResponse.Data = "error"
		context.JSON(http.StatusOK, xormResponse)
		return
	}
	affected, err := x.Insert(s)
	if affected <= 0 || err != nil {
		xormResponse.Code = http.StatusBadRequest
		xormResponse.Message = "写入失败"
		xormResponse.Data = "error"
		context.JSON(http.StatusOK, xormResponse)
		return
	}
	xormResponse.Code = http.StatusOK
	xormResponse.Message = "写入成功"
	xormResponse.Data = "OK"
	context.JSON(http.StatusOK, xormResponse)
}

在这里插入图片描述

func readData(context *gin.Context) {
	stuNum := context.Query("stu_num")
	var stus []Stu
	err := x.Where("stu_num=?", stuNum).Find(&stus)
	if err != nil {
		xormResponse.Code = http.StatusBadRequest
		xormResponse.Message = "查询错误"
		xormResponse.Data = "error"
		context.JSON(http.StatusOK, xormResponse)
		return
	}
	xormResponse.Code = http.StatusOK
	xormResponse.Message = "查询成功"
	xormResponse.Data = stus
	context.JSON(http.StatusOK, xormResponse)
}

在这里插入图片描述

func mulReadData(context *gin.Context) {
	name := context.Query("name")
	var stus []Stu
	err := x.Where("name=?", name).And("age>20").Limit(10, 0).Asc("age").Find(&stus)
	if err != nil {
		xormResponse.Code = http.StatusBadRequest
		xormResponse.Message = "查询错误"
		xormResponse.Data = "error"
		context.JSON(http.StatusOK, xormResponse)
		return
	}
	xormResponse.Code = http.StatusOK
	xormResponse.Message = "查询成功"
	xormResponse.Data = stus
	context.JSON(http.StatusOK, xormResponse)
}

在这里插入图片描述

func xormUpdateData(context *gin.Context) {
	var s Stu
	err := context.Bind(&s)
	if err != nil {
		xormResponse.Code = http.StatusBadRequest
		xormResponse.Message = "参数错误"
		xormResponse.Data = "error"
		context.JSON(http.StatusOK, xormResponse)
		return
	}
	var stus []Stu
	err = x.Where("stu_num=?", s.StuNum).Find(&stus)
	if err != nil || len(stus) <= 0 {
		xormResponse.Code = http.StatusBadRequest
		xormResponse.Message = "数据不存在"
		xormResponse.Data = "error"
		context.JSON(http.StatusOK, xormResponse)
		return
	}
	affected, err := x.Where("stu_num=?", s.StuNum).Update(&Stu{Name: s.Name, Age: s.Age})
	if err != nil || affected <= 0 {
		xormResponse.Code = http.StatusBadRequest
		xormResponse.Message = "修改失败"
		xormResponse.Data = "error"
		context.JSON(http.StatusOK, xormResponse)
		return
	}
	xormResponse.Code = http.StatusBadRequest
	xormResponse.Message = "修改成功"
	xormResponse.Data = "OK"
	context.JSON(http.StatusOK, xormResponse)
}

在这里插入图片描述

func delData(context *gin.Context) {
	stuNum := context.Query("stu_num")
	var stus []Stu
	err := x.Where("name=?", stuNum).Find(&stus)
	if err != nil {
		xormResponse.Code = http.StatusBadRequest
		xormResponse.Message = "没有此条数据"
		xormResponse.Data = "error"
		context.JSON(http.StatusOK, xormResponse)
		return
	}
	affected, err := x.Where("stu_num=?", stuNum).Delete(&Stu{})
	if err != nil || affected <= 0 {
		xormResponse.Code = http.StatusBadRequest
		xormResponse.Message = "删除失败"
		xormResponse.Data = "error"
		context.JSON(http.StatusOK, xormResponse)
		return
	}
	xormResponse.Code = http.StatusBadRequest
	xormResponse.Message = "删除成功"
	xormResponse.Data = "OK"
	context.JSON(http.StatusOK, xormResponse)
}

在这里插入图片描述

gorm框架

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"net/http"
	"time"
)

type Product struct {
	ID             int       `gorm:"primaryKey;autoIncrement" json:"id"`
	Number         string    `gorm:"unique" json:"number"`
	Category       string    `gorm:"type:varchar(256);not null" json:"category"`
	Name           string    `gorm:"type:varchar(20);not null" json:"name"`
	MadeIn         string    `gorm:"type:varchar(128);not null" json:"madeIn"`
	ProductionTime time.Time `json:"production_time"`
}

type GormResponse struct {
	Code    int         `json:"code"`    // 响应状态码
	Message string      `json:"message"` // 响应消息
	Data    interface{} `json:"data"`    // 响应数据
}

var gormResponse GormResponse

var gormDB *gorm.DB
func init() {
	var err error
	sqlStr := "root:123456@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=true&loc=Local"
	gormDB, err = gorm.Open(mysql.Open(sqlStr), &gorm.Config{})
	if err != nil {
		fmt.Println("数据库连接失败", err)
		return
	}
}

func main() {
	r := gin.Default()

	r.POST("/gorm/insert", insetData)
	r.GET("/gorm/read", readData)
	r.GET("/gorm/readAll", readAllData)
	r.PUT("/gorm/update", updateData)
	r.DELETE("/gorm/del", delData)

	r.Run(":9090")
}
func errorTouch(context *gin.Context) {
	defer func() {
		err := recover()
		if err != nil {
			gormResponse.Code = http.StatusBadRequest
			gormResponse.Message = "错误"
			gormResponse.Data = err
			context.JSON(http.StatusOK, gormResponse)
		}
	}()
}
func insetData(context *gin.Context) {
	errorTouch(context)
	var p Product
	err := context.Bind(&p)
	if err != nil {
		gormResponse.Code = http.StatusBadRequest
		gormResponse.Message = "参数错误"
		gormResponse.Data = err
		context.JSON(http.StatusOK, gormResponse)
		return
	}
	tx := gormDB.Create(&p)
	if tx.RowsAffected <= 0 {
		gormResponse.Code = http.StatusBadRequest
		gormResponse.Message = "写入失败"
		gormResponse.Data = "error"
		context.JSON(http.StatusBadRequest, gormResponse)
		return
	}
	gormResponse.Code = http.StatusOK
	gormResponse.Message = "写入成功"
	gormResponse.Data = "ok"
	context.JSON(http.StatusOK, gormResponse)
}

在这里插入图片描述

func readData(context *gin.Context) {
	errorTouch(context)
	number := context.Query("number")
	product := Product{}
	tx := gormDB.Where("number=?", number).First(&product)
	if tx.Error != nil {
		gormResponse.Code = http.StatusBadRequest
		gormResponse.Message = "查询错误"
		gormResponse.Data = tx.Error
		context.JSON(http.StatusOK, gormResponse)
		return
	}
	gormResponse.Code = http.StatusBadRequest
	gormResponse.Message = "查询成功"
	gormResponse.Data = product
	context.JSON(http.StatusOK, gormResponse)
}

在这里插入图片描述

func readAllData(context *gin.Context) {
	category := context.Query("category")
	products := make([]Product, 10)
	tx := gormDB.Where("category=?", category).Find(&products).Limit(10)
	if tx.Error != nil {
		gormResponse.Code = http.StatusBadRequest
		gormResponse.Message = "查询错误"
		gormResponse.Data = tx.Error
		context.JSON(http.StatusOK, gormResponse)
		return
	}
	gormResponse.Code = http.StatusOK
	gormResponse.Message = "查询成功"
	gormResponse.Data = products
	context.JSON(http.StatusOK, gormResponse)
}

在这里插入图片描述

func updateData(context *gin.Context) {
	errorTouch(context)
	var p Product
	err := context.Bind(&p)
	if err != nil {
		gormResponse.Code = http.StatusBadRequest
		gormResponse.Message = "参数错误"
		gormResponse.Data = err
		context.JSON(http.StatusOK, gormResponse)
		return
	}
	var count int64
	gormDB.Model(&Product{}).Where("number=?", p.Number).Count(&count)
	if count <= 0 {
		gormResponse.Code = http.StatusBadRequest
		gormResponse.Message = "数据不存在"
		gormResponse.Data = "error"
		context.JSON(http.StatusOK, gormResponse)
		return
	}
	tx := gormDB.Model(&Product{}).Where("number=?", p.Number).Updates(&p)
	if tx.RowsAffected <= 0 {
		gormResponse.Code = http.StatusBadRequest
		gormResponse.Message = "写入失败"
		gormResponse.Data = "error"
		context.JSON(http.StatusOK, gormResponse)
		return
	}
	gormResponse.Code = http.StatusOK
	gormResponse.Message = "更新成功"
	gormResponse.Data = "ok"
	context.JSON(http.StatusOK, gormResponse)
}

在这里插入图片描述

func delData(context *gin.Context) {
	errorTouch(context)
	number := context.Query("number")
	var count int64
	gormDB.Model(&Product{}).Where("number=?", number).Count(&count)
	if count <= 0 {
		gormResponse.Code = http.StatusBadRequest
		gormResponse.Message = "数据不存在"
		gormResponse.Data = "error"
		context.JSON(http.StatusOK, gormResponse)
		return
	}
	tx := gormDB.Where("number=?", number).Delete(&Product{})
	if tx.RowsAffected <= 0 {
		gormResponse.Code = http.StatusBadRequest
		gormResponse.Message = "删除失败"
		gormResponse.Data = "error"
		context.JSON(http.StatusOK, gormResponse)
		return
	}
	gormResponse.Code = http.StatusOK
	gormResponse.Message = "删除成功"
	gormResponse.Data = "ok"
	context.JSON(http.StatusOK, gormResponse)
}

在这里插入图片描述

HMAC 签名

package main

import (
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"net/http"
	"strings"
	"time"
)

type HmacUser struct {
	Id        string `json:"id"`
	Name      string `json:"name"`
	Telephone string `json:"telephone"`
	Password  string `json:"password"`
}

type MyClaims struct {
	UserId string
	jwt.StandardClaims
}

var jwtKey = []byte("a_secret_key")

func main() {
	r := gin.Default()

	r.POST("/getToken1", func(context *gin.Context) {
		var u HmacUser
		context.Bind(&u)
		token, err := hmacReleaseToken(u)
		if err != nil {
			context.JSON(http.StatusInternalServerError, err)
		}
		context.JSON(http.StatusOK, gin.H{
			"code": http.StatusOK,
			"msg":  "token分发成功",
			"data": token,
		})
	})

	r.POST("/checkToken1", hmacAuthMiddleware(), func(context *gin.Context) {
		context.JSON(http.StatusOK, "验证成功")
	})

	r.Run(":9090")
}

func hmacAuthMiddleware() gin.HandlerFunc {
	return func(context *gin.Context) {
		auth := "fanfan"
		tokenString := context.GetHeader("Authorization")
		if tokenString == "" || !strings.HasPrefix(tokenString, auth+":") {
			context.JSON(http.StatusUnauthorized, gin.H{"code": http.StatusUnauthorized, "msg": "前缀错误"})
			context.Abort()
			return
		}
		index := strings.Index(tokenString, auth+":")
		tokenString = tokenString[index+len(auth)+1:]
		token, claims, err := hmacParseToken(tokenString)

		if err != nil || !token.Valid {
			context.JSON(http.StatusUnauthorized, gin.H{"code": http.StatusUnauthorized, "msg": "证书无效"})
			context.Abort()
			return
		}
		var u HmacUser
		context.Bind(&u)
		if u.Id != claims.UserId {
			context.JSON(http.StatusUnauthorized, gin.H{"code": http.StatusUnauthorized, "msg": "用户不存在"})
			context.Abort()
			return
		}
		context.Next()
	}
}

func hmacParseToken(tokenString string) (*jwt.Token, *MyClaims, error) {
	claims := &MyClaims{}
	token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
		return jwtKey, nil
	})
	return token, claims, err
}

func hmacReleaseToken(u HmacUser) (string, error) {
	expirationTime := time.Now().Add(7 * 24 * time.Hour)
	claims := &MyClaims{
		UserId: u.Id,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: expirationTime.Unix(),
			IssuedAt:  time.Now().Unix(),
			Issuer:    "fanfan",
			Subject:   "user token",
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenString, err := token.SignedString(jwtKey)
	if err != nil {
		return "", err
	}
	return tokenString, nil
}

在这里插入图片描述

在这里插入图片描述

RSA签名

rsa签名生成公钥、私钥:https://www.metools.info/code/c80.html

package main

import (
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"io/ioutil"
	"net/http"
	"strings"
	"time"
)

type RsaUser struct {
	Id        string `json:"id"`
	Name      string `json:"name"`
	Telephone string `json:"telephone"`
	Password  string `json:"password"`
}

type RasClaims struct {
	UserId string `json:"user_id"`
	jwt.StandardClaims
}

var (
	resPrivateKey  []byte
	resPublicKey   []byte
	err2_1, err2_2 error
)

func init() {
	resPrivateKey, err2_1 = ioutil.ReadFile("./token/private.pem")
	resPublicKey, err2_2 = ioutil.ReadFile("./token/public.pem")
	if err2_1 != nil || err2_2 != nil {
		panic(fmt.Sprintf("打开密钥文件错误:%s,%s", err2_1, err2_2))
	}
}

func main() {
	r := gin.Default()

	r.POST("/getToken2", func(context *gin.Context) {
		u := RsaUser{}
		err := context.Bind(&u)
		if err != nil {
			context.JSON(http.StatusBadRequest, "参数错误")
			return
		}
		token, err := resaReleaseToken(u)
		if err != nil {
			context.JSON(http.StatusBadRequest, "生成token错误")
			return
		}
		context.JSON(http.StatusOK, gin.H{
			"code": http.StatusOK,
			"msg":  "token分发成功",
			"data": token,
		})
	})

	r.POST("/checkToken2", rsaTokenMiddle(), func(context *gin.Context) {
		context.JSON(http.StatusOK, "验证成功")
	})

	r.Run(":9090")
}

func rsaTokenMiddle() gin.HandlerFunc {
	return func(context *gin.Context) {
		auth := "fanfan"
		tokenString := context.GetHeader("Authorization")
		if tokenString == "" || !strings.HasPrefix(tokenString, auth+":") {
			context.JSON(http.StatusUnauthorized, gin.H{"code": http.StatusUnauthorized, "msg": "无效的token"})
			context.Abort()
			return
		}
		index := strings.Index(tokenString, auth+":")
		tokenString = tokenString[index+len(auth)+1:]
		claims, err := rsaJwtTokenRead(tokenString)
		if err != nil {
			context.JSON(http.StatusUnauthorized, gin.H{"code": http.StatusUnauthorized, "msg": "证书无效"})
			context.Abort()
			return
		}
		claimsValue := claims.(jwt.MapClaims)
		if claimsValue["user_id"] == nil {
			context.JSON(http.StatusUnauthorized, gin.H{"code": http.StatusUnauthorized, "msg": "用户不存在"})
			context.Abort()
			return
		}
		var u RsaUser
		context.Bind(&u)
		if u.Id != claimsValue["user_id"].(string) {
			context.JSON(http.StatusUnauthorized, gin.H{"code": http.StatusUnauthorized, "msg": "用户不存在"})
			context.Abort()
			return
		}
		context.Next()
	}
}

func rsaJwtTokenRead(tokenString string) (interface{}, error) {
	pem, err := jwt.ParseRSAPublicKeyFromPEM(resPublicKey)
	if err != nil {
		return nil, err
	}
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		if _, OK := token.Method.(*jwt.SigningMethodRSA); !OK {
			return nil, fmt.Errorf("解析方法错误")
		}
		return pem, err
	})
	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		return claims, err
	}
	return nil, err
}

func resaReleaseToken(u RsaUser) (interface{}, error) {
	tokenGen, err := rasJwtTokenGen(u.Id)
	return tokenGen, err
}

func rasJwtTokenGen(id string) (interface{}, error) {
	private, err := jwt.ParseRSAPrivateKeyFromPEM(resPrivateKey)
	if err != nil {
		return nil, err
	}
	claims := &RasClaims{
		UserId: id,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: time.Now().Add(7 * 24 * time.Hour).Unix(),
			Issuer:    "fanfan",
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
	signedString, err := token.SignedString(private)
	return signedString, err
}

在这里插入图片描述

在这里插入图片描述

ECDSA签名

package main

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"errors"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"net/http"
	"strings"
	"time"
)

type EcdsaUser struct {
	Id        string `json:"id"`
	Name      string `json:"name"`
	Telephone string `json:"telephone"`
	Password  string `json:"password"`
}

type EcdsaClaims struct {
	UserId string `json:"user_id"`
	jwt.StandardClaims
}

var (
	err3          error
	eccPrivateKey *ecdsa.PrivateKey
	eccPublicKey  *ecdsa.PublicKey
)

func init() {
	eccPrivateKey, eccPublicKey, err3 = getEcdsaKey(2)
	if err3 != nil {
		panic(err3)
		return
	}
}

func ecdsaReleaseToken(u EcdsaUser) (interface{}, error) {
	claims := &EcdsaClaims{
		UserId: u.Id,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: time.Now().Add(7 * 24 * time.Hour).Unix(),
			Issuer:    "fanfan",
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
	signedString, err := token.SignedString(eccPrivateKey)
	return signedString, err
}

func main() {
	r := gin.Default()

	r.POST("/getToken3", func(context *gin.Context) {
		var u EcdsaUser
		err := context.Bind(&u)
		if err != nil {
			context.JSON(http.StatusInternalServerError, "参数错误")
		}
		token, err := ecdsaReleaseToken(u)
		if err != nil {
			context.JSON(http.StatusInternalServerError, err)
		}
		context.JSON(http.StatusOK, gin.H{
			"code": http.StatusOK,
			"msg":  "token分发成功",
			"data": token,
		})
	})

	r.POST("/checkToken3", ecdsaTokenMiddleware(), func(context *gin.Context) {
		context.JSON(http.StatusOK, "验证成功")
	})

	r.Run(":9090")
}

func ecdsaTokenMiddleware() gin.HandlerFunc {
	return func(context *gin.Context) {
		auth := "fanfan"
		tokenString := context.GetHeader("Authorization")
		if tokenString == "" || !strings.HasPrefix(tokenString, auth+":") {
			context.JSON(http.StatusUnauthorized, gin.H{"code": http.StatusUnauthorized, "msg": "无效的token"})
			context.Abort()
			return
		}
		index := strings.Index(tokenString, auth+":")
		tokenString = tokenString[index+len(auth)+1:]
		claims, err := ecdsaJwtTokenRead(tokenString)
		if err != nil {
			context.AbortWithStatusJSON(http.StatusUnauthorized, err)
			return
		}
		claimsValue := claims.(jwt.MapClaims)
		if claimsValue["user_id"] == nil {
			context.AbortWithStatusJSON(http.StatusUnauthorized, "id不存在")
			return
		}
		var u EcdsaUser
		context.Bind(&u)
		if u.Id != claimsValue["user_id"] {
			context.JSON(http.StatusUnauthorized, gin.H{"code": http.StatusUnauthorized, "msg": "用户不存在"})
			context.Abort()
			return
		}
		context.Next()
	}
}

func ecdsaJwtTokenRead(tokenString string) (interface{}, error) {
	myToken, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodECDSA); !ok {
			return nil, fmt.Errorf("无效的签名方法")
		}
		return eccPublicKey, nil
	})
	if claims, ok := myToken.Claims.(jwt.MapClaims); ok && myToken.Valid {
		return claims, nil
	}
	return nil, err
}

func getEcdsaKey(keyType int) (*ecdsa.PrivateKey, *ecdsa.PublicKey, error) {
	var err error
	var prk *ecdsa.PrivateKey
	var pub *ecdsa.PublicKey
	var curve elliptic.Curve
	switch keyType {
	case 1:
		curve = elliptic.P224()
	case 2:
		curve = elliptic.P256()
	case 3:
		curve = elliptic.P384()
	case 4:
		curve = elliptic.P521()
	default:
		err = errors.New("输入签名 key 类型错误!")
		return nil, nil, err
	}
	prk, err = ecdsa.GenerateKey(curve, rand.Reader)
	if err != nil {
		return nil, nil, err
	}
	pub = &prk.PublicKey
	return prk, pub, err
}

在这里插入图片描述

在这里插入图片描述

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值