惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Hacker News: Ask HN
Hacker News: Ask HN
IT之家
IT之家
S
SegmentFault 最新的问题
T
Tailwind CSS Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 司徒正美
J
Java Code Geeks
博客园 - 聂微东
雷峰网
雷峰网
阮一峰的网络日志
阮一峰的网络日志
The Cloudflare Blog
博客园_首页
大猫的无限游戏
大猫的无限游戏
博客园 - 三生石上(FineUI控件)
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 【当耐特】
腾讯CDC
Apple Machine Learning Research
Apple Machine Learning Research
酷 壳 – CoolShell
酷 壳 – CoolShell
V
V2EX
宝玉的分享
宝玉的分享
小众软件
小众软件
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Hugging Face - Blog
Hugging Face - Blog
月光博客
月光博客
NISL@THU
NISL@THU
T
The Exploit Database - CXSecurity.com
C
CXSECURITY Database RSS Feed - CXSecurity.com
WordPress大学
WordPress大学
有赞技术团队
有赞技术团队
Blog — PlanetScale
Blog — PlanetScale
aimingoo的专栏
aimingoo的专栏
L
LINUX DO - 热门话题
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
F
Fortinet All Blogs
博客园 - Franky
L
Lohrmann on Cybersecurity
S
Secure Thoughts
量子位
V
Vulnerabilities – Threatpost
Last Week in AI
Last Week in AI
博客园 - 叶小钗
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
L
LINUX DO - 最新话题
I
InfoQ
C
CERT Recently Published Vulnerability Notes
Security Archives - TechRepublic
Security Archives - TechRepublic
P
Proofpoint News Feed
G
GRAHAM CLULEY
Cisco Talos Blog
Cisco Talos Blog

Chenxu's Blog

RAG检索优化:从 68% 到 82.2% 学习深度学习 北邮计算机研究生选课指北 学习机器学习 上海篇 提出一个好问题 写代码的环境 选购一台合适的设备 计算机的第零课 序言 内蒙古篇 山东篇 一篇对transformers的疑惑 浅析GPUStack 浅谈后端项目分层 浙江篇 河南篇 一些三端协同的开发工具记录 浅谈编程语言中的 GC 在 Windows 上安装 Rust 美化博客 消息队列 MyBatis 现代交换原理2024年题目 回忆版 北邮计科本科课程指北 ClkLog埋点用户分析系统研究报告 大数据梧桐实验分享 大数据HBase实验分享 国产向量数据库研究实践 —— 基于 Milvus 的电影推荐系统 服务器环境配置 大数据HDFS实验分享 SSE多服务之间推送数据 数据库系统原理 操作系统 编译原理 手把手教你gozero从开发到部署 (番外篇): 介绍 gtodolist 前端项目 天津篇 北京篇 手把手教你gozero从开发到部署 (5): gtodolist 之任务的删除 前端相关问题 vue3 + element-plus学习 git相关命令 人工智能原理 使用 MongoTemplate 时开启事务 MySQL Redis 力扣刷题笔记之动态规划 记录一次 VSCode 出现的各种奇怪的问题 手把手教你gozero从开发到部署 (4): 在 model 中自己编写函数实现数据库分页查询 记录平时用到的 windows 右键管理 记录平时使用的 linux 的指令 安装 gstore 并使用 java api 手把手教你gozero从开发到部署 (3): gtodolist 之任务的创建和修改 手把手教你gozero从开发到部署 (番外篇): 记录第一次部署 CI/CD 的流程 记录完成 gtodolist 中遇到的 bug 手把手教你gozero从开发到部署 (2): gtodolist 之 user 模块开发 手把手教你gozero从开发到部署 (1): gtodolist 项目说明和环境准备 快速入门 gozero 框架 vue部署在nginx上的相关问题 记一次安装scrapy的报错 将博客部署至服务器 Java CS143: Compliers 《MySQL必知必会》读书笔记(2) 初识 GORM Spring 《MySQL必知必会》读书笔记(1) 本地搭建博客 🤝友链 🙋🏻‍♂️关于 Browser and Device Check
初识 gin 框架
Chenxu · 2023-06-30 · via Chenxu's Blog

推荐阅读官方文档 , 里面写的很详细

万物始于安装, 首先是安装 gin, 只需要执行下面一条指令即可:

go get -u github.com/gin-gonic/gin

如果你出现了报错的信息, 可以试试修改 go 的代理, 即下面这条指令:

go env -w GOPROXY=https://goproxy.cn,direct

在你的项目中导入 gin:

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

执行以下代码, 在浏览器中输入 localhost:8090/ping, 出现 pong 且后台不报错, 即证明安装成功

package main

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

