1.gin简介
-
镜像配置:
// 安装出现timeout问题可以配置go module镜像,终端执行下面命令,为阿里镜像
go env -w GO111MODULE=on
go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct
-
安装gin:
import "github.com/gin-gonic/gin"
-
一切始于hello world
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
// 1.创建路由
r := gin.Default()
// 2.绑定路由规则,执行的函数
// gin.Context,封装了request和response
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "hello World!")
})
// 3.监听端口,默认在8080
// Run("里面不指定端口号默认为8080")
r.Run(":8000")
}
-
当运行时候会出现如下错误;
can't load package: package gin_demo is not in GOROOT
// 可以使用go mod模式,在当前项目目录下初始化mod
go mod init
-
此时访问127.0.0.1:8000 就会返回hello world。
2.gin路由
2.1 基本路由
-
gin框架中采用的路由哭给予httpprouter做的。
packge main
import (
"net/http"
"github.com/gin-gonic/gin"
)
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")
}
2.2 Restful风格API
2.3API参数
-
API参数,可以通过Context 的Param方法来获取API参数。
func main() {
r := gin.Default()
r.GET("/user/:name/*action", func(context *gin.Context) {
// 获取URL上name
name := context.Param("name")
// 获取URL上action
action := context.Param("action")
fmt.Println(action)
// 去除两边 /
action = strings.Trim(action, "/")
context.String(http.StatusOK, name+" is "+action)
})
r.Run(":8000")
}
-
如下图:
![F3A7649C-316C-49E1-84C2-0A5909EC46E7](/Users/xujunkai/Library/Containers/com.tencent.qq/Data/Library/Application Support/QQ/Users/874524334/QQ/Temp.db/F3A7649C-316C-49E1-84C2-0A5909EC46E7.png)
2.4URL参数
-
URL参数可以通过DefaultQuery()或Query()方法获取。
-
DefaultQuery()若参数不对称,则返回默认值。Query()若不存在,返回空串。
r.GET("userinfo", func(c *gin.Context){
name := c.DefaultQuery("name","Tom")
c.String(http.StatusOK, fmt.Sprintf("hello %s", name))
})
-
当不加参数访问URL:http://127.0.0.1:8000/userinfo ,返回是:hello Tom ,当加参数访问URL:http://127.0.0.1:8000/userinfo/?name=Jery ,返回是:hello Jery 。
2.5表单参数
-
表单传输为post请求,http常见的传输格式为:
- application/json
- application/x-www-form-urlencoded
- application/xml
- multipart/form-data
-
表单可以通过如下方式访问,该方法默认解析x-www-form-urlencoded或from-data格式的参数。
<form action="http://localhost:8000/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>
-
post请求处理:
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))
})
-
如下示例图:
![73478227-4D2D-4890-A7A8-B25BA628A84E](/Users/xujunkai/Library/Containers/com.tencent.qq/Data/Library/Application Support/QQ/Users/874524334/QQ/Temp.db/73478227-4D2D-4890-A7A8-B25BA628A84E.png)
2.6上传单个文件
-
gin文件上传与原生的net/http方法类似,不同在于gin把原声request封装到c.Request中
<!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>
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
//限制上传最大尺寸
r.MaxMultipartMemory = 8 << 20
r.POST("/upload", func(c *gin.Context) {
file, err := c.FormFile("file")
if err != nil {
c.String(500, "上传图片出错")
}
// c.JSON(200, gin.H{"message": file.Header.Context})
c.SaveUploadedFile(file, file.Filename)
c.String(http.StatusOK, file.Filename)
})
r.Run()
}
-
上传特定文件
// 获取headers
_, headers, err := c.Request.FormFile("file")
if err != nil {
log.Printf("error")
}
if headers.Size > 1024*1024*2 {
fmt.Println("文件太大了")
return
}
if headers.Header.Get("Content-Type") != "image/png" {
fmt.Println("只允许上传png图片")
return
}
2.7上传多个文件
func main() {
// 1.创建路由
// 默认使用了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")
}
2.8路由的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")
}
2.9路由原理
- httproter会将所有路由规则构造一颗前缀树。比如有root and as at cn com
2.10路由拆分与注册
2.10.1 基本路由注册
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
func helloHandler(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "Hello www.topgoer.com!",
})
}
func main() {
r := gin.Default()
// 指定路由函数。
r.GET("/topgoer", helloHandler)
if err := r.Run(); err!=nil{
fmt.Println("startup service failed. err:%v\n", err)
}
}
// 访问http://localhost:8080/topgoer 返回{"message":"Hello www.topgoer.com!"}
2.10.2 路由拆分成单独文件或包
-
当你的项目规模太大,就不适合在项目的main.go文件中去实现路由注册相关逻辑了。我们会倾向于把路由部分的代码拆分出来。形成单独一个文件或包
// 项目目录结构
gin_demo
|__go.sum
|__main.go
|__go.mod
|__routers
|___routers.go
-
Routers/routers.go
package routers
import (
"github.com/gin-gonic/gin"
"net/http"
)
func helloHandler(c *gin.Context){
c.JSON(http.StatusOK, gin.H{
"message": "Hello www.topgoer.com",
})
}
// SetupRouter 用于配置路由信息
func SetupRouter() *gin.Engine {
r := gin.Default()
r.GET("/topgoer", helloHandler)
return r
}
-
main.go
package main
import (
"fmt"
"gin_demo/routers"
)
func main() {
r :=routers.SetupRouter()
if err:= r.Run(); err != nil {
fmt.Printf("startUp service failed, err:%v\n", err)
}
}
2.10.3 路由拆分成多个文件
2.10.4路由拆分到不同APP
-
有时候项目实在太大,那么我们倾向于把业务拆分更详细一些,例如把不同业务代码拆分成不同APP
gin_demo
|__app
| |__blog
| | |__handler.go
| | |__router.go
| |__shop
| |__handler.go
| |__router.go
|__go.sum
|__main.go
|__go.mod
|__routers
|___routers.go
-
app/blog/handler.go
package blog
import (
"github.com/gin-gonic/gin"
"net/http"
)
func blogHandler(c *gin.Context){
c.JSON(http.StatusOK, gin.H{
"message": "Hello blog handler",
})
}
-
app/blog/router.go
package blog
import "github.com/gin-gonic/gin"
func Routers(e *gin.Engine){
e.GET("/blog",blogHandler)
}
-
app/shop/handler.go
package shop
import (
"github.com/gin-gonic/gin"
"net/http"
)
func helloHandler(c *gin.Context){
c.JSON(http.StatusOK, gin.H{
"message": "Hello handler",
})
}
func commentHandler(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "comment handler",
})
}
-
app/shop/router.go
package shop
import "github.com/gin-gonic/gin"
func Routers(e *gin.Engine) {
e.GET("/hello",helloHandler)
e.GET("/comment",commentHandler)
}
-
routers/routers.go
package routers
import "github.com/gin-gonic/gin"
type Option func(engine *gin.Engine)
var options = []Option{}
// 注册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
package main
import (
"fmt"
"gin_demo/app/blog"
"gin_demo/app/shop"
"gin_demo/routers"
)
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)
}
}
|
请发表评论