已经放弃goframe
框架,对待新手不是很友好,社区圈子也很小。因为我自身的话是没有go
语言的编程基础的,所以导致了我看不太懂那个框架,不过看很多人都说goframe
封装的很好,有人吐槽有人夸,开源的框架嘛,这些都是在所难免的。
放弃goframe
不代表放弃go
,当前go
语言还是很强的,查了一下相关的资料,大部分都推荐gin
框架开始学习,抱着怀疑入坑的心态,有一次开始了学习。
自己百度找到了一个中文文档,看起来还可以,http://www.topgoer.com/gin%E6%A1%86%E6%9E%B6/,废话不多说直接上手体验一下
gin
路由
首先构建一下依赖
require github.com/gin-gonic/gin v1.6.3
基本路由
func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "hello word")
})
r.POST("/xxxpost",getting)
r.PUT("/xxxput")
//监听端口默认为8080
r.Run(":8000")
}
从这个官方的示例中,能看出下面几个问题:
r
直接支持请求方式的定义- 支持自定义端口
也很简洁,我觉得相对于我之前学习的框架,这个确实要好理解很多
gin
还支持Restful
风格的API
,参照java
来说,那就好理解很多
API参数
func main() {
r := gin.Default()
r.GET("/user/:name/*action", func(c *gin.Context) {
name := c.Param("name")
action := c.Param("action")
//截取/
action = strings.Trim(action, "/")
c.String(http.StatusOK, name+" is "+action)
})
//默认为监听8080端口
r.Run(":8000")
}
如果我们直接访问
http://127.0.0.1:8000/user/ShaoJie/learning 页面上会输出ShaoJie is learning
gin
可以直接获取API
中的参数,这个还挺好理解的,Java
里面的话,有点类似@PathVariable()
注解,很强哈,这边还注意到,gin
截取string
类型所用的方法,strings.Trim()
URl参数
func main() {
r := gin.Default()
r.GET("/user", func(c *gin.Context) {
//指定默认值
//http://localhost:8080/user 才会打印出来默认的值
name := c.DefaultQuery("name", "ShaoJie")
c.String(http.StatusOK, fmt.Sprintf("hello %s", name))
})
r.Run()
}
访问 http://127.0.0.1:8000/user 页面上会输出hello ShaoJie
或者
访问 http://127.0.0.1:8080/user?name=Shao-Jie 页面上是 hello Shao-Jie
那也就是说,这里是有一个默认的取值的,如果没有接收到参数的话,这里直接输出定义的默认值
表单传参
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<form action="http://localhost:8080/form" method="post" action="application/x-www-form-urlencoded">
用户名:<input type="text" name="username" placeholder="请输入你的用户名"> <br>
密 码:<input type="password" name="userpassword" placeholder="请输入你的密码"> <br>
<input type="submit" value="提交">
</form>
</body>
</html>
func main() {
r := gin.Default()
r.POST("/form", func(c *gin.Context) {
types := c.DefaultPostForm("type", "post")
username := c.PostForm("username")
password := c.PostForm("userpassword")
// c.String(http.StatusOK, fmt.Sprintf("username:%s,password:%s,type:%s", username, password, types))
c.String(http.StatusOK, fmt.Sprintf("username:%s,password:%s,type:%s", username, password, types))
})
r.Run()
}
表单参数可以通过PostForm()
方法获取 该方法默认解析的是x-www-form-urlencoded
或from-data
格式的参数 ,这个例子其实很简单,我本来想直接用模板,但是看到这里,我好像还不太好实现这个问题,菜是原罪
routes group
func main() {
// 1.创建路由
// 默认使用了2个中间件Logger(), Recovery()
r := gin.Default()
// 路由组1 ,处理GET请求
v1 := r.Group("/v1")
// {} 是书写规范
{
v1.GET("/login", login)
v1.GET("submit", submit)
}
v2 := r.Group("/v2")
{
v2.POST("/login", login)
v2.POST("/submit", submit)
}
r.Run(":8000")
}
这个可以对比goframe
,相比较而言,我更喜欢现在这种(小声逼逼:那个框架差点给我心态整蹦了,初学者勿入坑)
路由拆分与注册
基本的路由注册
上面将路由写在一个main
中都属于基本的路由注册
路由拆分成单独文件或包
将路由拆分,相当于在启动的时候加载这个函数
routers/routers.go
func helloHandler(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "Hello www.topgoer.com!",
})
}
func SetupRouter() *gin.Engine {
r := gin.Default()
r.GET("/topgoer", helloHandler)
return r
}
main.go
func main() {
r := routers.SetupRouter()
if err := r.Run(); err != nil {
fmt.Println("startup service failed, err:%v\n", err)
}
}
这个涉及到go
的一个小知识点,那就是方法名称大小写的问题
1、 首字母小写,代表只对当前包内文件可见 类似java
中的protected
2、 首字母大写,代表只对所有文件可见 类似java
中的private
路由拆分成多个文件
routers/blog.go
func LoadShop(e *gin.Engine) { e.GET("/helloblog", helloHandler)}
routers/shop.go
func LoadBlog(e *gin.Engine) { e.GET("/helloshop", helloHandler)}
main.go
func main() {
r := gin.Default()
routers.LoadBlog(r)
routers.LoadShop(r)
if err := r.Run(); err != nil {
fmt.Println("startup service failed, err:%v\n", err)
}
}
路由拆分到不同的APP
blog.go
、shop.go
移动到app/blog
、app/shop
中,修改 routers/routers.go
// 注册app的路由配置
func Include(opts ...Option) {
options = append(options, opts...)
}
// 初始化
func Init() *gin.Engine {
r := gin.New()
for _, opt := range options {
opt(r)
}
return r
}
main.go
func main() {
// 加载多个APP的路由配置
routers.Include(shop.Routers, blog.Routers)
// 初始化路由
r := routers.Init()
if err := r.Run(); err != nil {
fmt.Println("startup service failed, err:%v\n", err)
}
}
gin
数据解析和绑定
Json数据解析和绑定
文档示例
// 定义接收数据的结构体
type Login struct {
// binding:"required"修饰的字段,若接收为空值,则报错,是必须字段
User string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"`
Pssword string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"`
}
func main() {
// 1.创建路由
// 默认使用了2个中间件Logger(), Recovery()
r := gin.Default()
// JSON绑定
r.POST("loginJSON", func(c *gin.Context) {
// 声明接收的变量
var json Login
// 将request的body中的数据,自动按照json格式解析到结构体
if err := c.ShouldBindJSON(&json); err != nil {
// 返回错误信息
// gin.H封装了生成json数据的工具
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 判断用户名密码是否正确
if json.User != "root" || json.Pssword != "admin" {
c.JSON(http.StatusBadRequest, gin.H{"status": "304"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "200"})
})
r.Run(":8000")
}
Postman
测试一下 地址:http://127.0.0.1:8000/loginJSON Post请求
{
"user":"root",
"password":"admin"
}
表单的数据解析和绑定
文档示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<form action="http://localhost:8000/loginForm" method="post" enctype="application/x-www-form-urlencoded">
用户名<input type="text" name="username"><br>
密码<input type="password" name="password">
<input type="submit" value="提交">
</form>
</body>
</html>
// 定义接收数据的结构体
type Login struct {
// binding:"required"修饰的字段,若接收为空值,则报错,是必须字段
User string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"`
Pssword string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"`
}
func main() {
// 1.创建路由
// 默认使用了2个中间件Logger(), Recovery()
r := gin.Default()
// JSON绑定
r.POST("/loginForm", func(c *gin.Context) {
// 声明接收的变量
var form Login
// Bind()默认解析并绑定form格式
// 根据请求头中content-type自动推断
if err := c.Bind(&form); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 判断用户名密码是否正确
if form.User != "root" || form.Pssword != "admin" {
c.JSON(http.StatusBadRequest, gin.H{"status": "304"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "200"})
})
r.Run(":8000")
}
URl数据解析和绑定
文档示例
// 定义接收数据的结构体
type Login struct {
// binding:"required"修饰的字段,若接收为空值,则报错,是必须字段
User string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"`
Pssword string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"`
}
func main() {
// 1.创建路由
// 默认使用了2个中间件Logger(), Recovery()
r := gin.Default()
// JSON绑定
r.GET("/:user/:password", func(c *gin.Context) {
// 声明接收的变量
var login Login
// Bind()默认解析并绑定form格式
// 根据请求头中content-type自动推断
if err := c.ShouldBindUri(&login); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 判断用户名密码是否正确
if login.User != "root" || login.Pssword != "admin" {
c.JSON(http.StatusBadRequest, gin.H{"status": "304"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "200"})
})
r.Run(":8000")
}
总结一下这三种解析和绑定
json
解析和绑定对应了body
传参- 表单解析对应了我们一般的表单请求,一般在
html
里面会更多一点,现在大部分前端都是vue
了,所以有些东西自然而然的会慢慢忘记 URL
解析对应了Restful API
请求
那更新这一期的话,很明显能看出来,其实gin
还是很简单的,基本上现在就可以写一点接口了,后面的话就不多叙述了,直接进入到这个深入学习了。
学习是一个漫长且煎熬的过程,但是我们还能在其中寻找乐趣的话,那肯定非常有意思。