func main() {
	// 创建一个默认的路由引擎
	r := gin.Default()
    // 设置请求路径为 /ping, 请求方式为 get
    r.GET("/ping", func(ctx *gin.Context) {
		ctx.String(http.StatusOK, "pong")
	})
	// 启动HTTP服务,默认在8080端口启动服务, 这里指定为8090端口
	r.Run(":8090")
}

返回数据

上述测试代码中的 ctx.String() 代表 gin 会给前端返回一个 string 类型的数据, 括号内的参数依次为 http 状态码和返回数据, gin 能返回的数据有很多, 但实际使用中应该是返回 json 的情况最多, 所以下面只介绍返回 json 的情况 (其他数据类型应该也大差不差)

  1. 可以将结构体直接作为 json 返回

    type josnTest struct {
        Name string `json:"username"`
        Age  int   `json:"age"`
    }
    
    func main() {
        r := gin.Default()
    
        r.GET("/json", func(ctx *gin.Context) {
        	test := josnTest{"测试", 1}
        	ctx.JSON(http.StatusOK, test)
        })
    
        r.Run(":8080")
    }
    
  2. 可以使用 gin.H()返回

    type josnTest struct {
        Name string `json:"username"`
        Age  int   `json:"age"`
    }
    
    func main() {
        r := gin.Default()
    
        r.GET("/json", func(ctx *gin.Context) {
    	    ctx.JSON(http.StatusOK, gin.H{"username": "测试", "age": 1})
        })
    
        r.Run(":8080")
    }
    

获取参数

获取 QueryString 中的参数

queryString 是指 url 中 ? 后面类似于键值对的参数

请求路径为: http://localhost:8090/query?username=zhangsan&age=17

r.GET("/query", func(ctx *gin.Context) {
    // Query 是从直接获取数据
	username := ctx.Query("username")
    // DefaultQuery 是如果没有获取到数据则会赋一个默认值
    // 注意获取到的数据均为 string, 需要转换类型
	age, _ := strconv.Atoi(ctx.DefaultQuery("age", "12"))
	test := josnTest{username, age}
	ctx.JSON(http.StatusOK, test)
})

获取表单中的数据

请求方式为 post, 请求数据通过 form 表单提交

r.POST("/form", func(ctx *gin.Context) {
    // 与上一段代码类似
	username := ctx.DefaultPostForm("username", "lisi")
	age, _ := strconv.Atoi(ctx.PostForm("age"))
	ctx.JSON(http.StatusOK, gin.H{"username": username, "age": age})
})

获取 json 数据

请求方式为 post, 请求数据通过 json 提交

r.POST("/json", func(ctx *gin.Context) {
    // 先获取到原始的 json 数据
	res, _ := ctx.GetRawData()
	var t josnTest
    // 再进行反序列化
	json.Unmarshal(res, &t)
	fmt.Println(t)
	ctx.JSON(http.StatusOK, t)
})

获取路径中的数据

REST 风格 中, 我们常常会直接使用/[参数]这种形式来发送请求

请求路径: http://localhost:8090/zhangsan/13

// 注意接口的写法
r.GET("/:username/:age", func(ctx *gin.Context) {
    // 第一种方式: 获取对应名称的参数
	username := ctx.Param("username")
	age := ctx.Param("age")

    // 第二种方式: 获取全部参数后再逐个获取
	ps := ctx.Params
	temp1 := ps.ByName("username")
	temp2, _ := ps.Get("age")
	fmt.Println(temp1, "   ", temp2)

	ctx.JSON(http.StatusOK, gin.H{"username": username, "age": age})
})

参数绑定

gin 提供了强大的参数绑定功能, 我们可以使用 .ShouldBind() 自动提取数据并绑定到对应的结构体上

结构体的定义为:

type bindingTest struct {
	User string `form:"username" json:"username" binding:"required"`
	Pwd  string `form:"password" json:"password" binding:"required"`
}

绑定 QueryString 参数

r.GET("/binding/get", func(ctx *gin.Context) {
	var test bindingTest
	err := ctx.ShouldBind(&test)
	if err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
	} else {
		fmt.Println(test.User, "   ", test.Pwd)
		ctx.JSON(http.StatusOK, test)
	}
})

绑定 form 参数

r.POST("/binding/post/form", func(ctx *gin.Context) {
	var test bindingTest
	err := ctx.ShouldBind(&test)
	if err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
	} else {
		fmt.Println(test.User, "   ", test.Pwd)
		ctx.JSON(http.StatusOK, test)
	}
})

绑定 json 参数

r.POST("/binding/post/json", func(ctx *gin.Context) {
	var test bindingTest
	err := ctx.ShouldBind(&test)
	if err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
	} else {
		fmt.Println(test.User, "   ", test.Pwd)
		ctx.JSON(http.StatusOK, test)
	}
})

