1. 基本路由
gin 框架中采用的路由库是基于httprouter做的
1、router:=gin.Default():这是默认的服务器。使用gin的Default方法创建一个路由Handler;
2、然后通过Http方法绑定路由规则和路由函数。不同于net/http库的路由函数,gin进行了封装,把request和response都封装到了gin.Context的上下文环境中。
3、最后启动路由的Run方法监听端口。还可以用http.ListenAndServe(":8080", router),或者自定义Http服务器配置。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
package
main
import
(
"net/http"
"github.com/gin-gonic/gin"
)
func
main() {
r := gin.Default()
//不带默然中间件的路由
//r := gin.New()
//Handle
r.Handle(
"GET"
,
"/"
,
func
(context *gin.Context) {
})
//直接使用httpMethod
r.GET(
"/"
,
func
(c *gin.Context) {
c.String(http.StatusOK,
"hello word"
)
})
r.POST(
"/xxxpost"
,
func
(context *gin.Context) {
})
r.PUT(
"/xxxput"
,
func
(context *gin.Context) {
})
//监听端口默认为8080
r.Run(
":8000"
)
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
//default源码
func
Default() *Engine {
debugPrintWARNINGDefault()
engine := New()
//不带中间件的路由
engine.Use(Logger(), Recovery())
return
engine
}
//Run源码
func
(engine *Engine) Run(addr ...string) (err error) {
defer
func
() { debugPrintError(err) }()
address := resolveAddress(addr)
debugPrint(
"Listening and serving HTTP on %s\n"
, address)
err = http.ListenAndServe(address, engine)
//engine就是r := gin.Default()
return
}
|
2. Restful风格的API
-
gin支持Restful风格的API
-
即Representational State Transfer的缩写。直接翻译的意思是"表现层状态转化",是一种互联网应用程序的API设计理念:URL定位资源,用HTTP描述操作
1.获取文章 /blog/getXxx Get blog/Xxx
2.添加 /blog/addXxx POST blog/Xxx
3.修改 /blog/updateXxx PUT blog/Xxx
4.删除 /blog/delXxxx DELETE blog/Xxx
3. API参数
-
可以通过Context的Param方法来获取API参数
-
localhost:8000/xxx/zhangsan
gin的路由来自httprouter库。因此httprouter具有的功能,gin也具有,不过gin不支持路由正则表达式。
冒号:
加上一个参数名组成路由参数。可以使用c.Params的方法读取其值。当然这个值是字串string。
除了:
gin还提供了*
号处理参数,*
号能匹配的规则就更多。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
package
main
import
(
"net/http"
"strings"
"github.com/gin-gonic/gin"
)
func
main() {
r := gin.Default()
r.GET(
"/: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)
})
//路由冲突,abc会被上面的name匹配
//编译不通过
r.GET(
"/abc/:name/*action"
,
func
(c *gin.Context) {
name := c.Param(
"name"
)
action := c.Param(
"action"
)
//截取
action = strings.Trim(action,
"/"
)
c.String(http.StatusOK, name+
" == "
+action)
})
r.GET(
"/"
,
func
(c *gin.Context) {
c.String(200,
"are you ok?"
)
})
//默认为监听8080端口
r.Run(
":9000"
)
}
|
4. URL参数
- URL参数可以通过DefaultQuery()或Query()方法获取
- DefaultQuery()若参数不村则,返回默认值,Query()若不存在,返回空串
web提供的服务通常是client和server的交互。其中客户端向服务器发送请求,除了路由参数,其他的参数无非两种,查询字符串query string和报文体body参数。所谓query string,即路由用,用?
以后连接的key1=value2&key2=value2
的形式的参数。当然这个key-value是经过urlencode编码。
URL 参数通过 DefaultQuery 或 Query 方法获取。
对于参数的处理,经常会出现参数不存在的情况,对于是否提供默认值,gin也考虑了,并且给出了一个优雅的方案,使用c.DefaultQuery方法读取参数,其中当参数不存在的时候,提供一个默认值。使用Query方法读取正常参数,当参数不存在的时候,返回空字串。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package
main
import
(
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
func
main() {
r := gin.Default()
r.GET(
"/user"
,
func
(c *gin.Context) {
//指定默认值
//http://localhost:8080/user 才会打印出来默认的值
name := c.DefaultQuery(
"name"
,
"golang"
)
c.String(http.StatusOK, fmt.Sprintf(
"hello %s"
, name))
})
r.Run()
}
|
5. 表单参数
- 表单传输为post请求,http常见的传输格式为四种:
- application/json
- application/x-www-form-urlencoded
- application/xml
- multipart/form-data
- 表单参数可以通过PostForm()方法获取,该方法默认解析的是x-www-form-urlencoded或from-data格式的参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package
main
import
(
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
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()
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<!
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
>
|
6. 上传单个文件
-
multipart/form-data格式用于文件上传
-
gin文件上传与原生的net/http方法类似,不同在于gin把原生的request封装到c.Request中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
package
main
import
(
"github.com/gin-gonic/gin"
"net/http"
)
func
main() {
r := gin.Default()
//限制上传最大尺寸
r.MaxMultipartMemory = 8 << 20
//8M
r.POST(
"/upload"
,
func
(c *gin.Context) {
file, err := c.FormFile(
"file"
)
if
err != nil {
c.String(500,
"上传图片出错"
)
}
/*
也可以直接使用io操作,拷贝文件数据。
out, err := os.Create(filename)
defer out.Close()
_, err = io.Copy(out, file)
*/
// c.JSON(200, gin.H{"message": file.Header.Context})
c.SaveUploadedFile(file, file.Filename)
c.String(http.StatusOK, file.Filename)
})
r.Run()
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<!
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/upload" method="post" enctype="multipart/form-data">
上传文件:<
input
type="file" name="file" >
<
input
type="submit" value="提交">
</
form
>
</
body
>
</
html
>
|
7. 上传多个文件
所谓多个文件,无非就是多一次遍历文件,然后一次copy数据存储即可。
与单个文件上传类似,只不过使用了c.Request.MultipartForm
得到文件句柄,再获取文件数据,然后遍历读写。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
package
main
import
(
"github.com/gin-gonic/gin"
"net/http"
"fmt"
)
func
main() {
// 创建路由
// 默认使用了2个中间件Logger(), Recovery()
r := gin.Default()
// 限制表单上传大小 8MB,默认为32MB
r.MaxMultipartMemory = 8 << 20
r.POST(
"/upload"
,
func
(c *gin.Context) {
form, err := c.MultipartForm()
if
err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf(
"get err %s"
, err.Error()))
}
// 获取所有图片
files := form.File[
"files"
]
// 遍历所有图片
for
_, file :=
range
files {
// 逐个存
if
err := c.SaveUploadedFile(file, file.Filename); err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf(
"upload err %s"
, err.Error()))
return
}
}
c.String(200, fmt.Sprintf(
"upload ok %d files"
, len(files)))
})
//默认端口号是8080
r.Run(
":8000"
)
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<!
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/upload" method="post" enctype="multipart/form-data">
上传文件:<
input
type="file" name="files" multiple>
<
input
type="submit" value="提交">
</
form
>
</
body
>
</
html
>
|
8. 路由组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
package
main
import
(
"github.com/gin-gonic/gin"
"fmt"
)
// gin的helloWorld
func
main() {
// 创建路由
// 默认使用了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"
)
}
func
login(c *gin.Context) {
name := c.DefaultQuery(
"name"
,
"jack"
)
c.String(200, fmt.Sprintf(
"hello %s\n"
, name))
}
func
submit(c *gin.Context) {
name := c.DefaultQuery(
"name"
,
"lily"
)
c.String(200, fmt.Sprintf(
"hello %s\n"
, name))
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
func
main() {
r := gin.Default()
userGroup := r.Group(
"/user"
)
{
userGroup.GET(
"/index"
,
func
(c *gin.Context) {...})
userGroup.GET(
"/login"
,
func
(c *gin.Context) {...})
userGroup.POST(
"/login"
,
func
(c *gin.Context) {...})
}
shopGroup := r.Group(
"/shop"
)
{
shopGroup.GET(
"/index"
,
func
(c *gin.Context) {...})
shopGroup.GET(
"/cart"
,
func
(c *gin.Context) {...})
shopGroup.POST(
"/checkout"
,
func
(c *gin.Context) {...})
}
r.Run()
}
|
路由组也是支持嵌套的,例如:
1
2
3
4
5
6
7
8
9
|
shopGroup := r.Group(
"/shop"
)
{
shopGroup.GET(
"/index"
,
func
(c *gin.Context) {...})
shopGroup.GET(
"/cart"
,
func
(c *gin.Context) {...})
shopGroup.POST(
"/checkout"
,
func
(c *gin.Context) {...})
// 嵌套路由组
xx := shopGroup.Group(
"xx"
)
xx.GET(
"/oo"
,
func
(c *gin.Context) {...})
}
|
9. 重定向
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
package
main
import
"github.com/gin-gonic/gin"
func
main() {
//请求重定向
r := gin.Default()
r.GET(
"/index"
,
func
(context *gin.Context) {
/*301: 永久重定向
常用的例如域名跳转:http:**** => https:****
302: 临时重定向
需要向服务端请求是否过期,过期返回新数据,没过期返回状态吗302,然后客户端重定向,
期间差别主要在于数据包的大小(没有过期的情况下,不需要再在数据包中附加数据返回,从而加速网络传输,提升速度)*/
context.Redirect(301,
"https://www.bilibili.com/"
)
})
//路由重定向
r.GET(
"/a"
,
func
(context *gin.Context) {
context.Request.URL.Path =
"/b"
r.HandleContext(context)
})
r.GET(
"/b"
,
func
(context *gin.Context) {
context.String(200,
"are you ok?"
)
})
r.Run()
}
|
来源:oschina
链接:https://my.oschina.net/u/4408223/blog/4291426