2 Gin 基本使用-基础篇
2.1 路由与传参
2.1.1 无参路由
所谓的无参路由最主要的区别就是将处理函数单独拿出来
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
// 将处理函数 handler 单独拆分开
// 无参数路由
func HelloHandler(c *gin.Context) {
c.String(200, "hello!!")
}
func main() {
// 1.生成 engine
r := gin.Default()
// 2.注册路由,将写在外面的处理函数注册到 GET 路由中
r.GET("/", HelloHandler)
fmt.Println("http://10.0.0.134:8888")
// 3.启动服务
r.Run(":8888")
}
postman 访问
2.1.2 API 传参
可以通过 Context 的 Param 方法来获取 API 参数
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func GetBookDeatilHandler(c *gin.Context) {
// 通过 c.Param 参数解析
// 因为我们在 r.GET 函数中指定了 :id 的 url 所以需要将 c.Param() 中的 key 定义为 id 这样才能够正常解析
bookId := c.Param("id")
fmt.Println(bookId)
c.String(200, "API 路由")
}
func main() {
r := gin.Default()
// 所谓的 API 路由其实就是直接通过斜杠的方式来进行传递
// API 路由:类似于 /book/1/ 这样的 URL 方式
// 这里定义了 :id 那么在上面的处理函数中也需要指定 id 这个 key 从而实现对 key 的解析
r.GET("/book/:id", GetBookDeatilHandler)
r.Run(":8888")
}
postman 请求的时候我在 book 的 uri 后面跟上了 99 ,这里的 99 就是对应代码中的 id
可以看到在代码中我将 bookid 输出就是 99
2.1.3 url 参数
URL
参数可以通过DefaultQuery()
或Query()
方法获取DefaultQuery()
若参数不存在,返回默认值,Query()
若不存在,返回空串- http://127.0.0.1:8080/user?name=zhangsan
方法一:Query()
获取传参
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func GetUserUrlHandelr(c *gin.Context) {
// 1.通过 Query() 方式传递
name := c.Query("name")
fmt.Println(name)
c.String(200, "user URL 解析!!")
}
func main() {
r := gin.Default()
// url 传参: http://127.0.0.1/user?id=20&name=zhangsan
// 所谓的 url 传参就是通过 ? 和 & 符进行传递
r.GET("/user", GetUserUrlHandelr)
r.Run(":8888")
}
postman 访问:我们在传递 url 的时候将 name 的值定义为张三
可以看到 gin 服务端成功获取我们传入的 张三
但是如果我们在请求的时候没有传递 ?name
的 URL 那么就会获取到一个空的字符串,那这种情况我们该怎么办?
方法二:DefaultQuery()
方式获取
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func URLGETUserHandler(c *gin.Context) {
// 2.通过 DefaultQuery() 获取,如果在放的时候没有指定 url 那么就通过默认值获取
/* func (*gin.Context).DefaultQuery(key string, defaultValue string) string
第一个参数为 key 的值
第二个参数为默认值,也就是说如果用户没有传递 url 那么就获取默认值
并且返回一个 key 的 value
*/
name := c.DefaultQuery("name", "default value !")
fmt.Println("获取到的用户名", name)
}
func main() {
r := gin.Default()
r.GET("/user", URLGETUserHandler)
r.Run()
}
postman 第一次传递 name url 访问
gin 终端成功获取张三
postman 第二次未传递任何数据访问
gin 终端输出默认值
2.1.4 ShouldBind 参数绑定(使用较多)
- 我们可以基于请求的
Content-Type
识别请求数据类型并利用反射机制 - 自动提取请求中
QueryString
、form
表单 、JSON
、XML
等参数到结构体中 - 下面的示例代码演示了
ShouldBind()
强大的功能 - 它能够基于请求自动提取
JSON
、form
表单 和QueryString
类型的数据,并把值绑定到指定的结构体对象。
比如用户在 POST 请求时候有非常复杂的数据的话,那么我们想要验证用户名的 name 和 password 是否正确,所以这时候我们就需要使用到 func (*gin.Context).ShouldBind(obj any) error
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
// 定义 login 结构体,并在 PostLoginHandler 函数中用来解析
type Login struct {
// post 请求的数据字段名一定要和 json:"username" 一摸一样
// binding:"requierd" 要求 json 中定义的字段不能为空
UserName string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
}
// shouldBind 方法,获取 json 中复杂数据
func PostLoginHandler(c *gin.Context) {
login := Login{}
// 将 net/http 中的 r.Body 数据解析到 Login 的结构体中
// 将 login 结构体的指针数据传递给 c.ShouldBind
if err := c.ShouldBind(&login); err != nil {
// 如果用户提交数据有错误直接响应"用户名或密码错误"
c.String(200, "用户名或密码错误!")
return
}
fmt.Println(login)
c.String(200, "登陆成功!")
}
func main() {
r := gin.Default()
// 这里已经不再是 GET 请求,而是一个复杂的 json 格式的 POST 请求
r.POST("/login", PostLoginHandler)
r.Run()
}
postman 提交数据验证登录成功
gin 服务器输出导入数据成功
传递错误参数:直接响应用户或密码错误!
总结:
通过上面的两个示例得出 shouldbind 不仅能够解析参数,还能够对参数进行校验。
2.2 响应返回
如何通过 gin 框架的封装进行返回数据
2.2.1 响应 String
package main
import "github.com/gin-gonic/gin"
// 响应一个普通的 string 字符串
func ResponseHandler(c *gin.Context) {
c.String(200, "响应 string 字符串")
}
func main() {
r := gin.Default()
r.GET("/response", ResponseHandler)
r.Run()
}
PostMan 请求,可以看到响应一个 string 字符串
2.2.2 响应 json
所谓响应 json 数据就是给客户端响应一个结构体,因为在 golang 中所有的服务返回的 json 本质就是一个 结构体
操作流程如下:
- 先定义一个结构体
- 然后将结构体赋值
- 最后将数据返回
方式一:直接通过结构体返回
package main
import "github.com/gin-gonic/gin"
// 响应一个 json 数据
func ResponseHandler(c *gin.Context) {
type Data struct {
Msg string `json:"msg"`
Code int `json:"code"`
}
/*
初始化响应结构体
其实这块就相当于在数据库中获取到的数据,并将起响应给客户端
*/
d := Data{Msg: "json 响应", Code: 17}
// c.JSON 将初始化的结构体 d 通过 json 格式响应给用户
c.JSON(200, d)
}
func main() {
r := gin.Default()
r.GET("/json", ResponseHandler)
r.Run()
}
postman 成功获取响应数据
方法二:直接通过 json 返回
package main
import "github.com/gin-gonic/gin"
func JsonHandler(c *gin.Context) {
/*
直接在 c.JSON 中通过 gin.H 进行返回, type H map[string]any
本质就 gin.H 是一个 map
*/
c.JSON(200, gin.H{
"msg": "gin.H",
"code": 123,
})
}
func main() {
r := gin.Default()
r.GET("/", JsonHandler)
r.Run()
}
2.2.3 路由重定向
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
// 路由重定向
func ResponseRedirectHandler(c *gin.Context) {
// 重定向至百度
// http.StatusMovedPermanently 自动注册状态码,因为想要实现重定向就需要 300 系列状态码
c.Redirect(http.StatusMovedPermanently, "https://www.baidu.com")
}
func main() {
r := gin.Default()
// 当用户访问 /redirect URL 的时候就会被重定向至百度
r.GET("/redirect", ResponseRedirectHandler)
r.Run()
}
可以看到当前状态码已经为 302 并且已经跳转至百度
2.3 路由分发
为什么需要路由分发?
-
我们一个项目有非常多的模块,如果全部写在一块导致代码结构混乱,不利于后续的扩展
-
按照大的模块,每个模块有自己独立的路由,主路由可以再main.go中进行注册
所谓的路由分发就是将不同的内容分开写,然后将相同的信息写到一起,比如有图书相关的内容那么就将图书相关的写到图书的路由中;如果有用户的信息就写到用户的路由中,以此类推
如何进行路由分层呢,其实基本的目录结构如下:
├── go.mod
├── go.sum
├── main.go
└── routers
├── books.go
└── users.go
2.3.1 演示如下
下面将进行一个演示项目的编写,在这个项目中分别有一个用户模块和一个书籍模块
2.3.1.1 初始化目录结构
创建对应的目录结构,准备初始环境
$ mkdir demo_router_layer
$ cd demo_router_layer/
/demo_router_layer$ mkdir routers
/demo_router_layer$ touch main.go
/demo_router_layer$ touch routers/users.go
/demo_router_layer$ go mod init demo_router_layer
/demo_router_layer$ go mod tidy
2.3.1.2 编写 user 路由
编写 users.go 路由程序
package routers
import "github.com/gin-gonic/gin"
// 编写加载用户路由的方法
func LoadUser(e *gin.Engine) {
e.GET("/users", UserHandler)
}
func UserHandler(c *gin.Context) {
c.String(200, "用户模块路由分发!")
}
2.3.1.3 编写 main 程序注册 user 路由
main.go 主程序注册 LoadUser 路由
package main
import (
"demo_router_layer/routers"
"github.com/gin-gonic/gin"
)
// 主程序只能有一个,并且用了实现注册路由操作
func main() {
// 初始化引擎
r := gin.Default()
// 注册加载用户路由,并传递 r 引擎
routers.LoadUser(r)
r.Run()
}
启动程序浏览器访问 http://10.0.0.134:8080/users URL 路由成功
以上就是所谓的路由分层写法,当然我们还有图书相关的路由需要完成所以接下来就需要编写 book 路由
2.3.1.4 编写 book 路由
1 创建 book.go 程序文件
demo_router_layer# touch routers/book.go
2 编写 book.go 代码
package routers
import "github.com/gin-gonic/gin"
// e 就是由 main.go 函数中 gin.Default 返回
func LoadBook(e *gin.Engine) {
// 注册路由
e.GET("/book", Books)
}
// 处理函数
func Books(c *gin.Context) {
c.String(200, "books")
}
3 编写 main.go 文件,合并 book 代码
package main
import (
"demo_router_layer/routers"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// 注册 user 路由
routers.LodeUser(r)
// 注册 BOOK 路由
routers.LoadBook(r)
r.Run()
}
4 启动然后浏览器访问 http://10.0.0.134:8080/book