路由组

在实际应用中我们的接口很多情况下会有相同的前缀(如上一部分代码中每个路径前都有 /binding), 所以 gin 中有一个路由组的概念, 通过设置路由组从而减少重复代码的书写

路由组的基本设置为:

r := gin.Default()
group := r.Group("/group")
// 这个括号可以不加
{
    // 路径为 /group/test
    group.GET("/test", )
}

重定向

http 重定向

注意: http 重定向时, 路径要加上 http:// 前缀

r.GET("/get/baidu", func(ctx *gin.Context) {
	ctx.Redirect(http.StatusMovedPermanently, "http://www.baidu.com")

    // 这样是重定向至 /get/www.baidu.com
	// ctx.Redirect(http.StatusMovedPermanently, "www.baidu.com")
})

路由重定向

r.GET("/get/ping", func(ctx *gin.Context) {
    // 重定向至 /ping
	ctx.Request.URL.Path = "/ping"
	r.HandleContext(ctx)
})

r.GET("/ping", func(ctx *gin.Context) {
	ctx.String(http.StatusOK, "pong")
})

程序清单

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"

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

type josnTest struct {
	Name string `json:"username"`
	Age  int    `json:"age"`
}

type bindingTest struct {
	User string `form:"username" json:"username" binding:"required"`
	Pwd  string `form:"password" json:"password" binding:"required"`
}

func ginFunc() *gin.Engine {
	r := gin.Default()

	r.GET("/ping", func(ctx *gin.Context) {
		ctx.String(http.StatusOK, "pong")
	})

	r.GET("/json", func(ctx *gin.Context) {
		test := josnTest{"测试", 1}
		fmt.Println(test)
		ctx.JSON(http.StatusOK, test)
	})

	r.GET("/query", func(ctx *gin.Context) {
		username := ctx.Query("username")
		age, _ := strconv.Atoi(ctx.DefaultQuery("age", "12"))
		test := josnTest{username, age}
		ctx.JSON(http.StatusOK, test)
	})

	r.POST("/form", func(ctx *gin.Context) {
		username := ctx.DefaultPostForm("username", "lisi")
		age, _ := strconv.Atoi(ctx.PostForm("age"))
		fmt.Print(age)
		ctx.JSON(http.StatusOK, gin.H{"username": username, "age": age})
	})

	r.POST("/json", func(ctx *gin.Context) {
		res, _ := ctx.GetRawData()
		var t josnTest
		json.Unmarshal(res, &t)
		fmt.Println(t)
		ctx.JSON(http.StatusOK, t)
	})

	r.GET("/:username/:age", func(ctx *gin.Context) {
		username := ctx.Param("username")
		age := ctx.Param("age")
		// ps := ctx.Params
		// temp1 := ps.ByName("username")
		// temp2, _ := ps.Get("age")
		// fmt.Println(temp1, "   ", temp2)
		ctx.JSON(http.StatusOK, gin.H{"username": username, "age": age})
	})

	return r
}

func ginBindingFunc() *gin.Engine {
	r := gin.Default()
	binding := r.Group("/binding")
	{
		binding.GET("/get", func(ctx *gin.Context) {
			var test bindingTest
			err := ctx.ShouldBind(&test)
			if err != nil {
				ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			} else {
				fmt.Println(test.User, "   ", test.Pwd)
				ctx.JSON(http.StatusOK, test)
			}
		})

		binding.POST("/post/form", func(ctx *gin.Context) {
			var test bindingTest
			err := ctx.ShouldBind(&test)
			if err != nil {
				ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			} else {
				fmt.Println(test.User, "   ", test.Pwd)
				ctx.JSON(http.StatusOK, test)
			}
		})

		binding.POST("/post/json", func(ctx *gin.Context) {
			var test bindingTest
			err := ctx.ShouldBind(&test)
			if err != nil {
				ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			} else {
				fmt.Println(test.User, "   ", test.Pwd)
				ctx.JSON(http.StatusOK, test)
			}
		})
	}

	return r
}

func ginRedirect() *gin.Engine {
	r := gin.Default()

	r.GET("/get/baidu", func(ctx *gin.Context) {
		ctx.Redirect(http.StatusMovedPermanently, "www.baidu.com")
	})

	r.GET("/get/ping", func(ctx *gin.Context) {
		ctx.Request.URL.Path = "/ping"
		r.HandleContext(ctx)
	})

	r.GET("/ping", func(ctx *gin.Context) {
		ctx.String(http.StatusOK, "pong")
	})

	return r
}

func main() {
	r := ginFunc()
	// r := ginBindingFunc()
	// r := ginRedirect()
	r.Run(":8090")
}

参考文章:

Gin 框架官方文档

Gin 框架的介绍及快速使用